Alice has ETH and wants XMR, Bob has XMR and wants ETH. They come to an agreement to do the swap and the amounts they will swap.
- Alice and Bob each generate Monero secret keys (which consist of secret spend and view keys): (
s_a
,v_a
) and (s_b
,v_b
), which are used to construct valid points on the ed25519 curve (ie. public keys):P_a
andP_b
accordingly. Alice sends Bob her public key and Bob sends Alice his public spend key and private view key. Note: The XMR will be locked in the account with address corresponding to the public keyP_a + P_b
. Bob needs to send his private view key so Alice can check that Bob actually locked the amount of XMR he claims he will.
Alice deploys a smart contract on Ethereum and locks her ETH in it. The contract has the following properties:
-
it is non-destructible
-
it contains two timestamps,
t_0
andt_1
, before and after which different actions are authorized. -
it is constructed containing
P_a
andP_b
, so that if Alice or Bob reveals their secret by calling the contract, the contract will verify that the secret corresponds to the expected public key that it was initialized with. -
it has a
Ready()
function which can only be called by Alice. OnceReady()
is invoked, Bob can proceed with redeeming his ether. Alice has until thet_0
timestamp to callReady()
- oncet_0
passes, then the contract automatically allows Bob to claim his ether, up until some second timestampt_1
. -
it has a
Claim()
function which can only be called by Bob afterReady()
is called ort_0
passes, up until the timestampt_1
. Aftert_1
, Bob can no longer claim the ETH. -
Claim()
takes one parameter from Bob:s_b
. OnceClaim()
is called, the ETH is transferred to Bob, and simultaneously Bob reveals his secret and thus Alice can claim her XMR by combining her and Bob's secrets. -
it has a
Refund()
function that can only be called by Alice and only beforeReady()
is called ort_0
is reached. OnceReady()
is invoked, Alice can no longer callRefund()
until the next timestampt_1
. If Bob doesn't claim his ether byt_1
, thenRefund()
can be called by Alice once again. -
Refund()
takes one parameter from Alice:s_a
. This allows Alice to get her ETH back in case Bob goes offline, but it simultaneously reveals her secret, allowing Bob to regain access to the XMR he locked.
Bob sees the smart contract has been deployed with the correct parameters. He sends his XMR to an account address constructed from P_a + P_b
. Thus, the funds can only be accessed by an entity having both s_a
and s_b
, as the secret spend key to that account is s_a + s_b
. The funds are viewable by someone having v_a + v_b
.
Note: Refund()
and Claim()
cannot be called at the same time. This is to prevent the case of front-running where, for example, Bob tries to claim, so his secret s_b
is in the mempool, and then Alice tries to call Refund()
with a higher priority while also transferring the XMR in the account controlled by s_a + s_b
. If her call goes through before Bob's and Bob doesn't notice this happening in time, then Alice will now have both the ETH and the XMR. Due to this case, Alice and Bob should not call Refund()
or Claim()
when they are approaching t_0
or t_1
respectively, as their transaction may not go through in time.
Alice sees that the XMR has been locked, and the amount is correct (as she knows v_a
and Bob send her v_b
in the first key exchange step). She calls Ready()
on the smart contract if the XMR has been locked. If the amount of XMR locked is incorrect, Alice calls Refund()
to abort the swap and reclaim her ETH.
From this point on, Bob can redeem his ether by calling Claim(s_b)
, which transfers the ETH to him.
By redeeming, Bob reveals his secret. Now Alice is the only one that has both s_a
and s_b
and she can access the monero in the account created from P_a + P_b
.
-
Alice locked her ETH, but Bob doesn't lock his XMR. Alice has until time
t_0
to callRefund()
to reclaim her ETH, which she should do ift_0
is soon. -
Alice called
Ready()
, but Bob never redeems. Deadlocks are prevented thanks to a second timelockt_1
, which re-enables Alice to call refund after it, while disabling Bob's ability to claim. -
Alice never calls
ready
withint_0
. Bob can still claim his ETH by waiting until aftert_0
has passed, as the contract automatically allows him to callClaim()
.
This protocol was inspired by the previous atomic swap research and work done by COMIT Network and the Farcaster Project.
Thanks for @mmagician, @alxs, and @Lederstrumpf for helping to review the protocol and kick-start the development of this project.