Connext contest details
- $22,500 USDC award pot
- $2,500 USDC gas optimization award pot
- Join C4 Discord to register
- Submit findings using the C4 form
- Read our guidelines for more details
- Starts 2021-07-09 00:00 UTC
- Ends 2021-07-11 23:59 UTC
|Contract Name||Lines of Code|
The main contract is the
TransactionManager.sol contract, which defines the logic behind the crosschain transfers. The contract implements the
ITransactionManager.sol interface, and can make an external calls to a contract that implements the
IFulfillHelper interface when the
fulfill funtion is called on the receiving chain. The
TransactionManager.sol uses the
LibAsset.sol to handle
LibERC20.sol to handle non-conforming token contracts (i.e. those which don't return a boolean). By convention an
assetId is either a token address or
address(0) when referring to a native asset.
If you are more of a video person, see here for a video walkthrough.
NXTP is designed to facilitate crosschain transactions via simple atomic swaps where one party provides the liquidity of
chainA and the other provides the liquidity of
There are two main offchain agents:
- User: The person initiating the crosschain transaction. Their ultimate desire is to move
chainB. They are willing to pay a fee denominated in
chainAto accomplish this, and may want to execute some contract call with
chainBwhen they transfer. The system does not make any assumptions about their liveness or their ability to maintain a data store.
- Router: The person providing liquidity to facilitate crosschain transactions. Routers earn fees on their available liquidity of
chainB(continuing the above example), and they are willing to accept a fee denominated in
chainA. There are no imposed storage requirements, though a router is assumed to be online through the duration of the transfer acceptance (via participation in an auction) to the provision of liquidity on the receiver chain.
When using NXTP to perform a crosschain swap, a user first locks liquidity of
chainA, waits for the router to lock
chainB, and finally is able to unlock the funds by providing a signature and submitting it to a contract that exists on
chainB. The router can use this same signature to unlock the funds the user locked on
chainA. If something goes wrong, or the payment expires, the transfer may also be cancelled, and the funds returned to their original owner.
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. This step allows the user to select which router will participate in the transaction.
- Prepare: Once the auction is completed, the transaction can be prepared. The user submits a transaction to
TransactionManagercontract on sender-side chain containing router's signed bid. This transaction locks up the users funds on the sending chain. Upon detecting an event containing their signed bid from the chain, router submits the same data to the
TransactionManageron the receiver-side chain, and locks up a corresponding amount of liquidity. The amount locked on the receiving chain is
sending amount - feeso the router is incentivized to complete the transaction (they pocket the difference).
- Fulfill: Upon detecting the
TransactionPreparedevent on the receiver-side chain, the user signs a message and sends it to a relayer, who will earn a fee for submission. The relayer (which may be the router) then submits the message to the
TransactionManagerto complete their transaction on receiver-side chain and claim the funds locked by the router. A relayer is used here to allow users to submit transactions with arbitrary calldata on the receiving chain without needing gas to do so. The router then submits the same signed message and completes transaction on sender-side, unlocking the original
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 person owed funds on that chain (router for sending chain, user for receiving chain) prior to expiry.
TransactionManageris our data store. Neither participant should require a store to complete crosschain transactions. All information to
canceltransactions should be retrievable through contract events. If a user goes offline and returns, they should be able to read the onchain data to determine which transactions require the actions, and the data needed to execute them.
TransactionManageris also how we pass messages most of the time -- the events are used as a mechanism for broadcasting data to the counterparty. This removes the need for the majority messaging overhead.
- The user should be able to use relayers for any actions that need to be taken on the receiving chain. It should not be assumed that they have gas on that chain.
expiryshould be decremented from the sending to the receiving chain. The
amountis decremented to allow the router to take some profits for facilitating the transaction upon unlocking the sender-chain transfer. The
expiryis decremented so the receiver-side is guaranteed to be completed (either cancellable or fulfilled) before the sender-side.
- The signature should be constant between the sending and receiving chains, to allow the router to automatically fulfill on the sending-side once it has been revealed.
- Router keeps their funds on the contract itself. This should slightly reduce costs, make analytics much easier, and will separate gas funds from operating funds (e.g. xDai side running out of gas bc all our $XDAI was drained).
The more detailed flow can be seen below:
There are three key functions in the contract:
Lets assume that by this point the user has already run the auction.
preparepassing in all of the relevant data about the transfer on the sender side chain along with their funds for the transfer. The contract stores the funds and the hash of the data in its state. This call emits a
TransactionPreparedevent with the same data used to create the transaction.
Router hears this event (which includes its address) and calls the
preparefunction with the same calldata on the receiving chain (with decremented
expiry). This call emits another
User hears the
TransactionPreparedevent on receiver chain, alerting them that the transaction is ready to be
fulfilledsince both parties have locked up funds.
User validates the data: a. If it is invalid, they can
cancelon the receiver-chain and wait for either the expiry to elapse and
cancelon the sending chain, or for the router to
cancelon the sending chain upon seeing the emittted
TransactionCancelledevent on the receiving chain. b. If the data is valid, they generate a signature that can be used to
fulfillthe transfer. The user can either call
fulfillon the receiver-chain themselves, or broadcast their signature to a relayer who will submit the receiver
fulfilltransaction for a fee.
The router, upon seeing the
TransactionFulfilledevent on receiver side, collects the signature from the event data and submits
fulfillto sender side. This claims the original
amountsent by the user to the
TransactionManagerwhen the transaction was
NOTE: In both sender and receiver cases,
fulfill must be called before the timeout expires. This acts as a failsafe against funds getting locked indefinitely if the counterparty is malicious. However, this also means expiry must be far enough away (w/ enough gap between both sides) to make sure the tx will go through.
TransactionManager contract and its associated events should contain sufficient information for both the user and the router to properly resume any active transfers if they have been offline. To accomplish this, the transactions all store the
preparedBlockNumber on them, and the contract tracks the
activeTransactionBlocks for each user in a
mapping(address => uint256). This mapping adds the
block.number each time a transaction is prepared, and removes the
preparedBlockNumber on completion (both
cancel). By looking at these blocks, users and routers should be able to easily find the relevant events and determine the necessary actions without needing a store of their own.