<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Typelex</title>
    <description>The latest articles on DEV Community by Typelex (@typelex).</description>
    <link>https://dev.to/typelex</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4030431%2Fe8d5acdf-f2f6-4fab-9ec8-53389b59f00d.jpg</url>
      <title>DEV Community: Typelex</title>
      <link>https://dev.to/typelex</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/typelex"/>
    <language>en</language>
    <item>
      <title>Address-Specific OTC Orders: A Simpler Way to Settle Private On-Chain Deals</title>
      <dc:creator>Typelex</dc:creator>
      <pubDate>Wed, 29 Jul 2026 13:51:44 +0000</pubDate>
      <link>https://dev.to/typelex/address-specific-otc-orders-a-simpler-way-to-settle-private-on-chain-deals-4ibp</link>
      <guid>https://dev.to/typelex/address-specific-otc-orders-a-simpler-way-to-settle-private-on-chain-deals-4ibp</guid>
      <description>&lt;p&gt;Public DEXs are designed for open trading.&lt;/p&gt;

&lt;p&gt;A user selects two assets, accesses shared liquidity, and receives a price calculated during execution.&lt;/p&gt;

&lt;p&gt;But some transactions are not meant for the open market.&lt;/p&gt;

&lt;p&gt;A project may sell tokens to a specific investor. A DAO may exchange treasury assets with an approved partner. Two funds may agree on a private stablecoin swap.&lt;/p&gt;

&lt;p&gt;In these cases, the participants already know the counterparty, assets, amounts, and exchange rate.&lt;/p&gt;

&lt;p&gt;Typelex converts those negotiated terms into an address-specific on-chain OTC order.&lt;/p&gt;

&lt;p&gt;The Problem With Open Orders&lt;/p&gt;

&lt;p&gt;Not every offer should be available to every wallet.&lt;/p&gt;

&lt;p&gt;Consider a private token allocation:&lt;/p&gt;

&lt;p&gt;the project has selected an investor;&lt;br&gt;
both sides have agreed on the price;&lt;br&gt;
the receiving wallet is already known;&lt;br&gt;
the allocation is not intended for the public market.&lt;/p&gt;

&lt;p&gt;Publishing this transaction as an unrestricted order creates unnecessary risk.&lt;/p&gt;

&lt;p&gt;Another wallet could attempt to execute it, even though the agreement was made with a specific participant.&lt;/p&gt;

&lt;p&gt;Typelex solves this by connecting the order to an authorized wallet address.&lt;/p&gt;

&lt;p&gt;What the Order Defines&lt;/p&gt;

&lt;p&gt;A Typelex OTC order can include:&lt;/p&gt;

&lt;p&gt;the token offered by the maker;&lt;br&gt;
the token expected in return;&lt;br&gt;
the amount of each asset;&lt;br&gt;
the fixed exchange rate;&lt;br&gt;
the authorized counterparty;&lt;br&gt;
the expiration time.&lt;/p&gt;

&lt;p&gt;The smart contract does not negotiate or recalculate these conditions.&lt;/p&gt;

&lt;p&gt;Its role is much simpler:&lt;/p&gt;

&lt;p&gt;Verify the order and execute it exactly as agreed.&lt;/p&gt;

&lt;p&gt;Wallet-Level Access Control&lt;/p&gt;

&lt;p&gt;When a user attempts to fill the order, the contract checks the sender’s wallet.&lt;/p&gt;

&lt;p&gt;Conceptually, the validation may look like this:&lt;/p&gt;

&lt;p&gt;require(&lt;br&gt;
    msg.sender == authorizedCounterparty,&lt;br&gt;
    "Unauthorized wallet"&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;The actual implementation may differ, but the principle remains the same:&lt;/p&gt;

&lt;p&gt;An on-chain order can be publicly visible without being publicly executable.&lt;/p&gt;

&lt;p&gt;A wallet that is not included in the order cannot complete the transaction.&lt;/p&gt;

&lt;p&gt;Fixed Terms, Not Dynamic Quotes&lt;/p&gt;

&lt;p&gt;An AMM calculates the exchange result from current liquidity.&lt;/p&gt;

&lt;p&gt;A Typelex OTC order starts with a result that has already been agreed.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Maker provides: Token A&lt;br&gt;
Counterparty provides: Token B&lt;br&gt;
Rate: Fixed before execution&lt;br&gt;
Authorized wallet: Defined in the order&lt;br&gt;
Expiration: Defined in the order&lt;/p&gt;

&lt;p&gt;The amounts do not automatically change because:&lt;/p&gt;

&lt;p&gt;another transaction was processed first;&lt;br&gt;
liquidity moved in an external pool;&lt;br&gt;
an aggregator selected a new route;&lt;br&gt;
the market price changed before confirmation.&lt;/p&gt;

&lt;p&gt;When the original terms can no longer be completed, the transaction is rejected or the order expires.&lt;/p&gt;

&lt;p&gt;The contract does not replace the agreement with a different quote.&lt;/p&gt;

&lt;p&gt;Both Transfers Happen Together&lt;/p&gt;

&lt;p&gt;Access control alone is not enough.&lt;/p&gt;

&lt;p&gt;The settlement must also ensure that one participant cannot receive assets while the other side gets nothing.&lt;/p&gt;

&lt;p&gt;Typelex uses atomic execution.&lt;/p&gt;

&lt;p&gt;There are only two possible results:&lt;/p&gt;

&lt;p&gt;Both transfers are completed.&lt;br&gt;
The entire transaction is reverted.&lt;/p&gt;

&lt;p&gt;Before execution, the contract can verify:&lt;/p&gt;

&lt;p&gt;wallet authorization;&lt;br&gt;
token balances;&lt;br&gt;
transfer approvals;&lt;br&gt;
order status;&lt;br&gt;
expiration time;&lt;br&gt;
exact token amounts.&lt;/p&gt;

&lt;p&gt;There is no partial settlement and no need to decide which participant should transfer first.&lt;/p&gt;

&lt;p&gt;Negotiation Happens Off-Chain, Settlement Happens On-Chain&lt;/p&gt;

&lt;p&gt;Typelex separates the deal into two stages.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Negotiation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The participants agree on:&lt;/p&gt;

&lt;p&gt;assets;&lt;br&gt;
volume;&lt;br&gt;
rate;&lt;br&gt;
wallet addresses;&lt;br&gt;
execution period.&lt;/p&gt;

&lt;p&gt;This can happen through an RFQ workflow, private communication channel, DAO decision, or existing business relationship.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Settlement&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agreed parameters are added to the on-chain order.&lt;/p&gt;

&lt;p&gt;The smart contract then verifies the conditions and coordinates both transfers.&lt;/p&gt;

&lt;p&gt;Participants define the deal. Typelex enforces the settlement logic.&lt;/p&gt;

&lt;p&gt;Why Public Liquidity Is Not Required&lt;/p&gt;

&lt;p&gt;The price has already been agreed, so the transaction does not need to search for liquidity inside an AMM.&lt;/p&gt;

&lt;p&gt;The assets come directly from the participating wallets.&lt;/p&gt;

&lt;p&gt;This avoids dependence on:&lt;/p&gt;

&lt;p&gt;public pool depth;&lt;br&gt;
intermediary tokens;&lt;br&gt;
multi-hop routing;&lt;br&gt;
dynamic AMM pricing;&lt;br&gt;
several external liquidity venues.&lt;/p&gt;

&lt;p&gt;The settlement remains recorded on-chain, but the exchange does not need to be executed as a public-market swap.&lt;/p&gt;

&lt;p&gt;Where Address-Specific Orders Can Be Used&lt;br&gt;
Strategic token allocations&lt;/p&gt;

&lt;p&gt;Projects can create orders for selected investors or partners.&lt;/p&gt;

&lt;p&gt;DAO treasury operations&lt;/p&gt;

&lt;p&gt;A DAO can exchange assets with an approved counterparty under predefined terms.&lt;/p&gt;

&lt;p&gt;Stablecoin rebalancing&lt;/p&gt;

&lt;p&gt;Two participants can settle a direct stablecoin exchange without routing the transaction through public pools.&lt;/p&gt;

&lt;p&gt;Fund and market maker settlement&lt;/p&gt;

&lt;p&gt;Previously negotiated transactions can be completed between approved wallets.&lt;/p&gt;

&lt;p&gt;Private token sales&lt;/p&gt;

&lt;p&gt;A large holder can transfer an allocation directly to a specific buyer.&lt;/p&gt;

&lt;p&gt;Non-Custodial Execution&lt;/p&gt;

&lt;p&gt;Typelex does not require users to keep assets on a shared platform balance.&lt;/p&gt;

&lt;p&gt;Participants connect their own wallets and approve the necessary transfers themselves.&lt;/p&gt;

&lt;p&gt;The protocol does not need to:&lt;/p&gt;

&lt;p&gt;hold private keys;&lt;br&gt;
approve withdrawals manually;&lt;br&gt;
maintain internal user balances;&lt;br&gt;
decide which participant should send first;&lt;br&gt;
release funds through a human operator.&lt;/p&gt;

&lt;p&gt;The transaction result is determined by the order parameters and smart contract logic.&lt;/p&gt;

&lt;p&gt;Final Takeaway&lt;/p&gt;

&lt;p&gt;Not every on-chain deal should be open to the entire market.&lt;/p&gt;

&lt;p&gt;Some transactions are created for specific participants under previously agreed conditions.&lt;/p&gt;

&lt;p&gt;Typelex turns those agreements into fixed-rate, address-specific OTC orders.&lt;/p&gt;

&lt;p&gt;The contract verifies the authorized wallet, checks the transaction parameters, and executes both transfers atomically.&lt;/p&gt;

&lt;p&gt;Private negotiation. Defined counterparty. Transparent on-chain settlement.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building Gas-Efficient OTC Orders With EIP-712: A Typelex Case Study</title>
      <dc:creator>Typelex</dc:creator>
      <pubDate>Mon, 20 Jul 2026 14:45:16 +0000</pubDate>
      <link>https://dev.to/typelex/building-gas-efficient-otc-orders-with-eip-712-a-typelex-case-study-3g62</link>
      <guid>https://dev.to/typelex/building-gas-efficient-otc-orders-with-eip-712-a-typelex-case-study-3g62</guid>
      <description>&lt;h1&gt;
  
  
  Building Gas-Efficient OTC Orders With EIP-712: A Typelex Case Study
&lt;/h1&gt;

&lt;p&gt;Creating every OTC order directly onchain is easy to understand.&lt;/p&gt;

&lt;p&gt;The maker submits a transaction, the contract stores the order, and another participant fills it later.&lt;/p&gt;

&lt;p&gt;But this model has an obvious weakness:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every quote costs gas, even if nobody accepts it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For an OTC or RFQ protocol, this can become inefficient very quickly.&lt;/p&gt;

&lt;p&gt;A market maker may prepare dozens of short-lived quotes. A DAO may request offers from several counterparties before choosing one. A fund may receive a price that remains valid for only a few minutes.&lt;/p&gt;

&lt;p&gt;Writing every quote into blockchain storage is often unnecessary.&lt;/p&gt;

&lt;p&gt;A more efficient approach is to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create the order offchain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sign its structured data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Send the signed order to the intended counterparty.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Submit it onchain only when the trade is ready to settle.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article explores that architecture using &lt;strong&gt;Typelex&lt;/strong&gt; as a practical case study.&lt;/p&gt;

&lt;p&gt;We will build a simplified fixed-price OTC settlement system using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;EIP-712 typed data&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Offchain maker signatures&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Onchain signature verification&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nonce-based replay protection&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Restricted taker wallets&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Atomic ERC-20 settlement&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for EOAs and smart-contract wallets&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The code below is educational. It is not production-ready and should not be deployed without extensive testing, independent review, and a professional security audit.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why storing every quote onchain is inefficient
&lt;/h2&gt;

&lt;p&gt;Consider a traditional order workflow.&lt;/p&gt;

&lt;p&gt;The maker calls a function such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;createOrder(
    sellToken,
    buyToken,
    sellAmount,
    buyAmount,
    taker,
    deadline
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contract records the order in storage.&lt;/p&gt;

&lt;p&gt;This requires an onchain transaction before anyone can accept the quote.&lt;/p&gt;

&lt;p&gt;That may be reasonable for long-lived public orders. It is less efficient for short-lived OTC quotes.&lt;/p&gt;

&lt;p&gt;Imagine the following situation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A treasury requests quotes from five market makers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each market maker prepares a different offer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The treasury accepts only one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The other four quotes expire.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a fully onchain system, all five quotes may consume gas even though only one becomes a real transaction.&lt;/p&gt;

&lt;p&gt;The blockchain does not need to store every unsuccessful negotiation.&lt;/p&gt;

&lt;p&gt;It only needs enough information to verify the order that is eventually executed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Moving order creation offchain
&lt;/h2&gt;

&lt;p&gt;Instead of publishing the order through a transaction, the maker signs a structured message.&lt;/p&gt;

&lt;p&gt;A simplified order may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Order {
    address maker;
    address taker;
    address sellToken;
    address buyToken;
    uint256 sellAmount;
    uint256 buyAmount;
    uint256 nonce;
    uint256 deadline;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signature represents the maker’s authorization:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I agree to sell this exact amount of one token in exchange for this exact amount of another token under these conditions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The signed order may be delivered through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;an RFQ interface;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a private API;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;direct communication;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;an encrypted message;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a backend quotation service.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The order reaches the blockchain only when the taker is ready to settle.&lt;/p&gt;

&lt;p&gt;The workflow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Maker creates a quote offchain
              ↓
Maker signs the structured order
              ↓
Taker receives the order and signature
              ↓
Taker submits them to the contract
              ↓
The contract verifies and settles the trade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unused quotes never create blockchain storage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why EIP-712 is useful
&lt;/h2&gt;

&lt;p&gt;A wallet can sign an arbitrary hash, but raw hexadecimal messages are difficult for users to understand.&lt;/p&gt;

&lt;p&gt;EIP-712 provides a standard for signing &lt;strong&gt;typed structured data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of showing an unreadable string, the wallet can display meaningful fields such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;sellToken&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;buyToken&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;sellAmount&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;buyAmount&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;taker&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;deadline&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The signature domain can also include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the protocol name;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the protocol version;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the chain ID;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the verifying contract.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Typelex, the frontend domain may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Typelex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;chainId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42161&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;verifyingContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xSettlementContract&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each field serves a purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;&lt;/strong&gt; identifies the signing application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;&lt;/strong&gt; separates different versions of the protocol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;chainId&lt;/code&gt;&lt;/strong&gt; helps prevent a signature created on one network from being reused on another.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;verifyingContract&lt;/code&gt;&lt;/strong&gt; binds the signature to one specific settlement contract.&lt;/p&gt;

&lt;p&gt;However, EIP-712 does not automatically make an order single-use.&lt;/p&gt;

&lt;p&gt;For that, the protocol still needs &lt;strong&gt;nonces or another replay-protection mechanism&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Defining the Typelex order
&lt;/h2&gt;

&lt;p&gt;We start with the Solidity structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Order {
    address maker;
    address taker;
    address sellToken;
    address buyToken;
    uint256 sellAmount;
    uint256 buyAmount;
    uint256 nonce;
    uint256 deadline;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The corresponding EIP-712 type hash must contain exactly the same fields in exactly the same order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bytes32 private constant ORDER_TYPEHASH =
    keccak256(
        "Order("
        "address maker,"
        "address taker,"
        "address sellToken,"
        "address buyToken,"
        "uint256 sellAmount,"
        "uint256 buyAmount,"
        "uint256 nonce,"
        "uint256 deadline"
        ")"
    );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part is easy to underestimate.&lt;/p&gt;

&lt;p&gt;A small mismatch between the frontend and Solidity definitions will produce a different digest.&lt;/p&gt;

&lt;p&gt;For example, the signature will fail if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the field order is different;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;one field uses another type;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;capitalization does not match;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the frontend omits a field;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the Solidity contract adds a field that the frontend does not sign.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The signature may still be cryptographically valid, but it will not be valid for the message the contract reconstructs.&lt;/p&gt;




&lt;h2&gt;
  
  
  A simplified Typelex settlement contract
&lt;/h2&gt;

&lt;p&gt;Below is a complete educational example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {
    EIP712
} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";

import {
    SignatureChecker
} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

import {
    IERC20
} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {
    SafeERC20
} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {
    ReentrancyGuard
} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract TypelexSignedOrders is EIP712, ReentrancyGuard {
    using SafeERC20 for IERC20;

    struct Order {
        address maker;
        address taker;
        address sellToken;
        address buyToken;
        uint256 sellAmount;
        uint256 buyAmount;
        uint256 nonce;
        uint256 deadline;
    }

    bytes32 private constant ORDER_TYPEHASH =
        keccak256(
            "Order("
            "address maker,"
            "address taker,"
            "address sellToken,"
            "address buyToken,"
            "uint256 sellAmount,"
            "uint256 buyAmount,"
            "uint256 nonce,"
            "uint256 deadline"
            ")"
        );

    mapping(address maker =&amp;gt; mapping(uint256 nonce =&amp;gt; bool used))
        public nonceUsed;

    event OrderFilled(
        bytes32 indexed orderHash,
        address indexed maker,
        address indexed taker,
        address sellToken,
        address buyToken,
        uint256 sellAmount,
        uint256 buyAmount,
        uint256 nonce
    );

    event NonceCancelled(
        address indexed maker,
        uint256 indexed nonce
    );

    error InvalidAddress();
    error InvalidTokenPair();
    error InvalidAmount();
    error OrderExpired();
    error NonceAlreadyUsed();
    error UnauthorizedTaker();
    error InvalidSignature();

    constructor() EIP712("Typelex", "1") {}

    function hashOrder(
        Order calldata order
    ) public view returns (bytes32) {
        bytes32 structHash = keccak256(
            abi.encode(
                ORDER_TYPEHASH,
                order.maker,
                order.taker,
                order.sellToken,
                order.buyToken,
                order.sellAmount,
                order.buyAmount,
                order.nonce,
                order.deadline
            )
        );

        return _hashTypedDataV4(structHash);
    }

    function fillOrder(
        Order calldata order,
        bytes calldata signature
    ) external nonReentrant {
        _validateOrder(order);

        if (nonceUsed[order.maker][order.nonce]) {
            revert NonceAlreadyUsed();
        }

        if (
            order.taker != address(0) &amp;amp;&amp;amp;
            order.taker != msg.sender
        ) {
            revert UnauthorizedTaker();
        }

        bytes32 orderHash = hashOrder(order);

        bool validSignature =
            SignatureChecker.isValidSignatureNow(
                order.maker,
                orderHash,
                signature
            );

        if (!validSignature) {
            revert InvalidSignature();
        }

        nonceUsed[order.maker][order.nonce] = true;

        IERC20(order.buyToken).safeTransferFrom(
            msg.sender,
            order.maker,
            order.buyAmount
        );

        IERC20(order.sellToken).safeTransferFrom(
            order.maker,
            msg.sender,
            order.sellAmount
        );

        emit OrderFilled(
            orderHash,
            order.maker,
            msg.sender,
            order.sellToken,
            order.buyToken,
            order.sellAmount,
            order.buyAmount,
            order.nonce
        );
    }

    function cancelNonce(
        uint256 nonce
    ) external {
        if (nonceUsed[msg.sender][nonce]) {
            revert NonceAlreadyUsed();
        }

        nonceUsed[msg.sender][nonce] = true;

        emit NonceCancelled(msg.sender, nonce);
    }

    function _validateOrder(
        Order calldata order
    ) internal view {
        if (
            order.maker == address(0) ||
            order.sellToken == address(0) ||
            order.buyToken == address(0)
        ) {
            revert InvalidAddress();
        }

        if (order.sellToken == order.buyToken) {
            revert InvalidTokenPair();
        }

        if (
            order.sellAmount == 0 ||
            order.buyAmount == 0
        ) {
            revert InvalidAmount();
        }

        if (block.timestamp &amp;gt; order.deadline) {
            revert OrderExpired();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contract uses several OpenZeppelin components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;EIP712&lt;/code&gt;&lt;/strong&gt; for typed-data hashing;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;SignatureChecker&lt;/code&gt;&lt;/strong&gt; for EOA and smart-wallet signature validation;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;SafeERC20&lt;/code&gt;&lt;/strong&gt; for safer token transfers;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ReentrancyGuard&lt;/code&gt;&lt;/strong&gt; for protection against nested settlement calls.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let us break down the execution flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Validate the order
&lt;/h2&gt;

&lt;p&gt;The contract first rejects obviously invalid parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_validateOrder(order);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validation checks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;zero maker address;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;zero token address;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;identical sell and buy tokens;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;zero token amounts;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;expired deadlines.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These checks belong inside the smart contract.&lt;/p&gt;

&lt;p&gt;A frontend can perform the same validation to improve user experience, but frontend checks are not security controls.&lt;/p&gt;

&lt;p&gt;Anyone can bypass the interface and call the contract directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Check whether the nonce was used
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (nonceUsed[order.maker][order.nonce]) {
    revert NonceAlreadyUsed();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nonce makes the order single-use.&lt;/p&gt;

&lt;p&gt;Each maker has an independent nonce space.&lt;/p&gt;

&lt;p&gt;That means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Maker A, nonce 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Maker B, nonce 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;are two separate orders.&lt;/p&gt;

&lt;p&gt;But Maker A cannot successfully settle nonce &lt;code&gt;10&lt;/code&gt; twice.&lt;/p&gt;

&lt;p&gt;Without this check, the same signed order could potentially be replayed until the maker’s allowance or balance was exhausted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Verify the taker
&lt;/h2&gt;

&lt;p&gt;Some OTC orders may be public.&lt;/p&gt;

&lt;p&gt;Others may be intended for one specific wallet.&lt;/p&gt;

&lt;p&gt;The contract supports both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (
    order.taker != address(0) &amp;amp;&amp;amp;
    order.taker != msg.sender
) {
    revert UnauthorizedTaker();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;taker = address(0);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;any wallet may fill the order.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;taker&lt;/code&gt; contains a specific address, only that wallet can execute it.&lt;/p&gt;

&lt;p&gt;This is especially important for privately negotiated RFQ quotes.&lt;/p&gt;

&lt;p&gt;A maker may offer a special rate to one approved counterparty. Without a taker restriction, anyone who receives the signed order could attempt to execute it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A restricted taker does not make the order private. It only controls who can fill it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Blockchain observers may still see the final settlement transaction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Reconstruct the signed digest
&lt;/h2&gt;

&lt;p&gt;The contract hashes the complete order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bytes32 orderHash = hashOrder(order);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The digest includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;all order fields;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the Typelex domain name;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the protocol version;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the current chain ID;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the verifying contract.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means a taker cannot modify one field and reuse the same signature.&lt;/p&gt;

&lt;p&gt;Changing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;buyAmount: 500,000 USDC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;buyAmount: 490,000 USDC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;produces a different digest.&lt;/p&gt;

&lt;p&gt;The maker’s original signature will no longer be valid.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Verify the maker’s signature
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool validSignature =
    SignatureChecker.isValidSignatureNow(
        order.maker,
        orderHash,
        signature
    );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contract asks one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Did the maker authorize this exact order?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using &lt;code&gt;SignatureChecker&lt;/code&gt; provides support for two wallet types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Externally owned accounts
&lt;/h3&gt;

&lt;p&gt;Traditional wallets sign with an ECDSA private key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smart-contract wallets
&lt;/h3&gt;

&lt;p&gt;Multisigs and smart accounts may validate signatures through ERC-1271.&lt;/p&gt;

&lt;p&gt;This distinction matters because a contract wallet does not necessarily have one private key corresponding directly to its address.&lt;/p&gt;

&lt;p&gt;Its authorization may depend on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;several owners;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a signing threshold;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;installed modules;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;custom validation logic.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The signature should therefore be validated during execution, not just once when the order is received by the frontend.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Mark the nonce as used
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nonceUsed[order.maker][order.nonce] = true;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contract updates its internal state before calling external token contracts.&lt;/p&gt;

&lt;p&gt;This follows the &lt;strong&gt;checks-effects-interactions&lt;/strong&gt; pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Perform checks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Call external contracts.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a malicious token attempts to reenter &lt;code&gt;fillOrder&lt;/code&gt;, the nonce has already been marked as used.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;nonReentrant&lt;/code&gt; modifier adds another layer of protection.&lt;/p&gt;

&lt;p&gt;If a later token transfer fails, the complete transaction reverts, including the nonce update.&lt;/p&gt;

&lt;p&gt;The order is not permanently consumed by a failed settlement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Transfer both assets
&lt;/h2&gt;

&lt;p&gt;The taker sends the requested payment token to the maker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IERC20(order.buyToken).safeTransferFrom(
    msg.sender,
    order.maker,
    order.buyAmount
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The maker sends the offered token to the taker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IERC20(order.sellToken).safeTransferFrom(
    order.maker,
    msg.sender,
    order.sellAmount
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both operations happen inside the same transaction.&lt;/p&gt;

&lt;p&gt;The settlement has only two valid outcomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Both transfers succeed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The entire transaction reverts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the atomic property of the exchange.&lt;/p&gt;

&lt;p&gt;The maker should not lose tokens without receiving payment.&lt;/p&gt;

&lt;p&gt;The taker should not send payment without receiving the purchased asset.&lt;/p&gt;




&lt;h2&gt;
  
  
  Signing the order with Viem
&lt;/h2&gt;

&lt;p&gt;The frontend may use &lt;code&gt;signTypedData&lt;/code&gt; to request the maker’s signature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;WalletClient&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TypelexOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;maker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;taker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;sellToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;buyToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;sellAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;buyAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;deadline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;maker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;taker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sellToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buyToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sellAmount&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uint256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buyAmount&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uint256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nonce&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uint256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deadline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uint256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;signTypelexOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;walletClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WalletClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;maker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;settlementContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;chainId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TypelexOrder&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Hex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;maker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Connected wallet does not match the order maker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sellAmount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buyAmount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Order amounts must be greater than zero&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deadline&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;currentTimestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Order deadline has already passed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;walletClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signTypedData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;maker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Typelex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;chainId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;verifyingContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;settlementContract&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;primaryType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Order&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend definitions must match Solidity exactly.&lt;/p&gt;

&lt;p&gt;Double-check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Field names&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Field order&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Solidity types&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Protocol name&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Protocol version&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chain ID&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verifying contract&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A single mismatch creates a different signature digest.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example order
&lt;/h2&gt;

&lt;p&gt;Imagine that the maker wants to sell 100,000 tokens for 500,000 USDC.&lt;/p&gt;

&lt;p&gt;The order is restricted to one counterparty and expires after ten minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TypelexOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;maker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xMakerWallet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;taker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xApprovedTaker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sellToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xTokenAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;buyToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xUSDCAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sellAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;_000n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;buyAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="nx"&gt;_000n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;84&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;deadline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the difference in decimals.&lt;/p&gt;

&lt;p&gt;The example assumes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the offered token uses &lt;strong&gt;18 decimals&lt;/strong&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;USDC uses &lt;strong&gt;6 decimals&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The contract works with integer units.&lt;/p&gt;

&lt;p&gt;It does not understand a human-readable value such as &lt;code&gt;500,000 USDC&lt;/code&gt; unless the frontend converts it correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  A valid signature does not guarantee settlement
&lt;/h2&gt;

&lt;p&gt;This point is critical.&lt;/p&gt;

&lt;p&gt;A valid signature proves that the maker authorized the order.&lt;/p&gt;

&lt;p&gt;It does not prove that the maker can still complete it.&lt;/p&gt;

&lt;p&gt;Before settlement, the maker may:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;transfer the tokens elsewhere;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;revoke the contract allowance;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;spend part of the balance;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;become blacklisted by the token issuer;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;sign several orders backed by the same funds.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A complete settlement check therefore includes more than signature verification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the signature valid?
Is the nonce unused?
Is the order unexpired?
Is the taker authorized?
Does the maker have enough balance?
Is the allowance sufficient?
Are token transfers currently enabled?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A well-designed interface should simulate the transaction before asking the taker to submit it.&lt;/p&gt;

&lt;p&gt;However, the smart contract must remain the final source of truth.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the maker needs an allowance
&lt;/h2&gt;

&lt;p&gt;The example uses a non-escrowed model.&lt;/p&gt;

&lt;p&gt;The maker keeps the offered tokens until the order is filled.&lt;/p&gt;

&lt;p&gt;During settlement, the contract calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;safeTransferFrom(
    order.maker,
    msg.sender,
    order.sellAmount
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The maker must approve the settlement contract before execution.&lt;/p&gt;

&lt;p&gt;This creates a trade-off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantage
&lt;/h3&gt;

&lt;p&gt;The maker does not need to lock assets when signing a quote.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disadvantage
&lt;/h3&gt;

&lt;p&gt;The quote may become unfillable if the balance or allowance changes.&lt;/p&gt;

&lt;p&gt;A signature is therefore an authorization, not proof that liquidity is reserved.&lt;/p&gt;




&lt;h2&gt;
  
  
  Replay protection
&lt;/h2&gt;

&lt;p&gt;Without replay protection, the same signed order could be executed repeatedly.&lt;/p&gt;

&lt;p&gt;The contract prevents this with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nonceUsed[maker][nonce] = true;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a successful fill, the nonce cannot be used again.&lt;/p&gt;

&lt;p&gt;The maker can also cancel an unused nonce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function cancelNonce(
    uint256 nonce
) external {
    if (nonceUsed[msg.sender][nonce]) {
        revert NonceAlreadyUsed();
    }

    nonceUsed[msg.sender][nonce] = true;

    emit NonceCancelled(
        msg.sender,
        nonce
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This cancellation requires an onchain transaction.&lt;/p&gt;

&lt;p&gt;That is one of the central trade-offs of offchain orders:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Creating a quote can be free, but guaranteed cancellation still requires an onchain state update.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Better cancellation models
&lt;/h2&gt;

&lt;p&gt;A production protocol may use a more advanced cancellation system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minimum valid nonce
&lt;/h3&gt;

&lt;p&gt;The maker stores a minimum acceptable nonce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(address =&amp;gt; uint256)
    public minValidNonce;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the maker raises the value from &lt;code&gt;100&lt;/code&gt; to &lt;code&gt;200&lt;/code&gt;, every order below &lt;code&gt;200&lt;/code&gt; becomes invalid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; Many old orders can be cancelled in one transaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; Nonce ordering becomes part of the protocol logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nonce bitmap
&lt;/h3&gt;

&lt;p&gt;Nonce states can be grouped into bitmaps.&lt;/p&gt;

&lt;p&gt;This may reduce storage costs when large numbers of orders are filled or cancelled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; Efficient storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; More complex implementation and testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Order-hash cancellation
&lt;/h3&gt;

&lt;p&gt;The contract stores the hash of each cancelled order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; Precise cancellation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; A new storage entry is required for every cancelled order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Short expiration windows
&lt;/h3&gt;

&lt;p&gt;RFQ orders may expire after a few minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; Stale quotes disappear quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; Expiration does not replace explicit cancellation while the order remains valid.&lt;/p&gt;




&lt;h2&gt;
  
  
  Smart-wallet signatures can change over time
&lt;/h2&gt;

&lt;p&gt;An EOA signature normally remains valid for the same digest unless the private key is compromised.&lt;/p&gt;

&lt;p&gt;A smart-contract wallet behaves differently.&lt;/p&gt;

&lt;p&gt;Its validation rules may change after:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;an owner is removed;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the signing threshold changes;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a module is disabled;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the wallet contract is upgraded;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;recovery logic is triggered.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A signature that was valid earlier may no longer be valid when settlement occurs.&lt;/p&gt;

&lt;p&gt;That is why ERC-1271 signatures should be checked at execution time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fixed price and zero slippage
&lt;/h2&gt;

&lt;p&gt;The order defines exact quantities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100,000 TOKEN
for
500,000 USDC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contract does not query an AMM pool to calculate the output.&lt;/p&gt;

&lt;p&gt;Therefore, the rate does not gradually deteriorate as the order consumes liquidity.&lt;/p&gt;

&lt;p&gt;This provides:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Zero execution slippage relative to the signed order terms.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It does not guarantee:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the best market price;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a fair OTC quote;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;future token value;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;stablecoin peg stability;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;economic profitability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The smart contract guarantees execution consistency.&lt;/p&gt;

&lt;p&gt;It does not evaluate the quality of the negotiation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fixed pricing reduces some MEV exposure
&lt;/h2&gt;

&lt;p&gt;A traditional sandwich attack depends on changing the state of a public liquidity pool around a victim’s swap.&lt;/p&gt;

&lt;p&gt;The attacker modifies the pool price before the victim’s transaction and trades again afterward.&lt;/p&gt;

&lt;p&gt;A signed Typelex order does not use the external pool to determine its output.&lt;/p&gt;

&lt;p&gt;A bot cannot change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;500,000 USDC for 100,000 TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;500,000 USDC for 95,000 TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;by trading against a public AMM before settlement.&lt;/p&gt;

&lt;p&gt;The order contains exact amounts.&lt;/p&gt;

&lt;p&gt;However, this does not mean the transaction is fully MEV-proof.&lt;/p&gt;

&lt;p&gt;An observer may still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;see the pending transaction;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;compete for block inclusion;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;attempt to delay the transaction;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;identify the participating wallets;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;trade on another venue;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;react to later hedging activity.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture protects fixed settlement terms from AMM-based repricing.&lt;/p&gt;

&lt;p&gt;It does not provide complete privacy or eliminate every form of MEV.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fee-on-transfer tokens
&lt;/h2&gt;

&lt;p&gt;The contract assumes that transferring &lt;code&gt;100&lt;/code&gt; units causes the recipient to receive exactly &lt;code&gt;100&lt;/code&gt; units.&lt;/p&gt;

&lt;p&gt;That assumption is not valid for every token.&lt;/p&gt;

&lt;p&gt;A fee-on-transfer token may behave like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requested transfer: 100,000
Recipient receives: 98,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SafeERC20&lt;/code&gt; helps with inconsistent ERC-20 return values, but it does not guarantee that the recipient received the nominal amount.&lt;/p&gt;

&lt;p&gt;Possible protocol policies include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Allowlisting reviewed tokens&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rejecting fee-on-transfer assets&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Checking balances before and after transfers&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using token-specific adapters&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Defining settlement by actual received amounts&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Balance checks are also imperfect for rebasing tokens.&lt;/p&gt;

&lt;p&gt;Token compatibility should therefore be a documented protocol decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Approval risk
&lt;/h2&gt;

&lt;p&gt;Many applications ask users for unlimited token approvals.&lt;/p&gt;

&lt;p&gt;That improves convenience, but it also increases the consequences of a contract vulnerability.&lt;/p&gt;

&lt;p&gt;Safer alternatives include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;approving only the amount required for one order;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;using permit-based approvals when available;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;displaying active allowances in the interface;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;separating settlement contracts by version;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;resetting allowances after use.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no universal best option.&lt;/p&gt;

&lt;p&gt;The protocol must balance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;user experience;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gas cost;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;contract complexity;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;approval exposure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Overcommitted orders
&lt;/h2&gt;

&lt;p&gt;A maker may sign several orders using the same balance.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Maker balance: 1,000,000 TOKEN

Order A: Sell 1,000,000 TOKEN
Order B: Sell 1,000,000 TOKEN
Order C: Sell 1,000,000 TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three signatures may be valid.&lt;/p&gt;

&lt;p&gt;But after Order A executes, Orders B and C may fail because the maker no longer has enough tokens.&lt;/p&gt;

&lt;p&gt;Offchain signatures do not reserve inventory.&lt;/p&gt;

&lt;p&gt;Possible solutions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;using escrow;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;creating very short-lived RFQ quotes;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;exposing outstanding order liabilities;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;limiting active nonce ranges;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;accepting that signed quotes are not guaranteed liquidity.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Escrowed versus non-escrowed orders
&lt;/h2&gt;

&lt;p&gt;Both approaches have advantages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Non-escrowed signed orders
&lt;/h3&gt;

&lt;p&gt;The maker signs offchain and keeps the assets until settlement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;no transaction is required to create an order;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;unused quotes cost no gas;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;funds remain available until execution;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the model works well for RFQ systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the maker may lose balance or allowance;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;several orders may compete for the same assets;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the taker has less certainty that the order is fillable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Escrowed orders
&lt;/h3&gt;

&lt;p&gt;The maker deposits tokens into a smart contract before settlement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the assets are reserved;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the taker has stronger execution certainty;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;overcommitting the same balance becomes harder.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;order creation requires gas;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;funds remain locked;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;unused orders still create onchain state;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;escrow-contract risk becomes more important.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A hybrid protocol could support both models.&lt;/p&gt;

&lt;p&gt;Short-lived RFQ quotes may use signatures, while large strategic transactions may use isolated escrow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security tests worth writing
&lt;/h2&gt;

&lt;p&gt;A production implementation needs more than a few unit tests.&lt;/p&gt;

&lt;p&gt;It should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;unit tests;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fuzz testing;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;integration tests;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;invariant testing;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;adversarial token mocks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At minimum, the following properties should be tested.&lt;/p&gt;

&lt;h3&gt;
  
  
  The same nonce cannot execute twice
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First fill: succeeds
Second fill: reverts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A cancelled nonce cannot execute
&lt;/h3&gt;

&lt;p&gt;After cancellation, every signature using that maker and nonce must fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  An expired order cannot execute
&lt;/h3&gt;

&lt;p&gt;Test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;one second before expiration;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;exactly at expiration;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;one second after expiration.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The contract should clearly define its boundary behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing any field invalidates the signature
&lt;/h3&gt;

&lt;p&gt;Modify each field independently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;maker;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;taker;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;sell token;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;buy token;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;sell amount;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;buy amount;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;nonce;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;deadline.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every modified order must fail verification.&lt;/p&gt;

&lt;h3&gt;
  
  
  A signature cannot be replayed on another contract
&lt;/h3&gt;

&lt;p&gt;Deploy two settlement contracts.&lt;/p&gt;

&lt;p&gt;A signature created for Contract A should fail on Contract B.&lt;/p&gt;

&lt;h3&gt;
  
  
  A signature cannot be replayed on another chain
&lt;/h3&gt;

&lt;p&gt;A different chain ID should produce a different digest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Only the approved taker can fill a private order
&lt;/h3&gt;

&lt;p&gt;Possessing the signed order must not be enough for an unauthorized wallet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failed transfers restore the nonce state
&lt;/h3&gt;

&lt;p&gt;If either token transfer fails, the entire transaction should revert and the nonce should remain unused.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reentrancy cannot settle the order twice
&lt;/h3&gt;

&lt;p&gt;Tests should use malicious token contracts capable of making callback attempts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smart-wallet signatures work correctly
&lt;/h3&gt;

&lt;p&gt;Test ERC-1271 validation and changes to wallet configuration.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Typelex gains from signed orders
&lt;/h2&gt;

&lt;p&gt;This architecture offers several practical benefits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lower cost for unused quotes
&lt;/h3&gt;

&lt;p&gt;Quotes that expire without being filled never create onchain storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faster RFQ workflows
&lt;/h3&gt;

&lt;p&gt;Market makers can create quotes without waiting for a transaction to confirm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Controlled distribution
&lt;/h3&gt;

&lt;p&gt;A signed order can be sent directly to one intended counterparty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixed settlement conditions
&lt;/h3&gt;

&lt;p&gt;The signature commits the maker to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;exact token addresses;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;exact quantities;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;one nonce;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;one deadline;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;an optional taker wallet.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Flexible execution
&lt;/h3&gt;

&lt;p&gt;The taker or an approved relayer can submit the final transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Broader wallet support
&lt;/h3&gt;

&lt;p&gt;The system can support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ordinary wallets;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;multisigs;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;smart accounts;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;contract-based treasuries.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These advantages do not remove the need for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;correct pricing;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;sufficient balances;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;token allowances;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;replay protection;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;token compatibility policies;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;contract security.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;An onchain OTC protocol does not need to publish every quote to the blockchain.&lt;/p&gt;

&lt;p&gt;EIP-712 allows Typelex to represent an order as structured data, collect the maker’s authorization offchain, and verify it only when settlement happens.&lt;/p&gt;

&lt;p&gt;The full flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Define the order terms
          ↓
Sign typed data offchain
          ↓
Send the order to the taker
          ↓
Verify the signature onchain
          ↓
Check nonce, deadline, and wallet
          ↓
Transfer both assets atomically
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signature itself is not the hardest part.&lt;/p&gt;

&lt;p&gt;The difficult work is designing the complete order lifecycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replay protection&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cancellation&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Wallet restrictions&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Allowance management&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Smart-account support&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stale quote handling&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Overcommitted balances&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nonstandard token behavior&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Atomic state transitions&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Typelex can use this architecture to separate two responsibilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Counterparties or RFQ systems determine the economic terms.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The smart contract verifies authorization and enforces settlement.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That separation can make OTC execution more gas-efficient without turning every offchain quote into a trusted manual exchange.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: This article was prepared with AI assistance and reviewed before publication.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>blockchain</category>
      <category>ethereum</category>
      <category>performance</category>
    </item>
    <item>
      <title>Bypassing AMM Slippage: How Typelex Uses On-Chain Atomic Swaps for MEV-Proof OTC Trading</title>
      <dc:creator>Typelex</dc:creator>
      <pubDate>Wed, 15 Jul 2026 12:39:04 +0000</pubDate>
      <link>https://dev.to/typelex/bypassing-amm-slippage-how-typelex-uses-on-chain-atomic-swaps-for-mev-proof-otc-trading-1759</link>
      <guid>https://dev.to/typelex/bypassing-amm-slippage-how-typelex-uses-on-chain-atomic-swaps-for-mev-proof-otc-trading-1759</guid>
      <description>&lt;p&gt;If you’ve ever built or interacted with DeFi protocols, you know the mathematical limitations of Constant Product Market Makers ($x \times y = k$). While AMMs are great for retail liquidity, executing a large transaction (e.g., $100,000+) directly against a liquidity pool triggers two major issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Severe Slippage:&lt;/strong&gt; The marginal price of the asset degrades exponentially relative to the trade size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MEV Exploitation (Sandwich Attacks):&lt;/strong&gt; Public mempool transactions are highly vulnerable. Front-running bots will buy the asset ahead of your execution block, push the price up to your maximum slippage limit, and dump it immediately after.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve this without relying on centralized, custodial desks or risky off-chain escrow setups, we built &lt;strong&gt;Typelex&lt;/strong&gt;—a decentralized, non-custodial P2P OTC protocol. &lt;/p&gt;

&lt;p&gt;Here is a look at how we bypassed the AMM bonding curve entirely using on-chain atomic swaps.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Architecture of an On-Chain Atomic Swap
&lt;/h3&gt;

&lt;p&gt;Instead of routing trades through active liquidity pools, Typelex utilizes isolated smart contracts to execute peer-to-peer trades. The entire swap happens &lt;strong&gt;atomically&lt;/strong&gt;: either all conditions are met within a single block execution, or the entire transaction reverts.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conceptual Smart Contract Logic (Solidity-based)
&lt;/h4&gt;

&lt;p&gt;To understand how the trustless escrow works under the hood, here is a simplified mental model of the swap execution logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Order {
    address maker;
    address taker; // address(0) if public
    address tokenA;
    uint256 amountA;
    address tokenB;
    uint256 amountB;
    bool active;
}

mapping(uint256 =&amp;gt; Order) public orders;

function takeOrder(uint256 orderId) external {
    Order storage order = orders[orderId];
    require(order.active, "Order not active");
    if (order.taker != address(0)) {
        require(msg.sender == order.taker, "Unauthorized taker");
    }

    order.active = false;

    // Pull Token B from Taker to Maker
    IERC20(order.tokenB).transferFrom(msg.sender, order.maker, order.amountB);

    // Push Token A from Contract Escrow to Taker
    IERC20(order.tokenA).transfer(msg.sender, order.amountA);

    emit OrderExecuted(orderId, msg.sender);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why This Design is Inherently MEV-Proof
&lt;/h3&gt;

&lt;p&gt;By shifting the execution model from AMMs to fixed-rate P2P swaps, Typelex mitigates common mainnet exploits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero Slippage Parameters:&lt;/strong&gt; Because the exchange rate between Token A and Token B is hardcoded directly into the order struct, there is no price slippage to exploit. MEV searchers cannot sandwich the transaction because the contract will strictly revert if the exact amounts are not cleared.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Spot Market Footprint:&lt;/strong&gt; The transaction occurs entirely within the Typelex contract storage states, moving balances directly between the maker's and taker's wallets. No public liquidity pools are touched, meaning the spot price on external trackers remains completely unaffected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Direct Cryptographic Access Control:&lt;/strong&gt; For private trades negotiated off-chain, the maker can assign a cryptographic constraint (locking the swap to a specific &lt;code&gt;taker&lt;/code&gt; address). Even if a malicious bot views the transaction pending in the mempool, any attempt to hijack or front-run the execution will fail the &lt;code&gt;msg.sender&lt;/code&gt; validation check on-chain.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Primary Use Cases for Web3 Developers and DAOs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Treasury Diversification:&lt;/strong&gt; DAOs can liquidize or diversify native project tokens into stablecoins without signaling market dumps or feeding MEV bots.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strategic OTC Rounds:&lt;/strong&gt; Projects can distribute allocations to early investors or partners securely on-chain, ensuring tokens are locked or routed directly to validated contributor wallets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gas-Optimized Settlement:&lt;/strong&gt; By bypassing complex routing paths and multi-hop pool swaps, transactions consume minimal gas, keeping execution costs flat even during network congestion.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;We designed Typelex to bring security and structural integrity back to high-volume on-chain trading. By moving OTC completely on-chain, we remove the need to trust intermediaries or centralized escrow services.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>crypto</category>
      <category>fintech</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
