Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save meteoncu/4d7da443c3ab6749378b0037d4a9046e to your computer and use it in GitHub Desktop.

Select an option

Save meteoncu/4d7da443c3ab6749378b0037d4a9046e to your computer and use it in GitHub Desktop.
Fully On-Chain Scheduler via Native Messaging on Avalanche, Polkadot & Cosmos: The Cross-Subnet Ping-Pong Pattern

Fully On-Chain Scheduler via Native Messaging on Avalanche, Polkadot & Cosmos: The Cross-Subnet Ping-Pong Pattern

Introduction

One of the biggest hurdles in decentralized application development is creating schedulers that can initiate on-chain actions without relying on off-chain keeper services. Keepers introduce centralization risks and can suspend operation unexpectedly, halting any time-based logic.

By leveraging native cross-chain messaging—whether Avalanche Warp Messaging (AWM), Polkadot's XCM, or Cosmos IBC—we can build a fully on-chain, self-sustaining scheduler that requires zero external dependencies. This "Ping-Pong" mechanism has three core components:

  • Asynchronous messages sent natively between chains or subnets
  • Local task execution when each message arrives
  • Automated forwarding to keep the loop alive

Below we describe the pattern, show example code for Avalanche, Polkadot and Cosmos, explain why the loop must run continuously (no on-chain "delay" or "sleep"), and compare scheduling costs for a one-hour period on each network.

The Cross-Chain Ping-Pong Pattern

Core Loop

  1. Seed: when the first task is scheduled, Chain A sends a "check_tasks" message to Chain B.
  2. Process: Chain B inspects and executes due tasks, then—if tasks remain—sends a "check_tasks" message back to Chain A.
  3. Repeat: each incoming message triggers local execution and a follow-up ping. The two chains ping-pong continuously until all tasks are done.
  4. Continuous Execution: because on-chain code cannot "sleep," the scheduler runs as fast as each cross-chain transaction finalizes.

Key Benefits

  • Fully decentralized—no off-chain keepers or relayers, everything runs in consensus.
  • Trustless—execution enforced by the blockchain itself.
  • Predictable costs—native messaging fees are transparent and generally lower than third-party keeper subscriptions.

Implementation Examples

Avalanche Warp Messaging (AWM)

contract SchedulerA {
    struct Task {
        address target;
        bytes data;
        uint256 executeAt;
        bool done;
    }

    mapping(uint256 => Task) public tasks;
    uint256 public taskCount;
    uint256 public lastPing;
    uint256 constant MIN_INTERVAL = 30;

    function receiveWarp(bytes memory) external {
        require(block.timestamp >= lastPing + MIN_INTERVAL, "Too frequent");
        lastPing = block.timestamp;
        _processTasks();
        if (_hasPending()) {
            sendWarpMessage(subnetB, "check_tasks");
        }
    }

    function _processTasks() internal {
        for (uint256 i = 0; i < taskCount; i++) {
            if (!tasks[i].done && block.timestamp >= tasks[i].executeAt) {
                (bool success,) = tasks[i].target.call(tasks[i].data);
                tasks[i].done = success;
            }
        }
    }

    function _hasPending() internal view returns (bool) {
        for (uint256 i = 0; i < taskCount; i++) {
            if (!tasks[i].done) return true;
        }
        return false;
    }
}

Polkadot XCM

// Pseudocode for a Substrate pallet
fn on_xcm(origin: OriginFor<T>, msg: Xcm<()>) -> DispatchResult {
    Scheduler::<T>::process_tasks()?;
    if Scheduler::<T>::has_pending() {
        XcmPallet::send_xcm(
            Destination::X1(Parachain(PARACHAIN_B_ID.into())),
            Xcm(vec![Transact {
                origin_type: OriginKind::Native,
                require_weight_at_most: 1_000_000_000,
                call: ("check_tasks", ()).encode().into(),
            }]),
        )?;
    }
    Ok(())
}

Cosmos IBC

// Pseudocode for a CosmWasm contract
pub fn ibc_packet_receive(
    deps: DepsMut,
    _env: Env,
    _msg: IbcPacket
) -> IbcReceiveResponse {
    Scheduler::process_tasks(deps.storage, &_env.block)?;
    if Scheduler::has_pending(deps.storage) {
        return IbcReceiveResponse::new()
            .add_packet(IbcPacket::new(
                to_chain_b_port(),
                b"check_tasks".to_vec(),
                // timeouts etc.
            ));
    }
    IbcReceiveResponse::default()
}

Advanced Patterns

Multi-Chain Ring

Extend beyond two networks into a ring topology: Chain A → Chain B → Chain C → Chain A. Each chain forwards to the next, boosting availability and distributing load. New chains can join by inserting themselves into the ring.

Gas & Fee Considerations

Because the scheduler loop cannot pause on-chain, it executes one ping per cross-chain transaction. Each ping costs the native messaging fee of the source chain. The effective "interval" between pings equals that chain's block finalization time.

1-Hour Cost Comparison

Avalanche

  • Block finalization time: 2 seconds
  • Cost per ping: 0.001 AVAX
  • Pings per hour: 3600 s / 2 s = 1800
  • Total hourly cost: 1800 × 0.001 = 1.8 AVAX (≈ $36 at $20/AVAX)

Polkadot

  • Block time: 6 seconds
  • Average XCM ping fee: ~0.01 DOT
  • Pings per hour: 3600 / 6 = 600
  • Total hourly cost: 600 × 0.01 = 6 DOT (≈ $24 at $4/DOT)

Cosmos

  • Block time: 5 seconds
  • Cost per ping: 0.005 ATOM
  • Pings per hour: 3600 / 5 = 720
  • Total hourly cost: 720 × 0.005 = 3.6 ATOM (≈ $14.40 at $4/ATOM)

Use Cases

  • Real-Time Gaming Events: event triggers every few seconds or minutes
  • Short-Lived Auctions: NFT bids over 15–30 minutes
  • Micro-Loan Expiry: flash loans with sub-hour deadlines

Conclusion

By abstracting scheduling into a native cross-chain messaging pattern, we eliminate all off-chain dependencies, achieve trustless execution, and integrate seamlessly across multiple chains. Whether on Avalanche with AWM, Polkadot via XCM or any Cosmos SDK chain over IBC, the Ping-Pong scheduler provides truly decentralized, continuous automation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment