7Block Labs
Blockchain Technology

ByAUJay

Composable Solvers: A Modular Codebase for Intent Matching and Execution


Why this matters now

Intent-based UX has evolved from just a trendy term to something that's actually making waves in the real world. Projects like UniswapX, 1inch Fusion, and CoW Protocol are already leveraging competitive "solvers"--also called fillers or resolvers--to help bring users' goals to life. And it doesn't stop there; the Ethereum community is jumping in too! They’re working on standardizing cross-chain intents with ERC‑7683 and creating a shared, modular setup through the Open Intents Framework (OIF). This powerful combo gives startups and larger companies alike a flexible, composable solver stack, so they can sidestep the headache of building separate infrastructure for every application or chain. Check it out here: (x.uniswap.org).


What “composable solvers” means

A composable solver codebase is like a set of flexible building blocks that you can mix, match, or swap out with ease, all without messing with the core logic underneath.

  • Intent ingestion: Get into the nitty-gritty of on-chain or off-chain intent feeds and make sure those orders are smoothed out to match a common schema--think ERC‑7683 order structs for those cross-chain transactions. Take a look at it here.
  • Constraint engine: It’s crucial to stick to those user-defined hard constraints--like price, tokens, and expiry--before you start moving things around. Don’t forget about preferences and policy rules too! For more info, check it out here.
  • Routing and pricing: Look for fills by exploring DEX/AMM aggregation, using internalization strategies, or even by taking advantage of cross-domain paths.
  • Risk and funding: It’s really about keeping an eye on position sizes, establishing inventory limits, and shuffling things around between L2s.
  • Settlement adapters: Tie everything together by completing fills using standard settler contracts or messaging layers. This could be stuff like ERC‑7683 settlers or RIP‑7755 for those L2↔L2 calls, along with your go-to bridge and messaging providers. If you want to dive deeper, check it out here.
  • Submission and Privacy: You can choose to send transactions via private order flow networks like MEV-Share or SUAVE, or you might want to use sequencer APIs to help cut down on leakage and minimize MEV risks. For more details, check it out here.
  • Observability and SLAs: Stay on top of real-time telemetry, keep an eye on your fill-rate SLOs, and make sure your incident response tools are all set and ready to roll.

This modular setup makes it super easy to dive into different intent markets like CoW, UniswapX, 1inch, and ERC-7683--all from one codebase. It's a fantastic way to enhance your order flow without breaking the bank on infrastructure and audit costs. Want to learn more? Check it out here!


The state of the ecosystem (late 2025)

  • ERC‑7683: This draft standard was put together by Uniswap Labs and Across to help manage cross-chain intents with some handy generic order structures and settler interfaces. The goal? To encourage a universal filler network and build shared infrastructure. There are already some cool real-world applications in action, such as the AcrossOriginSettler on Base and Arbitrum. Take a look here: (eips.ethereum.org)
  • Open Intents Framework (OIF): Supported by the Ethereum Foundation, this is a super flexible reference stack that takes care of everything, from where intents start to how they get fulfilled, settled, and rebalanced. They're also on track to launch a reference solver and a cross-chain validation module by 2025. Check out the details here: (blog.ethereum.org)
  • UniswapX: Picture this as a super aggregator that brings in third-party fillers, has built-in MEV protection, and settles transactions in “1 block or less on average.” It’s all about providing open services for order propagation and tweaking parameters. Check it out here: (x.uniswap.org)
  • 1inch Fusion: So, here’s the scoop on 1inch Fusion. It features resolver-based Dutch auctions where those approved resolvers can stake Unicorn Power. There’s a select group of resolvers to choose from, and the whole setup is thoroughly documented. On top of that, Solana Fusion runs completely on-chain for intents. If you want to dive deeper, check out this link: (help.1inch.io)
  • CoW Protocol: This is where the magic happens with open solver competitions that mix in batch auctions and build out some cool composable order tools (check out ComposableCoW, sdk‑composable, and solver templates). Want to dive deeper? Head over to: (docs.cow.fi)
  • SUAVE and MEV‑Share: These projects are all about keeping your order flow private and diving into order flow auction research. They're really crucial for things like solver submissions, batching, and rebates. Want to learn more? Check it out here: (writings.flashbots.net)

A reference architecture for a composable solver

Here’s a great layout we recommend for teams diving into multi-market solvers that link up with ERC‑7683, CoW, UniswapX, and 1inch.

1) Ingestion and normalization

  • ERC‑7683 Listener: This tool captures OrderCreated events from origin settlers. If you’re working with Permit2, it decodes the witness data and translates it into our internal Order format. You can explore it more at erc7683.org.
  • Market Adapters:

    • CoW: You can grab a WebSocket or HTTP order book feed for batch auctions.
    • UniswapX: Feel free to subscribe to the order propagation service and also check out the parameterization API to set up your guardrails. For all the nitty-gritty details, hop over to GitHub.
    • 1inch Fusion: You’ve got the choice to either poll the relayer backend or jump straight in as a registered resolver. And hey, don’t forget to keep an eye on those auction curves! You can find the complete rundown on 1inch's blog.

Core Design

Let's stick with a standard Intent that covers all the essential constraints, like minOut, deadline, and legs. Plus, we should throw in some nice-to-have preferences, such as route bias, privacy, and slippage rebate. Don’t forget to include an expected fee for those accounting details.

When you're working on cross-chain transactions, make sure to add legs[] in line with the ERC‑7683 guidelines, along with the destination chain IDs. If you need more details, you can find it over at erc7683.org.

2) Constraint engine

Implement a Declarative Constraint Checker

Building Your Own Declarative Constraint Checker

Creating a declarative constraint checker can be a fun little project! This tool will allow you to validate data against specific rules without getting lost in all the procedural details. Let’s break down how to implement one.

Step 1: Define Your Constraints

First things first, you need to lay out the rules for what your data should match. Think about the scenarios you want to cover and write down the constraints clearly.

Here’s a simple example:

constraints = {
    "age": {"min": 18, "max": 65},
    "email": {"pattern": r"[^@]+@[^@]+\.[^@]+"}
}

Step 2: Create a Validator Function

Next up, you’ll want to create a function that takes your data and checks it against the constraints you just defined. This function should go through each constraint and validate the data accordingly.

Here’s a sample validator:

def validate(data, constraints):
    errors = {}
    for field, rules in constraints.items():
        if field in data:
            if "min" in rules and data[field] < rules["min"]:
                errors[field] = f"Must be at least {rules['min']}."
            if "max" in rules and data[field] > rules["max"]:
                errors[field] = f"Must be no more than {rules['max']}."
            if "pattern" in rules and not re.match(rules["pattern"], data[field]):
                errors[field] = "Invalid format."
    return errors

Step 3: Test Your Validator

Once you have your validator function ready, it’s time to give it a spin! You can run some tests to see how it behaves with different inputs.

Here’s an example of how to use the validator:

data_to_validate = {
    "age": 17,
    "email": "wrong-email-format"
}

errors = validate(data_to_validate, constraints)
if errors:
    print("Validation failed:", errors)
else:
    print("Validation successful!")

Step 4: Expand Your Checker

Now that you’ve got the basics down, feel free to expand your checker! You can add more complex constraints, support different data types, or even create a user-friendly interface. The sky's the limit!

Conclusion

And there you have it! A simple way to create a declarative constraint checker that makes validating data a breeze. It’s a great exercise to sharpen your programming skills and can be super useful in various applications. Have fun building!

Step 1: Define Your Constraints

First things first, you’ll want to figure out what kind of constraints you’re looking to check with your data. Here’s a handy list of some common constraints to consider:

  • Unique: Guarantees that values are one-of-a-kind within a specific context.
  • Range: Verifies that values sit comfortably within a designated range.
  • Format: Confirms that the values follow the right format (such as emails or phone numbers).
  • Presence: Ensures that a value is actually provided and not left blank.

You can set up these constraints using a straightforward dictionary format or any other style that works for you.

Step 2: Create a Constraint Class

Next up, let’s create a class to manage our constraints. It’ll be great to keep everything organized in one place. Check out this quick example:

class Constraint:
    def __init__(self, field, constraint_type, value=None):
        self.field = field
        self.constraint_type = constraint_type
        self.value = value

    def validate(self, data):
        # Method to validate the data against the constraint
        pass

Step 3: Implement Validation Logic

Now, it's time to dive into the validate method. This is where the fun begins! Depending on the type of constraint we're dealing with, we'll be checking the data. Here’s a rough outline:

class Constraint:
    def __init__(self, field, constraint_type, value=None):
        self.field = field
        self.constraint_type = constraint_type
        self.value = value

    def validate(self, data):
        field_value = data.get(self.field)

        if self.constraint_type == 'unique':
            return self._check_unique(field_value)
        elif self.constraint_type == 'range':
            return self._check_range(field_value, self.value)
        elif self.constraint_type == 'format':
            return self._check_format(field_value, self.value)
        elif self.constraint_type == 'presence':
            return self._check_presence(field_value)

    def _check_unique(self, value):
        # Logic to check uniqueness
        pass

    def _check_range(self, value, range_values):
        return range_values[0] <= value <= range_values[1]

    def _check_format(self, value, format_pattern):
        return bool(re.match(format_pattern, value))

    def _check_presence(self, value):
        return value is not None

Step 4: Assemble Your Checker

Let's bring everything together in a ConstraintChecker class that will help us validate our data using these constraints:

class ConstraintChecker:
    def __init__(self):
        self.constraints = []

    def add_constraint(self, constraint):
        self.constraints.append(constraint)

    def validate(self, data):
        for constraint in self.constraints:
            if not constraint.validate(data):
                return False
        return True

Step 5: Test Your Checker

Alright, let’s dive in and give your checker a whirl! Go ahead and whip up a sample dataset along with some constraints to see it in action:

data = {
    'username': 'user1',
    'age': 25,
    'email': 'user@example.com'
}

checker = ConstraintChecker()
checker.add_constraint(Constraint('username', 'unique'))
checker.add_constraint(Constraint('age', 'range', (18, 30)))
checker.add_constraint(Constraint('email', 'format', r'^[\w\.-]+@[\w\.-]+\.\w+$'))
checker.add_constraint(Constraint('username', 'presence'))

is_valid = checker.validate(data)
print(f"Data is valid: {is_valid}")

Summary

And there you go! You've just created a declarative constraint checker that verifies your data according to the rules you've set. It's a super flexible method to keep your data tidy and consistent. Have fun coding!

  • Hard checks: This is all about making sure signatures are legit, deadlines are hit, token allowlists are in order, minOut is verified, and that there’s a solid partial-fill policy laid out.
  • Cross-chain checks: We need to confirm that everything’s complete, ensure compatibility with the settler, and check which messaging backends are good to go (like sticking to just RIP-7755 or Hyperlane-only).
  • Risk policy: Here, we’re talking about setting limits based on the venue and asset, enforcing notional caps, and doing counterparty sanctions screening when it’s needed.

Anoma's perspective on constraints versus preferences makes it super easy to keep your checker simple and clear for audits. Take a look here: (anoma.net)

3) Routing and pricing engine

  • Single-domain routing: This is all about using AMM graph searches on platforms such as Uniswap v2/v3, Curve, and Balancer. Don't forget to explore RFQs to market makers and internal crossing when it's allowed (like with CoW batches). For a deeper dive, you can check it out here.
  • Cross-domain routing: For this, you’ve got a couple of options with ERC‑7683 legs and settlers to consider. You can choose a settlement mechanism--right now, the Across settler is up and running on Base/Arbitrum--and you’ll also need to pick a verification path (you can think of it like RIP‑7755 when shifting between L2s). Want more info? Check it out here.
  • Pricing sources: This involves blending on-chain quotes with off-chain market makers. It's super important to set some pessimistic limits to avoid getting caught in adverse selection on those fast L2s.

Tip: Before jumping into the auction, take a moment to calculate those “route capsules” for each venue, keeping in mind the gas costs and any potential failure penalties. This way, your solver can churn out responses in just a few milliseconds!

4) Settlement adapters

Abstract a Common Interface:

Creating a shared interface can really make things easier when different parts of a system need to chat with each other. Think of it as a bridge that helps various components collaborate smoothly without getting lost in the nitty-gritty details. Here’s a straightforward approach to tackle it:

  1. Identify Core Functionality:
    To kick things off, check out the main features or functions you’re gonna need in various parts of your application. Consider what’s really essential and how you might bundle these functionalities together.
  2. Define the Interface:
    Now that you've nailed down your core functionalities, it’s time to get into the nitty-gritty of defining the interface. Here’s where you'll lay out what methods and properties should be available, without getting into the weeds of how they actually function behind the scenes.

    public interface CommonInterface {
        void start();
        void stop();
        void reset();
    }
  3. Implement the Interface:
    At this point, you can get various classes to implement this interface. Each class can handle the interface's requirements in its own unique way, giving you plenty of flexibility.

    public class FirstImplementation implements CommonInterface {
        @Override
        public void start() {
            // Implementation for the first class
        }
    
        @Override
        public void stop() {
            // Implementation for the first class
        }
    
        @Override
        public void reset() {
            // Implementation for the first class
        }
    }
    
    public class SecondImplementation implements CommonInterface {
        @Override
        public void start() {
            // Different implementation for the second class
        }
    
        @Override
        public void stop() {
            // Different implementation for the second class
        }
    
        @Override
        public void reset() {
            // Different implementation for the second class
        }
    }
  4. Use the Interface:
    Whenever you need to use these methods, just call them through the interface type. This approach lets you focus on what you need to do without getting bogged down in the details of which specific implementation you're dealing with.

    public void operate(CommonInterface instance) {
        instance.start();
        // Other operations
    }

When you create a common interface, you're really laying down a solid and efficient groundwork for different components to collaborate. This keeps everything modular and easy to maintain, which is pretty great for everyone involved! It's definitely a win-win!

  • Erc7683SettlerAdapter: Alright, let’s dive in and get those fill data sorted based on the order type! We also need to sort out the claim proofs on the destination and ensure that the filler payment is all set. First up, let's take a look at the AcrossOriginSettler addresses:

    • Base: 0x4afb570AC68BfFc26Bb02FdA3D801728B0f93C9E
    • Arbitrum: 0xB0B07055F214Ce59ccB968663d3435B9f3294998

    Just a quick reminder--make sure to cross-check everything against the spec version and the production document before we go live. You can find it all here: erc7683.org

  • Rip7755Adapter: This is your go-to for creating and checking state proofs for those L2↔L2 calls. Just a heads up--make sure you version your ABI and proof verifiers along with your chains! If you want to dive deeper, check out more details at eco.com.
  • Venue adapters:

    • CowSettleAdapter: This one’s designed for handling batch auction settlement contracts.
    • UniswapXFillAdapter: We’re going to use this for putting together filler-specific transactions.
    • 1inchResolverAdapter: This is our go-to for tackling issues that pop up during Dutch auction windows. If you want to learn more, check it out at docs.cow.fi.

5) Submission, privacy, and MEV

  • Opt for private rails:
    • MEV-Share: This handy tool lets you send bundles while keeping certain validity conditions in mind. It also throws in some privacy hints to help minimize info leaks. Plus, it’s designed to share MEV rebates with users, which is pretty neat! You can check it out over on GitHub.
    • SUAVE: This one's a game changer for managing batch or cross-domain flows with an extra layer of privacy. There’s some fascinating research going on around OFA constructs and frequent batch auctions for matching intent. If you want to dive deeper, take a look at the details on Flashbots Writings.
  • Stick to public mempools with anti-sandwich protections only when you've got no other options.

6) Funding, inventory, and rebalancing

  • Make sure to monitor those per-chain capital accounts and don’t forget to set your target bands! It’s pretty smart to auto-rebalance using on-chain bridges or OIF modules as they start becoming available. Following the guidance from EF, let’s keep an eye on the rebalancing modules and the solver on the OIF roadmap. (blog.ethereum.org)
  • Keep an eye on the queueing risk on L2s because of the optimistic MEV pressure. When you're handling high-volatility intents, it's usually a good idea to go for “fast finality” paths. (arxiv.org)

7) Observability, SLOs, and ops

Measure:

  • We’re diving into the fill latency distribution (p50/p90), checking out win rates based on the venue, and comparing our realized prices with the quoted ones. Plus, we’re doing a bit of housekeeping on our taxonomy and keeping an eye on those cross-chain settlement times.
  • Also, don’t overlook the health checks for each adapter, putting in circuit breakers to tackle those annoying failure storms, and making sure we’ve got SLA pages ready for our enterprise clients.

Concrete example: a cross-chain stablecoin bridge via ERC‑7683

Scenario: A customer wants to move 10 USDC from Base to Arbitrum, ensuring they receive a guaranteed minimum output (minOut) and that the transfer completes within 5 minutes.

  1. Ingestion: First things first, grab an ERC‑7683 order that lets you transfer with the Permit2 witness. This way, the approval and order get linked to one signature, which is super handy. Next up, you'll want to map out the legs of the journey: from the starting point (Base) to the final stop (Arbitrum). For more info, check it out here.
  2. Constraints: Alright, let’s dive into validating the token allowlist. Don’t forget to check the deadline, the minimum output, the limits for each chain, and that policy that needs an ERC‑7683 settlement in place.
  3. Route Selection: When it comes to the origin custody, choose the AcrossOriginSettler on Base and settle down at the Arbitrum SpokePool. You can find more details here.
  4. Settlement: At this point, it’s time to give the AcrossOriginSettler a call using the ABI-encoded order data. The solver will take care of advancing the funds at the destination while grabbing a fee at the origin based on the settler’s guidelines.
  5. Submission: Alright, it’s time to send this off using MEV‑Share to keep things under wraps in the public mempool. Make sure you include a validity condition that links back to the destination receipt. If you need more info, take a peek at this GitHub link.

This is essentially the same as the “Intents in Action” guide you’ll find in the ERC‑7683 production docs. Start by taking a look at their addresses and encoding flow, then go ahead and incorporate your own risk and privacy policies. For more info, you can check it out here: (erc7683.org).


Concrete example: TWAP and conditional orders on CoW

For those bigger treasury-sized sells, take a look at CoW’s ComposableCoW. It’s a fantastic option for whipping up stateless, composable conditional orders:

  • Alright, let’s dive into a TWAP where you’re looking to sell 12M DAI for WETH split into 30 daily portions. Each chunk comes with a minimum output and a validity window. Every day, solvers will compete to settle these portions at the best prices. If you want to learn more, feel free to check it out here.
  • The sdk-composable Multiplexer is a handy tool for bundling conditions and managing Merkle proofs. It really simplifies the process of tweaking parameters, whether you want to pause bands or set up volatility guards. You can find more details about it here.
  • When you use CoW's batch auction, you're not just cutting down on MEV; you're also grabbing those internal crossing chances before you even touch AMM liquidity. Want to dive deeper? Check it out here.

Concrete example: gasless swap intent on UniswapX

  • You can start by getting a gasless order (just a quick sign-up!) where fillers handle the gas fees while going head-to-head with both private and public liquidity. UniswapX is all about those fast “1 block or less” fills on average. Your filler logic pulls in liquidity from different AMMs and MMs, sending any MEV back to you as a nice little price boost. Take a look here: (x.uniswap.org).
  • Now, it's time to roll out the UniswapX Service. This will help you process those signed orders and leverage the UniswapX Parameterization API to establish limits on your quotes and slippage assumptions in your solver. Get all the nitty-gritty details here: (github.com).

Concrete example: Dutch auction resolver on 1inch Fusion

  • Alright, let’s kick things off! First, you need to register as a resolver by meeting the UP staking criteria. After that, make sure to set up your backend so you can keep an eye on those 1inch relayer updates. The trick here is to fine-tune your algorithm so it knows the perfect moment to make a move during that downward price trend. And don’t forget to bundle your transactions to protect yourself against MEV. Since Fusion on Solana is all about those fully on-chain intents, remember to tweak your signer and fee models accordingly. If you need a deeper dive into this, take a look at this link: (help.1inch.io).

Implementation notes that save months

  • Get in on the ERC‑7683 action early for your cross-chain flows!

    • Why should you care? The shared order format is a game-changer--it helps build a bigger filler network and reduces fragmentation. Plus, production contracts are already live and kicking. Take a look here: (erc7683.org).
    • Pro tip: check out the Permit2 “witness” feature. It lets you bundle the user's token approval and order terms all at once. Just map the nonce and deadline from Permit2 into your 7683 order. You can find more info here: (erc7683.org).
  • Let’s get started with OIF’s reference solver as soon as it hits the scene.

    • We’re diving into a TypeScript/EVM-first framework that features pluggable settlement modules and rebalancers. This arrangement works perfectly with Across’s relayer, giving clients even more choices. (openintents.xyz)
  • If you're just starting out, it's usually best to stick with private submission rails.

    • MEV‑Share emphasizes validity conditions and privacy hints, while SUAVE is all about cross-domain OFA and private batching. Together, they really enhance realized prices and minimize leak risks. Take a look here: (github.com)
  • Design for “venue hot-swap”

    • Implement a feature flag for each market (think CoW, UniswapX, Fusion) and for every chain. Keeping those adapter boundaries lightweight and stateless is key; just make sure to store idempotency keys and receipts.
  • Set up a "proof box" for cross-chain verification

    • Don’t forget to version your ABI, storage proof verifiers, and chain metadata. If you're implementing RIP-7755 for L2↔L2 verification, consider separating proof generation. This approach allows for an easy switch to trustless verification when it becomes available. Check out more details here!
  • Imagine solver funding like a treasury program

    • Establish capital bands, credit lines, and failure budgets for each chain. It's a good idea to plan for rebalancing during those quiet gas periods, and don’t forget to stress test for optimistic MEV congestion patterns on L2s. (arxiv.org)

Security checklist (what auditors will ask)

  • We’re implementing canonical decoding and EIP‑712 domain separation for all signed payloads, plus we’ve got witness binding for Permit2 when working with ERC‑7683 orders. You can check it out at (erc7683.org).
  • Now, let’s talk about settlement invariants: if everything goes as planned, users will at least receive their minimum output. But don’t worry--if something goes sideways, no one will be able to tamper with token approvals, and we won’t have any stranded partial escrows.
  • When it comes to replay and ordering, we’re taking care of nonce management across different venues. We’re using unique intent IDs and designing settlement calls to be idempotent, so there’s no room for confusion.
  • For cross-chain settlement correctness, we’re laying out the assumptions of liveness and finality for each mechanism. We’re comparing the ERC‑7683 settler to the RIP‑7755 proof path and documenting how we tackle reorgs. Feel free to dive deeper at (eips.ethereum.org).
  • On the privacy side of things, we’re transparent about which data goes out to public mempools and what stays on private rails, plus we’ve got some leakage tests in the mix.
  • Finally, we’ve set up kill switches and rollbacks, with per-adapter circuit breakers and automatic revocation of Permit2 allowances if there’s a policy breach. You can find more details at (erc7683.org).

KPIs that correlate with business outcomes

  • Check out how the win rates stack up based on venue and asset class. We’re pitting retail order flow against institutional RFQs.
  • Dive into the realized price improvement after we factor in fees and gas, and see how it measures up against the public baseline.
  • We’ve got the scoop on the success rate for cross-chain settlements and the p95 time-to-funds for each mechanism.
  • Let’s chat about capital efficiency: we’re looking at the average idle funding per chain and breaking down the rebalancing cost in terms of basis points of flow.
  • And don’t forget about resilience! We’ve sorted the revert rates (think out of gas, slippage, and proof invalidation) and kept tabs on the mean time to recovery (MTTR) for various types of incidents.

Emerging practices for 2026 readiness

  • Standard-first design: We're all about creating our setup based on ERC‑7683 and OIF modules. This helps us avoid the hassle of custom bridges and makes it easier to share our filler networks with other protocols as they adopt the standard. The Ethereum Foundation's 2025 protocol update has some great milestones outlined for reference solvers and interoperability layers. You can dive into the details here.
  • Multi‑settlement optionality: It makes sense to have a couple of settlement backends for each flow--like using the ERC‑7683 settler alongside the RIP‑7755 verification. This gives us a way to dodge outages or those annoying high fees. If you want to dive deeper, check out more details here.
  • Vertical specialization: We’re implementing routing strategies tailored to various asset classes, including stablecoins, LSTs, and even those fun long-tail memecoins. Plus, we're using venue-aware guards because we've noticed that L2 dynamics can really differ across different ecosystems. If you're curious to learn more about this research, check it out here.
  • Client diversity: Mixing it up with our clients is key! We’re planning to use the OIF reference solver along with other client stacks, like the Across relayer family. This way, we can minimize the chances of correlated failures. If you want to dive deeper, check it out here.
  • Intent languages: We're really looking at domain-specific languages for intents--take a look at cool projects like Essential's declarative approach. It has the potential to enhance our solver reasoning and make switching between protocols a lot easier. You can get all the details here.

Minimal code scaffold (TypeScript)

A Tiny, Framework-Agnostic Structure to Kickstart a Solver Service

Creating a solver service can actually be pretty straightforward! With this lightweight, framework-agnostic setup, you’ll be up and running in no time. Here’s a simple guide to help you get started:

Getting Started

Step 1: Set Up Your Environment

Make sure you’ve got these installed:

  • Node.js (make sure it's version 14 or newer)
  • NPM (version 6 or above is what you need)

Step 2: Initialize Your Project

Go ahead and create a fresh directory for your project, then hop into it:

mkdir my-solver-service
cd my-solver-service

Next, let’s kick off a new Node.js project:

npm init -y

Step 3: Install Dependencies

To get our service up and running, we’ll need to install a couple of packages. You can do that by running:

npm install express body-parser

Step 4: Create a Basic Server

Next up, let's whip up a simple server to manage our requests. Go ahead and create a file called server.js, and then toss in this code:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const PORT = process.env.PORT || 3000;

app.use(bodyParser.json());

app.post('/solve', (req, res) => {
    const problem = req.body.problem;
    // Here, you'd put your solver logic
    res.json({ solution: `Solution to ${problem}` });
});

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Step 5: Run Your Service

Get your server rolling with:

node server.js

Now that your solver service is all set up and running, you can easily send a POST request to http://localhost:3000/solve. Just include a JSON body that looks something like this:

{
    "problem": "2 + 2"
}

And there you have it! You'll get a reply with the solution!

What's Next?

Go ahead and explore the code further to boost your solver’s skills! You might want to incorporate some actual solving logic or even throw in extra endpoints to tackle various types of problems.

Conclusion

In just a few easy steps, you can have a simple, framework-agnostic solver service up and running. So, keep tinkering and enjoy coding!

// types.ts
export type ChainId = number
export type Address = `0x${string}`

export type Leg = {
  origin: ChainId
  destination: ChainId
  sellToken: Address
  buyToken: Address
  sellAmount: bigint
  minBuyAmount: bigint
}

export type Intent = {
  id: string
  deadline: number
  legs: Leg[]
  permit2Witness?: string // EIP-712 witness payload binding approval + order
  preferences?: Record<string, unknown>
}

// adapters/erc7683.ts
export interface Erc7683Settler {
  canSettle(i: Intent): Promise<boolean>
  settle(i: Intent): Promise<`0x${string}`> // tx hash
}

// adapters/venues.ts
export interface Venue {
  name: string
  subscribe(handler: (i: Intent) => Promise<void>): Promise<void>
}

// engine.ts
export class SolverEngine {
  constructor(
    private venues: Venue[],
    private settler: Erc7683Settler,
  ) {}

  async start() {
    for (const v of this.venues) {
      await v.subscribe(async (i) => this.onIntent(i))
    }
  }

  private async onIntent(i: Intent) {
    if (!this.hardChecks(i)) return
    const route = await this.findRoute(i) // your pricing logic
    if (!route) return
    if (!(await this.settler.canSettle(i))) return
    const tx = await this.settler.settle(i)
    console.log(`settled ${i.id} via ${tx}`)
  }

  private hardChecks(i: Intent) { /* deadline, allowlists, sizes */ return true }
  private async findRoute(i: Intent) { /* AMM+RFQ routing */ return {} }
}

Bind this to:

  • A CoW venue adapter designed for batch auctions. You can dive into the details here.
  • A UniswapX venue adapter that handles order propagation and filling. If you're curious about the code, you can check it out on GitHub.
  • A 1inch venue adapter tailored just for Fusion auctions. There’s more information for you here.
  • An ERC‑7683 settler adapter getting started with AcrossOriginSettler. For all the nitty-gritty details, swing by erc7683.org.

Procurement and build strategy for decision‑makers

  • Phase 0 (2-4 weeks): Due diligence and PoC

    • To get started, we'll select two venues and explore the ERC‑7683 flow. We'll bring in some reference adapters and take a look at win rates and p95 latencies on two Layer 2s--like Base and Arbitrum. Just head over to (x.uniswap.org).
  • Phase 1 (6-10 weeks): Productionization

    • Alright, let’s kick things into high gear! We’re diving into MEV‑Share/SUAVE submission, and we’ll also tackle funding and rebalancing. On top of that, we’ll get those super useful SLO dashboards up and running and take a closer look at doing a limited-scope audit for the constraint engine and settlement adapters. Check it out here: (github.com)
  • Phase 2 (ongoing): Optimization

    • We’ve entered the optimization phase! It's time to spice things up by adding a third venue and a second cross-chain verification path. We’ll also be fine-tuning the per-asset routing profiles and locking in those SLAs for our enterprise flows.

Budget Lever

If you jump on board with ERC‑7683/OIF, you'll be able to share infrastructure, enhance your eligible order flow, and avoid those pesky custom integrations for every single protocol. Trust me, this method tends to give you a much better ROI than building solvers that only work for one venue. Take a look at this for more details: (erc7683.org)


Final take

Composable solvers are definitely the way to go if you're looking to upscale intent-based user experiences across various chains and venues without ending up with awkward, one-off setups. By sticking to the ERC-7683 standard, embracing OIF modules as they come out, and creating a lightweight layer of venue/settlement adapters on top of a strong constraint engine and private submission rails, you’ll be all set for production in just a few weeks instead of dragging it out for months. This strategy positions you perfectly to ride the next wave of intent order flow in Ethereum’s multichain future. Check out the details here: (eips.ethereum.org)


Sources and further reading

  • Take a look at the ERC‑7683 specs, FAQs, and the “in production” docs (like settler addresses) right here. Plus, The Block has got some coverage on it too! (erc7683.org)
  • The Open Intents Framework just got a sweet update to the EF protocol. If you’re curious, you can check out the official site along with some chatter from CoinDesk, The Block, and Blockworks. Grab all the details you need at (blog.ethereum.org).
  • Check out the venue docs and repositories for UniswapX (site, service, parameterization), 1inch Fusion (help + architecture), and CoW Protocol (solvers, composable orders). You can explore everything over at (x.uniswap.org).
  • If you’re diving into MEV submission rails, make sure to check out the MEV‑Share repo along with the insights from the SUAVE writings and those interesting forum threads. You can find it over on github.com.
  • And finally, if you’re looking for some insight on L2 cross‑chain verification, check out this awesome explainer on RIP‑7755 and what's behind it. You can dive deeper into the details at (eco.com).

7Block Labs is ready to help you tailor your architecture to fit your unique assets, risk tolerance, and the current market. We're in this together, creating the adapters and controls that will let you launch swiftly and securely.

Like what you're reading? Let's build together.

Get a free 30-minute consultation with our engineering team.

7BlockLabs

Full-stack blockchain product studio: DeFi, dApps, audits, integrations.

7Block Labs is a trading name of JAYANTH TECHNOLOGIES LIMITED.

Registered in England and Wales (Company No. 16589283).

Registered Office address: Office 13536, 182-184 High Street North, East Ham, London, E6 2JA.

© 2026 7BlockLabs. All rights reserved.