Skip to main content

What Is Synchronous Composability?

Synchronous composability enables your appchain to interact with settlement layer contracts atomically and synchronously—as if they were deployed on the same chain. Cross-chain calls execute in the same transaction, succeed or fail together, and require no waiting for confirmations or async handling. Pylon enables synchronous composability between your appchain and its settlement layer through coordinated sequencing and shared finality, making cross-chain operations appear seamless to developers.

Why Synchronous Matters

Traditional cross-chain solutions use asynchronous messaging, which introduces significant limitations:

Capital Efficiency

  • Partial execution risk - Transactions can succeed on one chain and fail on another, forcing complex rollback logic
  • Idle capital - Assets sit in bridges or escrow contracts waiting for confirmations, reducing usable liquidity
  • Higher costs - Users pay risk premia because every cross-chain action carries failure uncertainty

Developer Experience

  • Custom messaging code - Engineers must build or integrate bespoke bridge handlers, callbacks, and retry flows
  • Out-of-band coordination - Apps track state across domains manually, increasing complexity and bug surface area
  • Tooling gaps - Standard Ethereum debuggers and tracing tools do not account for asynchronous workflows

Backwards Compatibility

  • Contract rewrites - Many DeFi protocols require wrappers or modified interfaces to work across bridges
  • Integration risk - Each new async pattern multiplies failure cases and security assumptions
  • User friction - dApps often expose manual “claim” steps, long waits, or complex fallback UX
Synchronous composability removes these constraints by making settlement layer calls feel like local contract interactions—no idle capital, no custom retry logic, no protocol rewrites.

Seamless Developer Experience

Pylon operates the settlement layer node and your appchain’s sequencing pipeline, so cross-chain reads feel like standard single-chain function calls. Forwarding proxies and the SettlementPort contract hide the coordination work:
  • Write once - Your contracts call native interfaces (IERC20, IUniswapV2Pair, etc.)
  • No polling or retries - Pylon pre-populates data with priming transactions before your code executes
  • Production-ready tooling - The same Foundry, Hardhat, ethers.js, and web3.js workflows “just work,” including tracing and debugging tools

How Pylon Implements It

Pylon achieves synchronous composability through two key mechanisms:

Coordinated Sequencing

Pylon runs a coordinator that sequences blocks for your appchain in coordination with the settlement layer. This enables:
  • Cross-chain call detection - The coordinator monitors your appchain for calls to settlement layer contracts
  • Request forwarding - Calls are forwarded to the settlement layer during block building
  • Result injection - Results are injected back into your appchain via priming transactions

How It Works

When your app makes a cross-chain call:
  1. Call Detection - Your contract calls a forwarding proxy or SettlementPort contract
  2. Coordinator Intercepts - The Pylon coordinator detects the cross-chain call
  3. Forwarding - The call is forwarded to the settlement layer during block building
  4. Priming - Results are injected back via a priming transaction that executes before your transaction
  5. Execution - Your transaction completes with the settlement layer data already available
This makes cross-chain calls synchronous—your contract executes as if the settlement layer contract was on the same chain.

Current Capabilities

✅ Synchronous Reads

  • Query settlement state - Read any contract state from the settlement layer
  • View functions - Call any view function on settlement layer contracts
  • Real-time data - Access up-to-date settlement layer state in your transactions

✅ Atomic Operations

  • Success or fail together - Cross-chain calls are atomic
  • No partial execution - Either everything succeeds or everything reverts
  • Consistent state - No risk of inconsistent cross-chain state

✅ Developer-Friendly

  • Standard interfaces - Use forwarding proxies to call settlement contracts with standard interfaces
  • Familiar patterns - Write code like you would for single-chain interactions
  • Full tooling support - Works with Foundry, Hardhat, and all standard tools

In the Works

🔄 Synchronous Writes

  • Modify settlement state - Write to settlement layer contracts from your appchain
  • Two-way composability - Settlement layer contracts can call your appchain
  • Full atomicity - Multi-chain transactions that succeed or fail together

🔄 Multi-Appchain Coordination

  • Appchain-to-appchain - Direct synchronous composability between appchains
  • Sync zones - Groups of appchains that can interact atomically
  • Universal composability - Treat multiple appchains as one unified system

Practical Example

Here’s how you’d read from a settlement layer contract:
// Deploy a forwarding proxy for the settlement layer contract
address proxy = SettlementForwardingProxyFactory.createProxy(
    settlementLayerContractAddress
);

// Call it like any other contract - it's synchronous!
(uint256 balance, uint256 price) = IUniswapV2Pair(proxy).getReserves();
The forwarding proxy handles the cross-chain coordination behind the scenes. To your contract, it’s just a regular contract call.

Direct Settlement Reads API

Apps can also call the SettlementPort directly using the l2Read helper exposed on every Pylon chain. This function accepts a target settlement address plus encoded calldata, then returns the raw bytes from the settlement layer.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

interface IL2Reader {
    function l2Read(address target, bytes calldata data) external view returns (bytes memory);
}

function getL2Balance(address settlementPort, address token, address account) external view returns (uint256) {
    bytes memory data = abi.encodeWithSignature("balanceOf(address)", account);
    bytes memory result = IL2Reader(settlementPort).l2Read(token, data);
    return abi.decode(result, (uint256));
}
This pattern keeps view calls synchronous while giving you full control over calldata encoding, error handling, and fallbacks.

Learn More