npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vishal-blocksone/testnet-portal

v0.2.0

Published

Solidity contracts for receiving XTalk cross-chain messages, verified by L1X validators

Readme

@vishal-blocksone/testnet-portal

Testnet preview. Published from a personal account while XTalk is on testnet. It will move to an official L1X package before mainnet — expect the name to change.

Solidity contracts for sending and receiving XTalk cross-chain messages.

A message sent on one chain is checked by L1X validators and delivered to your contract on another chain. You write one function to receive and one call to send. Signatures, quorum, replay protection and sender verification are all handled for you.

npm install @vishal-blocksone/testnet-portal

There is no registration step and no stake. Every application on a chain shares one XTalk endpoint that L1X deploys and registers, so sending is just a contract call.


Your contract

import {XTalkSender} from "@vishal-blocksone/testnet-portal/contracts/XTalkSender.sol";
import {XTalkReceiver} from "@vishal-blocksone/testnet-portal/contracts/XTalkReceiver.sol";

contract MyApp is XTalkSender, XTalkReceiver {
    constructor(address sourceEndpoint, address endpoint, uint256 peerChainId, address peer)
        XTalkSender(sourceEndpoint)
        XTalkReceiver(endpoint, peerChainId, peer) {}

    function send(uint256 destChainId, address to, uint256 amount) external {
        _xtalkSend(destChainId, trustedSender, abi.encode(to, amount));
    }

    function _xtalkReceive(bytes32 messageId, bytes calldata payload) internal override {
        (address to, uint256 amount) = abi.decode(payload, (address, uint256));
        // your logic
    }
}

That's the whole integration. Inherit only XTalkReceiver if you just want to receive.

Constructor

| | | |---|---| | sourceEndpoint | the XTalk source endpoint on this chain — you send through it | | endpoint | the XTalk Endpoint on this chain — it delivers to you | | peerChainId | the chain your counterpart is on, e.g. 97 for BSC testnet | | peer | your application contract on that chain |

You cannot deploy without naming your peer. That is deliberate — see below.


What you get

| Check | Handled by | |---|---| | Signatures are valid, enough validators signed, message is not a repeat | the Endpoint | | The message came through an L1X-registered source endpoint | the Endpoint | | Only the Endpoint can call your contract | XTalkReceiver | | Only your peer can trigger your contract | XTalkReceiver |

You cannot switch these off. onXTalkReceive is not virtual, and the constructor rejects a zero peer address.

Why naming your peer matters

The validators prove the message really came through XTalk. They do not prove it came from someone you trust.

Every application on a chain sends through the same shared endpoint, so "it arrived via XTalk" says nothing on its own about who sent it — at the infrastructure level, every application's messages look alike. sender is the calling application. If your app did not name its peer, any other application on that chain could send a message pointing at yours — with genuine validator signatures on it — and your app would act on it.

This is the same idea as LayerZero's OApp peer check: the endpoint owns the pathway, the application owns the peer.


Contracts

| File | | |---|---| | contracts/XTalkSender.sol | inherit this to send | | contracts/XTalkReceiver.sol | inherit this to receive | | contracts/IXTalkReceiver.sol | the interface, if you need it directly | | contracts/XTalkSourceEndpoint.sol | the source endpoint. L1X deploys this — you don't. Included for reference and so the tests run | | contracts/XTalkOutboundVerifier.sol | the Endpoint. L1X deploys this — you don't. Same | | contracts/examples/ExampleXTalkToken.sol | a complete working app, both directions, in ~50 lines |


The message

What validators sign:

keccak256(abi.encode(
    uint256 srcChainId,    // which chain it came from
    address srcContract,   // the source endpoint it came through — the pathway
    address sender,        // WHICH APP sent it — this is what your peer check pins
    uint256 destChainId,
    bytes32 messageId,
    address destAddress,   // WHERE it goes — the relayer cannot redirect it
    bytes   payload        // WHAT it says — the relayer cannot change it
))

srcContract and sender are different things and both are bound. srcContract is shared infrastructure — the Endpoint checks it against the source endpoint L1X registered for that chain, and refuses any chain that has none. sender is your counterpart application, and it is what XTalkReceiver pins. Binding only the pathway would let every app on the chain speak for every other.

Signatures are raw recoverable secp256k1 (65 bytes r‖s‖v, v = 27/28) with no eth-personal prefix. Quorum is 60% of validator stake.

lib/message.js builds the same hash in JavaScript, for relayers and tests.


Sending

Call _xtalkSend(destChainId, destAddress, payload) from XTalkSender. It forwards to the shared source endpoint, which assigns the message id and stamps your contract as sender.

You do not emit MessageSent yourself, and you do not register anything. Applications used to do both; the endpoint now does it, which is what removed the per-application registration, the stake, and the ability of one app to send in another's name.

Ask L1X for the source endpoint address on your chain.


Tests

npm install
npm test

24 tests. Covers: delivery from the correct peer, deployment refused without a pinned peer, a genuinely-signed message from a different application being rejected, messages that did not come through the registered source endpoint, a chain with no registered endpoint, wrong source chain, direct calls that bypass the Endpoint, replay, source-endpoint id assignment, and validator set rotation including epoch expiry.


Current limits

  • This moves messages, not value. Nothing is locked on the source chain.
  • Check the payload yourself. XTalk proves a message is authentic, not that it is sensible.
  • You cannot run your own relayer yet. One L1X-operated relayer serves everyone.
  • No fees yet. L1X currently absorbs the cost of every message.

License

MIT