Gasless Relay Redeem Race Condition
Summary
When the maker redeems via the gasless relay, the on-chain redemption
is visible before the server receives the deferred relay notification.
The taker's FindRedemption detects the on-chain spend and attempts to
redeem + notify the server, but the server rejects because it thinks
the maker hasn't redeemed yet. The trade ultimately completes but with
errors and a match revocation.
Timeline (from taker's log)
08:51:41 - Taker broadcasts polygon swap
08:51:12 - Maker submits gasless relay redeem (from maker's log)
08:52:15 - Relay redeem confirmed on-chain, maker sends deferred notification to server
08:52:25 - Taker sees its polygon swap contract is spent on-chain
08:52:55 - Taker finds maker's redemption via FindRedemption, extracts secret
08:53:10 - Taker broadcasts DCR redeem
08:53:10 - ERROR: "expected other party to act" - server rejects taker's redeem notification
08:53:10 - Match revoked while redeem tx was being broadcast
08:53:10 - SettlementSequenceError starts match status resolution
08:53:19 - Server finally notifies taker of maker's redemption
08:53:19 - Taker re-broadcasts redeem from cache (fast-path)
08:53:19 - Match marked complete (but also revoked)
Root Cause
The gasless relay flow has a gap between on-chain visibility and server
awareness:
- Maker signs a relay redeem, relay submits it on-chain
- The transaction is mined and visible to the taker's RPC providers
- BUT the server doesn't know about the redeem until the relay confirms
and the maker sends the deferred redeem notification
- In this window, the taker's FindRedemption sees the spend, extracts
the secret, redeems its side, and tries to notify the server
- Server rejects with "expected other party to act" (error code 26)
because from its perspective, it's still the maker's turn
Impact
- Funds are safe - both sides redeem successfully
- Match gets revoked during the race, but redeems are already broadcast
- Error log spam and match status resolution attempts
- The "Redemption Resubmitted" warning on the maker side suggests the
relay notification path may also have hiccups
Possible Fixes
-
Server: accept taker redeem when maker hasn't reported yet - If
the taker provides a valid secret in its redeem, the server could
accept it and infer the maker redeemed, rather than rejecting with
"expected other party to act"
-
Client: delay FindRedemption reporting - If the taker finds the
redemption on-chain but the server hasn't notified them of the
maker's redeem, wait briefly for the server notification before
sending the redeem request
-
Relay: notify server directly - Instead of the maker client
sending a deferred notification after relay confirmation, have the
relay notify the server directly (would require relay-server
integration)
-
Server: don't revoke during relay window - Add tolerance for
the deferred notification delay in the match state machine
Observed On
- Mainnet, DCR-POLYGON market
- Match 0bcaf4a6fea9a44385047c0bb244486cae5e121759c9529155176d6f82925ebf
- v1.1.0-rc1 client and server, relay running on Polygon chain 137
Files
client/core/core.go - match tick processing, FindRedemption handling
client/asset/eth/eth.go - gaslessRedeem, relay status checking
server/swap/swap.go - match state machine, redeem validation
evmrelay/cmd/evmrelay/ - relay task lifecycle
Gasless Relay Redeem Race Condition
Summary
When the maker redeems via the gasless relay, the on-chain redemption
is visible before the server receives the deferred relay notification.
The taker's FindRedemption detects the on-chain spend and attempts to
redeem + notify the server, but the server rejects because it thinks
the maker hasn't redeemed yet. The trade ultimately completes but with
errors and a match revocation.
Timeline (from taker's log)
Root Cause
The gasless relay flow has a gap between on-chain visibility and server
awareness:
and the maker sends the deferred
redeemnotificationthe secret, redeems its side, and tries to notify the server
because from its perspective, it's still the maker's turn
Impact
relay notification path may also have hiccups
Possible Fixes
Server: accept taker redeem when maker hasn't reported yet - If
the taker provides a valid secret in its redeem, the server could
accept it and infer the maker redeemed, rather than rejecting with
"expected other party to act"
Client: delay FindRedemption reporting - If the taker finds the
redemption on-chain but the server hasn't notified them of the
maker's redeem, wait briefly for the server notification before
sending the redeem request
Relay: notify server directly - Instead of the maker client
sending a deferred notification after relay confirmation, have the
relay notify the server directly (would require relay-server
integration)
Server: don't revoke during relay window - Add tolerance for
the deferred notification delay in the match state machine
Observed On
Files
client/core/core.go- match tick processing, FindRedemption handlingclient/asset/eth/eth.go- gaslessRedeem, relay status checkingserver/swap/swap.go- match state machine, redeem validationevmrelay/cmd/evmrelay/- relay task lifecycle