Noncustodial Xchain Transfer Protocol.
Nxtp is a lightweight protocol for generalized xchain transactions that retain the security properties of the underlying chain (i.e. it does not rely on any external validator set).
The protocol is made up of a simple contract that uses a locking pattern to prepare
and fulfill
transactions, a network of offchain routers that participate in pricing auctions and pass calldata between chains, and a user-side sdk that finds routes and prompts onchain transctions.
Transactions go through three phases:
- Route Auction: User broadcasts to our network signalling their desired route. Routers respond with sealed bids containing commitments to fulfilling the transaction within a certain time and price range.
- Prepare: User submits a transaction to
TransactionManager
contract on sender-side chain containing router's signed bid along with their funds. Upon detecting an event containing their signed bid from the chain, router submits the same transaction toTransactionManager
on the receiver-side chain. - Fulfill: Upon detecting an event from the prepare step on the receiver-side chain, user signs a message and sends it to the router. Router submits that message to the
TransactionManager
alongside the user's calldata to complete their transaction on receiver-side chain. Router then submits the same signed message and completes transaction on sender-side.
If a transaction is not fulfilled within a fixed timeout, it reverts and can be reclaimed by the party that called prepare
on each chain (initiator). Additionally, transactions can be cancelled unilaterally by the counterparty (responder).
Benefits:
- Nxtp only has onchain data. No offchain state or db at all. This means:
- Impossible for data to get out of sync
- TxService can be way simpler bc double collateralizations are impossible.
- No more iframe/browser state data.
- Out of the box perfect crash tolerance.
- We can use a subgraph out of the box for all of our data reading needs.
- Router can be fully stateless.
- When the receiving side funds are unlocked, sender side is immediately able to be unlocked. This means it is not possible for liquidity to leak.
- All of our current functionality around crosschain transfers is preserved. Auctions and AMMs will work very easily on this.
- The protocol is stupidly simple (enough that we can reasonably build and test it in 2-3 weeks).
Drawbacks/Risks:
- Nxtp is only a protocol for (generalized) xchain transactions. It does not use channels (i.e. does not utilize offchain state). This means it cannot be used to do batched conditional transfers for the purposes of scalable micropayments.
- While there is great crash tolerance, there is a strong requirement that the router must reclaim its funds within a certain time window (we can set this how we like... presumably 48-96 hours). Note that the pessimistic channel case actually has this same liveness requirement, but it exists on both the user and router.
This monorepo contains the following pieces:
- Contracts - hold funds for all network participants, and lock/unlock based on data submitted by users and routers
- Subgraph - enables scalable querying/responding by caching onchain data and events.
- TxService - resiliently attempts to send transactions to chain (with retries, etc.)
- Messaging - prepares, sends, and listens for message data over nats
- Router - listens for events from messaging service and subgraph, and then dispatches transactions to txService
- SDK - creates auctions, listens for events and creates transactions on the user side.
These are important and everyone must adhere to them:
-
Keep it simple, stupid.
-
Follow the Unix philosophy for every file and function. For instance, a
listeners.ts
file should only handle setting up listeners and then route to a correspondinghandler
. This keeps all business logic consolidated, making it easy to test and read. -
Every file and function should be unit tested. The scope of this codebase is very small, so it shouldn't be difficult to do this.
-
Build for future hires and contributors. Every function should have a top-level comment that describes what it does, and internal comments breaking down each step. Files should have comments delineating their reponsibilities. Remember: Good code is never surprising.
We are using a Yarn 2 Workspace-based monorepo structure. The individual workspaces are within the packages/
directory. This repo structure was heavily inspired by create-ts-project. The goal is to minimize 3rd party dependencies, and expose the configurations so that they can be transparently managed.
There are a few top-level scripts that can be used:
lint:all
- Lints all packages.test:all
- Tests all packages.clean:all
- Removes build artifacts.build:all
- Builds all packages.verify:all
- Tests, builds, and lints all packages.version:all
- Sets versions for packages.purge:all
- Cleans and removes node_modules, etc.
Individual commands can be run against workspaces as so (example for nxtp-utils
package):
yarn workspace @connext/nxtp-utils test
You should be able to do everything from the root and not need to go into the individual package dirs. For example, adding an npm package:
yarn workspace @connext/nxtp-txservice add ethers
Make sure you are on the latest yarn version:
yarn set version berry
Try running yarn
to update everything. If you have issues, try deleting node_modules
and yarn.lock
. After deleting yarn.lock
run touch yarn.lock
since it does not like if there is no lock file.
yarn
: Install deps, create symlinks, hoist packages.yarn build:all
: Build all packages.
Run router:
yarn workspace @connext/nxtp-router dev
- Runs router in hot-reload mode.
Run test-ui:
yarn workspace @connext/nxtp-test-ui dev
- Runs test-ui in hot-reload mode.
yarn
: Install deps, create symlinks, hoist packages.yarn build:all
: Build all packages. oryarn workspace @connext/nxtp-contracts build
: Build the specific package.
Run test:
yarn workspace @connext/nxtp-contracts test
- Runs test.
To add a new package that can be shared by the rest of the repo, you can use some convenience scripts that we have installed:
yarn tsp create @connext/test-lib --template node-lib
Note: The tsp
tool is not required, it just makes boilerplate generation easier. If you want, you can copy paste stuff from other packages. Documentation on the tool is here.
To add the lib to be a dependency of a consuming app (i.e. the router):
yarn tsp add @connext/test-lib --cwd packages/router
Again, this can all be done without the tool, all it does is add some files and make some config changes.
Note: We use node-lib
as the template for all the packages. There are some other included templates like browser-lib
which didn't work with our bundling. We might need to revisit things for bundling reqs.