Monero-Ethereum atomic swaps

Alice has ether and wants Monero.

Bob has Monero and wants ether.

Ethereum smart contract interface

Offline phase:

  • Both parties Alice and Bob select a secret: s_a & s_b, which are used to construct valid points on the ed25519 curve: P_ed_a and P_ed_b accordingly. The parties share their public keys with each other.

Step 1.

Alice creates a smart contract on Ethereum and sends her ether to it. The contract has the following properties:

  • it is non-destructible
  • it is initiated with P_ed_a & P_ed_b
  • it has a ready function, which can only be called by Alice. Once ready is invoked (in step 3), Bob can proceed with redeeming his ether. Alice has t_0 time period to call ready, otherwise all funds are transferred to Bob.
  • it has a refund function that can only be called by Alice, and only before ready is called. Once ready is invoked, Alice can no longer call refund within the next time durationt_1. After t_1 has passed though, and Bob hasn't called redeem, refund is re-enabled to prevent a dead-lock.
  • refund takes one parameter from Alice: s_a. This allows Alice to get her ether back in case Bob mis-behaves, but as it's public from now on, Bob can still redeem his monero (in case he was offline).
  • redeem takes one parameter from Bob: s_b. At this point, Alice found out Bob's secret and can claim Monero by combining her and Bob's secrets.
  • both the refund & redeem functions check, whether the argument s_x provided indeed corresponds to the public key P_ed_x and only if true, allows fund transfer.

Step 2.

Bob sees the smart contract being created. He sends his monero to an account address constructed from P_ed_a + P_ed_b.

The funds can only be accessed by an entity having both s_a & s_b.

Step 3.

Alice sees that monero has been sent. She calls ready on the smart contract.
From this point on, Bob can redeem his ether by calling redeem(s_b).

By redeeming, Bob revealed his secret. Now Alice is the only one that has both s_a & s_b and she can access the monero in the account created from P_ed_a + P_ed_b.

What could go wrong

Step 2.

  • Alice locked her ether, but Bob doesn't lock his monero.
    Alice never calls ready, and can instead call refund(s_a) within t_0, getting her ether back.

Step 3.

  • Alice called ready, but Bob never redeems. Deadlocks are prevented thanks to a second timelock t_1, which re-enables Alice to call refund.

  • Alice never calls ready within t_1. Bob can still claim his ETH, after t_1 has passed, and XMR remains locked forever.

Flowchart

st=>start: Spend key shares generation
ethContract=>operation: Alice creates ETH funded contract
lockMonero=>condition: Bob locks XMR < t_0
isLockMonero=>condition: Alice: is XMR locked?
refund_bt0=>condition: Alice refund(s_a) < t_0
claimBad=>condition: Bob claim(s_b) < t_1
ready=>condition: Alice ready() < t_0
claimY=>condition: Bob claim(s_b) < t_1
claimN=>condition: Bob claim(s_b) < t_1
refund_at1=>condition: Alice refund(s_a) > t_1
xmrEndAlice=>end: Alice controls XMR, Bob ETH
xmrEndBob=>end: Bob controls XMR, Alice ETH
deadEndAlice=>end: Dead lock until Alice
badEndBob=>end: Bob gets XMR + ETH

st->ethContract->lockMonero
lockMonero(yes)->ready
lockMonero(no)->refund_bt0
refund_bt0(yes)->xmrEndBob
refund_bt0(no)->claimBad
claimBad(yes)->badEndBob
claimBad(no)->refund_at1
ready(yes)->claimY
ready(no)->claimN
claimY(yes)->xmrEndAlice
claimY(no)->refund_at1
claimN(yes)->xmrEndAlice
claimN(no)->refund_at1
refund_at1(yes)->xmrEndBob
refund_at1(no)->deadEndAlice
Select a repo