P2P Atomic Swaps in Syrius

I’d like to share a UI design proposal for how atomic swaps could work in Syrius.

View design in Figma

This UI is designed to work with the HTLC implementation @georgezgeorgez and other community members are working on enabling users to do trustless P2P atomic swaps in Syrius with ease.

The Figma demonstrates two happy case flows. One where Alice and Bob swap ZNN <> QSR and one where they swap ZNN <> BTC.

Potential issues for users and other considerations

Fake ZTS tokens → the user must check that the token ID is correct (excluding ZNN and QSR which are reserved symbols).

With atomic swaps it’s the user’s responsibility to make sure that the counter deposit expires before the initial deposit. In ZTS <> ZTS swaps this can be handled automatically by Syrius but when doing cross-chain swaps the user must be careful with this. Showing a clear message informing the user about this would help mitigate the problem.

If the user does not keep Syrius open after the counterparty has unlocked the counter deposit and the initial deposit that was locked for the user expires, the user will lose access to the deposit. A power outage or hardware failure could cause trouble. This issue could be mitigated by having Syrius force relatively long expiration durations for deposits.

Community made YouTube tutorials on how to perform swaps (especially cross-chain) could be helpful.


Awesome work!
I’m currently testing HTLC functions in znn_sdk_dart and will implement your design in Syrius soon.


I want to make sure I understand this issue.

Bob swaps ZNN for QSR with Alice

  1. Bob locks ZNN in HTLC
  2. Alice locks QSR in HTLC
  3. Bob unlocks the QSR with the preimage
  4. ZNN is then unlocked for Alice automatically. If Alice does not claim the ZNN before the HTLC expires Alice will lose the ZNN.

Is #4 correct?


This is correct. The htlc Bob created holding the ZNN will expire. An expired htlc cannot be claimed by Alice.

What happens if Alice closes syrius when Bob publishes the preimage and Alice opens it some time later but still within the expiration time?

Will syrius do a lookup on the published preimage and unlock it automatically?

Looks fantastic btw, great job!


Agree, this is amazing work. Thank you very much.

I guess the second variation of this question is, what if syrius loses a connection to the internet, but then reestablishes before the HTLC expires. Will that produce the same result as losing power to syrius?


Yes this is good question as it’s a central part of the swap UX. If Syrius is only monitoring for newly published blocks then indeed Syrius may miss the preimage if it’s not open at the right time. I’ve noticed that sometimes when I have Syrius open for extended periods of time the embedded node will stop for whatever reason and I have to reboot Syrius. This would also cause trouble during an active swap.

So I’m hoping it would be feasible for Syrius to keep an index of the last account block it checked and then be able to go through the blocks to find the preimage if Syrius was closed when it was published. Would there be potential issues doing this?

Block explorers could also index the preimages and users could find them from there, but UX-wise that’s not ideal. Block explorers can also experience downtime or have incorrect information due to a bug for example.

Yep same problem. Syrius will miss the published account blocks if it’s only monitoring for newly published blocks.

Amazing UI/UX design vilkris. I can clearly see what’s going on even though my atomic swap expertise is nil.

I was wondering, does it make sense at all to add an optional fail-safe feature to the htlc implementation to allow peers to redirect funds to AZ in the event described above (failure to claim unlocked tokens) ? UX side could just be a checkbox. If all data is on-chain pillars could act as customer service and recover the funds :sweat_smile:

Decentralized recovery of funds might be another alien first.

Maybe we should implement both options - in syrius and the block explorers. Having redundancy seems prudent.

This looks great @vilkris - should there be an indicator for the time remaining on the swap contract (edit: sorry it’s already there I missed it, was looking at the rectangular buttons underneath)

And what options are there for expiry? Or is that already defined in the code?

For cross-chain swaps do we have to ensure the expiry on the Zenon side is less? To avoid someone executing at the last second without time for the counterparty to act

When the user initiates an atomic swap he will have to set an expiration time for the deposit. It’s not yet decided what values the user should be able to input for the expiration time. The current embedded contract implementation has no limits on the time but from the users perspective maybe some presets to select from would be enough in Syrius, e.g. 1h, 3h, 12h, 24h, 48h.

If the deposit expires, a “Reclaim” button will be shown to the user.

Yes the expiry time has to always be less for the counter deposit. In the current design this is completely the user’s resposibility when doing cross-chain swaps. There may be some solution to this problem but I’m not knowledgeable enough in that area.


Just thinking out loud, in what way can different block times effect the expiration times for cross-chain swaps?

If it is completely the user’s resposibility, selecting the minimum allowed time unit as the expiry time also automatically means the counterparty will never be able to make one with less expiry time.

For example: When Alice creates a htlc with an expiration time of 1 hour, Bob will not be able to create one with less expiry time, becuase the minimum allowed time is 1 hour.

Maybe it’s better to calculate the expiration time automaticlly when creating a htlc for the counterparty. You can use the expiration time of the counterparty to calculate a margin and remaining time. This does require the user to select the htlc of the counterparty in one way or another. You can also show a warning message when creating the htlc if the remaining time is within a certain thresshold.

1 Like

It’s completely the user’s responsibility only when we’re talking about cross-chain swaps. If Alice makes a BTC hashlock that expires in 30 minutes and Bob doesn’t notice that and proceeds to make a hashlock with Syrius that expires in 1 hour then he might be in trouble.

Unless we add ways for Syrius to now about other chains I don’t know how we can solve this problem for cross-chain swaps.

This is pretty much what the Figma design proposes for ZTS <> ZTS swaps. When making a counter deposit Syrius should calculate the expiration time automatically based on the initial deposit. I agree that there should be some thresholds here.

Making a counter deposit:

1 Like

Here’s an idea to avoid the unpleasant scenario where an atomic swap is unlocked by Alice but Bob remains offline until after the swap expires.

EDIT: George responded to this post on Telegram.
He convinced me that we shouldn’t pursue the idea.

I don’t see a way for go-zenon to update swap details, but I’m hoping it’s possible.

We could update go-zenon with the following logic:

if htlc.unlock[id] conditions pass:
   _hashlock = htlc[id].hashlock
   for _id in context.Storage():
      if htlc[_id].hashlock == _hashlock:
         htlc[_id].expirationtime = 0

I’m not sure if there is much downside to this change… I guess the for loop could be expensive if there are a lot of swaps.

This can be optimized by adding a function that returns an array of all id:hashlock pairs in one lookup so the for loop would take less time (many disk reads vs one disk read).

The change would

  • allow multi-party zts<->zts swaps to occur without great emphasis on expiration time coordination
  • reduce risk of atomic swap failure
  • mitigate fraud
Atomic Swap Attack

Alice entices Bob with an offer he can’t refuse.
Alice will lock in 100 zBTC for 2 hours and asks Bob to lock 100 ZNN for 1 hour in exchange. The swap is setup and both parties confirm the funds are locked as expected.
However, Alice knows Bob’s IP address and has the ability to knock Bob offline for a couple hours via DDOS.
She waits until Bob’s swap is nearly expired, starts her attack, and unlocks the swap.
Bob’s Syrius is offline and he potentially has no recourse.
Alice waits an hour, confirms her swap has expired, reclaims her funds, and stops the DDOS attack.

That’s just the attack scenario. There are other reasons why Bob would be unable to unlock Alice’s swap in time. Let’s try to minimize atomic swap failure for the ones within our control.

I’m also planning to integrate the htlc.inspect function in Syrius so users can lookup an existing htlc id.
If the id was unlocked, its preimage will be displayed.

I’m reading this as, when unlocking a htlc, all htlc’s with the same hashlock are expired immediately?

Does it still make sense to uphold the expiration time when the hashlock is published and made public available on chain? Would it be an idea to disable the expiration time once the hashlock has been published?

I see how that’s ambiguous. Yeah, I meant to say that it disables the expirationtime.
However, this for loop can become expensive to execute and we want to avoid looping over user-controlled data.

I’m still thinking of other ways to improve on this idea.

1 Like

I agree, the loops are stricky, but I like the idea of disabling the expirationtime.

Topic moved to #development:funding-staging