Skip to content

Commit 2fd0efd

Browse files
authored
feat: lifi escrow message handler (#72)
* Implement compact digest generation * Add lifi api tests * Use create transaction hash for mayan swaps * Implement fetching the allocator from chain * Fix compact tests * Add order validation * Add order signing * Use compact address to verify signature * Add nonce and allocator on-chain verification * Move lifi api types to separate file * Generate lifi calldata * Fix compact types to match lifi * Lint * Add lifi tests * Add lifi compact to api * Lint * Change lifi compact to lifi escrow * Separate confirmation watcher into value and token * Implement borrow many unlock hash * Remove extra code * Fix protocol type * Add lifi escrow message handler initialization * Bump solver config * Add tests * Tidy dependcies * Lint * Fix mocks check * Regenerate mocks with version v0.5.0 * Tidy go.mod * Bump lifi solver
1 parent a3bf182 commit 2fd0efd

File tree

27 files changed

+1516
-105
lines changed

27 files changed

+1516
-105
lines changed

.github/workflows/mocks.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@
33

44
on: [pull_request]
55
name: Mocks check
6+
67
env:
78
GO111MODULE: on
89
GOPRIVATE: github.com/sprintertech
10+
TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
911

1012
jobs:
1113
mocks-check:
1214
runs-on: ubuntu-latest
1315
steps:
1416
- uses: actions/checkout@v2
1517

18+
- name: setup git access to private repos
19+
run: git config --global url."https://${TOKEN}:[email protected]/sprintertech/".insteadOf "https://github.com/sprintertech/"
20+
1621
- uses: actions/setup-go@v2
1722
with:
1823
go-version: "^1.23"

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ genmocks:
3737
mockgen -destination=./comm/p2p/mock/stream/stream.go github.com/libp2p/go-libp2p/core/network Stream,Conn
3838
mockgen -source=./chains/evm/message/across.go -destination=./chains/evm/message/mock/across.go
3939
mockgen -source=./chains/evm/message/rhinestone.go -destination=./chains/evm/message/mock/rhinestone.go
40+
mockgen -source=./chains/evm/message/lifiEscrow.go -destination=./chains/evm/message/mock/lifiEscrow.go
4041
mockgen -source=./chains/evm/message/mayan.go -destination=./chains/evm/message/mock/mayan.go
4142
mockgen -source=./chains/evm/message/confirmations.go -destination=./chains/evm/message/mock/confirmations.go
4243
mockgen -source=./api/handlers/signing.go -destination=./api/handlers/mock/signing.go
44+
mockgen -package mock_message -destination=./chains/evm/message/mock/pricing.go github.com/sprintertech/lifi-solver/pkg/pricing OrderPricer
4345

4446

4547

api/handlers/signing.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const (
2020
AcrossProtocol ProtocolType = "across"
2121
MayanProtocol ProtocolType = "mayan"
2222
RhinestoneProtocol ProtocolType = "rhinestone"
23+
LifiEscrow ProtocolType = "lifi-escrow"
2324
LifiProtocol ProtocolType = "lifi"
2425
)
2526

@@ -112,6 +113,19 @@ func (h *SigningHandler) HandleSigning(w http.ResponseWriter, r *http.Request) {
112113
BorrowAmount: b.BorrowAmount.Int,
113114
})
114115
}
116+
case LifiEscrow:
117+
{
118+
m = evmMessage.NewLifiEscrowData(0, b.ChainId, &evmMessage.LifiEscrowData{
119+
OrderID: b.DepositId,
120+
Nonce: b.Nonce.Int,
121+
LiquidityPool: common.HexToAddress(b.LiquidityPool),
122+
Caller: common.HexToAddress(b.Caller),
123+
ErrChn: errChn,
124+
Source: 0,
125+
Destination: b.ChainId,
126+
BorrowAmount: b.BorrowAmount.Int,
127+
})
128+
}
115129
default:
116130
JSONError(w, fmt.Errorf("invalid protocol %s", b.Protocol), http.StatusBadRequest)
117131
return

api/handlers/signing_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,40 @@ func (s *SigningHandlerTestSuite) Test_HandleSigning_RhinestoneSuccess() {
360360
s.Equal(http.StatusAccepted, recorder.Code)
361361
}
362362

363+
func (s *SigningHandlerTestSuite) Test_HandleSigning_LifiSuccess() {
364+
msgChn := make(chan []*message.Message)
365+
handler := handlers.NewSigningHandler(msgChn, s.chains)
366+
367+
input := handlers.SigningBody{
368+
DepositId: "depositID",
369+
Protocol: "lifi-escrow",
370+
LiquidityPool: "0xbe526bA5d1ad94cC59D7A79d99A59F607d31A657",
371+
Caller: "0xbe526bA5d1ad94cC59D7A79d99A59F607d31A657",
372+
Calldata: "0xbe5",
373+
Nonce: &handlers.BigInt{big.NewInt(1001)},
374+
BorrowAmount: &handlers.BigInt{big.NewInt(1000)},
375+
}
376+
body, _ := json.Marshal(input)
377+
378+
req := httptest.NewRequest(http.MethodPost, "/v1/chains/1/signatures", bytes.NewReader(body))
379+
req = mux.SetURLVars(req, map[string]string{
380+
"chainId": "1",
381+
})
382+
req.Header.Set("Content-Type", "application/json")
383+
384+
recorder := httptest.NewRecorder()
385+
386+
go func() {
387+
msg := <-msgChn
388+
ad := msg[0].Data.(*across.LifiEscrowData)
389+
ad.ErrChn <- nil
390+
}()
391+
392+
handler.HandleSigning(recorder, req)
393+
394+
s.Equal(http.StatusAccepted, recorder.Code)
395+
}
396+
363397
type StatusHandlerTestSuite struct {
364398
suite.Suite
365399

app/app.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ import (
1818
"github.com/libp2p/go-libp2p/core/crypto"
1919
"github.com/rs/zerolog/log"
2020
"github.com/spf13/viper"
21+
"github.com/sprintertech/lifi-solver/pkg/pricing"
22+
lifiTypes "github.com/sprintertech/lifi-solver/pkg/protocols/lifi"
23+
"github.com/sprintertech/lifi-solver/pkg/protocols/lifi/validation"
24+
"github.com/sprintertech/lifi-solver/pkg/token"
25+
"github.com/sprintertech/lifi-solver/pkg/tokenpricing/pyth"
2126
solverConfig "github.com/sprintertech/solver-config/go/config"
2227
"github.com/sprintertech/sprinter-signing/api"
2328
"github.com/sprintertech/sprinter-signing/api/handlers"
@@ -36,6 +41,7 @@ import (
3641
"github.com/sprintertech/sprinter-signing/metrics"
3742
"github.com/sprintertech/sprinter-signing/price"
3843
"github.com/sprintertech/sprinter-signing/protocol/across"
44+
"github.com/sprintertech/sprinter-signing/protocol/lifi"
3945
"github.com/sprintertech/sprinter-signing/protocol/mayan"
4046
"github.com/sprintertech/sprinter-signing/topology"
4147
"github.com/sprintertech/sprinter-signing/tss"
@@ -153,6 +159,7 @@ func Run() error {
153159
var hubPoolContract across.TokenMatcher
154160
acrossPools := make(map[uint64]common.Address)
155161
mayanPools := make(map[uint64]common.Address)
162+
lifiOutputSettlers := make(map[uint64]common.Address)
156163
repayerAddresses := make(map[uint64]common.Address)
157164
tokens := make(map[uint64]map[string]config.TokenConfig)
158165
for _, chainConfig := range configuration.ChainConfigs {
@@ -175,6 +182,11 @@ func Run() error {
175182
mayanPools[*c.GeneralChainConfig.Id] = poolAddress
176183
}
177184

185+
if c.LifiOutputSettler != "" {
186+
settlerAddress := common.HexToAddress(c.LifiOutputSettler)
187+
lifiOutputSettlers[*c.GeneralChainConfig.Id] = settlerAddress
188+
}
189+
178190
if c.AcrossHubPool != "" {
179191
hubPoolAddress := common.HexToAddress(c.AcrossHubPool)
180192
hubPoolContract = contracts.NewHubPoolContract(client, hubPoolAddress, c.Tokens)
@@ -269,6 +281,32 @@ func Run() error {
269281
confirmationsPerChain[*c.GeneralChainConfig.Id] = c.ConfirmationsByValue
270282
}
271283

284+
if c.LifiOutputSettler != "" {
285+
usdPricer := pyth.NewClient(ctx)
286+
resolver := token.NewTokenResolver(solverConfig, usdPricer)
287+
orderPricer := pricing.NewStandardPricer(resolver)
288+
lifiApi := lifi.NewLifiAPI()
289+
lifiValidator := validation.NewLifiEscrowOrderValidator[lifiTypes.LifiOrder](solverConfig, orderPricer)
290+
291+
lifiMh := evmMessage.NewLifiEscrowMessageHandler(
292+
*c.GeneralChainConfig.Id,
293+
lifiOutputSettlers,
294+
coordinator,
295+
host,
296+
communication,
297+
keyshareStore,
298+
watcher,
299+
tokenStore,
300+
lifiApi,
301+
orderPricer,
302+
lifiValidator,
303+
sigChn,
304+
)
305+
mh.RegisterMessageHandler(evmMessage.LifiEscrowMessage, lifiMh)
306+
supportedChains[*c.GeneralChainConfig.Id] = struct{}{}
307+
confirmationsPerChain[*c.GeneralChainConfig.Id] = c.ConfirmationsByValue
308+
}
309+
272310
lifiUnlockMh := evmMessage.NewLifiUnlockHandler(
273311
*c.GeneralChainConfig.Id,
274312
repayerAddresses,

chains/evm/calls/consts/lifi.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package consts
2+
3+
import (
4+
"strings"
5+
6+
"github.com/ethereum/go-ethereum/accounts/abi"
7+
)
8+
9+
var LifiABI, _ = abi.JSON(strings.NewReader(`[{
10+
"name": "fillOrderOutputs",
11+
"type": "function",
12+
"stateMutability": "nonpayable",
13+
"inputs": [
14+
{"name": "fillDeadline", "type": "uint32"},
15+
{"name": "orderId", "type": "bytes32"},
16+
{
17+
"name": "outputs",
18+
"type": "tuple[]",
19+
"components": [
20+
{"name": "oracle", "type": "bytes32"},
21+
{"name": "settler", "type": "bytes32"},
22+
{"name": "chainId", "type": "uint256"},
23+
{"name": "token", "type": "bytes32"},
24+
{"name": "amount", "type": "uint256"},
25+
{"name": "recipient", "type": "bytes32"},
26+
{"name": "call", "type": "bytes"},
27+
{"name": "context", "type": "bytes"}
28+
]
29+
},
30+
{"name": "proposedSolver", "type": "bytes32"}
31+
],
32+
"outputs": []
33+
}]`))

chains/evm/config.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ type EVMConfig struct {
2121
GeneralChainConfig chain.GeneralChainConfig
2222
Admin string
2323

24-
AcrossPool string
25-
AcrossHubPool string
26-
MayanSwift string
27-
Repayer string
24+
AcrossPool string
25+
AcrossHubPool string
26+
MayanSwift string
27+
LifiOutputSettler string
28+
Repayer string
2829

2930
Tokens map[string]config.TokenConfig
3031
// usd bucket -> confirmations
@@ -102,6 +103,8 @@ func NewEVMConfig(chainConfig map[string]interface{}, solverConfig solverConfig.
102103

103104
MayanSwift: solverConfig.ProtocolsMetadata.Mayan.SwiftContracts[id],
104105

106+
LifiOutputSettler: solverConfig.ProtocolsMetadata.Lifi.OutputSettler,
107+
105108
// nolint:gosec
106109
BlockRetryInterval: time.Duration(c.BlockRetryInterval) * time.Second,
107110
BlockInterval: big.NewInt(c.BlockInterval),

chains/evm/config_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ func (s *NewEVMConfigTestSuite) Test_ValidConfig() {
107107
"eip155:1": "repayer",
108108
},
109109
},
110+
Lifi: &solverConfig.LifiMetadata{
111+
OutputSettler: "settler",
112+
},
110113
},
111114
})
112115

@@ -128,6 +131,7 @@ func (s *NewEVMConfigTestSuite) Test_ValidConfig() {
128131
AcrossPool: "acrossPool",
129132
AcrossHubPool: "acrossHubPool",
130133
MayanSwift: "mayanSwift",
134+
LifiOutputSettler: "settler",
131135
ConfirmationsByValue: make(map[uint64]uint64),
132136
Tokens: make(map[string]config.TokenConfig),
133137
})
@@ -215,6 +219,9 @@ func (s *NewEVMConfigTestSuite) Test_ValidConfigWithCustomTxParams() {
215219
"eip155:1": "repayer",
216220
},
217221
},
222+
Lifi: &solverConfig.LifiMetadata{
223+
OutputSettler: "settler",
224+
},
218225
},
219226
})
220227

@@ -235,6 +242,7 @@ func (s *NewEVMConfigTestSuite) Test_ValidConfigWithCustomTxParams() {
235242
AcrossPool: "acrossPool",
236243
AcrossHubPool: "acrossHubPool",
237244
MayanSwift: "mayanSwift",
245+
LifiOutputSettler: "settler",
238246
Repayer: "repayer",
239247
ConfirmationsByValue: expectedBlockConfirmations,
240248
Tokens: expectedTokens,

chains/evm/message/across.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,17 @@ type Coordinator interface {
3636
}
3737

3838
type ConfirmationWatcher interface {
39-
WaitForConfirmations(
39+
WaitForTokenConfirmations(
4040
ctx context.Context,
4141
chainID uint64,
4242
txHash common.Hash,
4343
token common.Address,
4444
amount *big.Int) error
45+
WaitForOrderConfirmations(
46+
ctx context.Context,
47+
chainID uint64,
48+
txHash common.Hash,
49+
orderValue float64) error
4550
}
4651

4752
type DepositFetcher interface {
@@ -117,7 +122,7 @@ func (h *AcrossMessageHandler) HandleMessage(m *message.Message) (*proposal.Prop
117122
return nil, err
118123
}
119124

120-
err = h.confirmationWatcher.WaitForConfirmations(
125+
err = h.confirmationWatcher.WaitForTokenConfirmations(
121126
context.Background(),
122127
h.chainID,
123128
data.DepositTxHash,
@@ -136,7 +141,7 @@ func (h *AcrossMessageHandler) HandleMessage(m *message.Message) (*proposal.Prop
136141
return nil, err
137142
}
138143

139-
unlockHash, err := unlockHash(
144+
unlockHash, err := borrowUnlockHash(
140145
calldata,
141146
d.OutputAmount,
142147
common.BytesToAddress(d.OutputToken[12:]),

chains/evm/message/across_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ func (s *AcrossMessageHandlerTestSuite) Test_HandleMessage_ValidDeposit() {
187187
}
188188
s.mockDepositFetcher.EXPECT().Deposit(gomock.Any(), gomock.Any(), gomock.Any()).Return(deposit, nil)
189189

190-
s.mockWatcher.EXPECT().WaitForConfirmations(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
190+
s.mockWatcher.EXPECT().WaitForTokenConfirmations(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
191191
s.mockCoordinator.EXPECT().Execute(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
192192

193193
errChn := make(chan error, 1)

0 commit comments

Comments
 (0)