Skip to content

Commit 6d09d0c

Browse files
authored
feat: rhinestone message handler (#56)
* Use depositId field for getting mayan order hash * Update tests * Lint * Implement rhinestone orchestrator api * Check origin from across deposit event * Move across deposit fetching into separate package * Add rhinestone fill decoding * Implement rhinestone optimistic test * Update app with new deposit fetcher * Fix tests * Add rhinestone to signing api * Fix linter
1 parent 7e6a7ce commit 6d09d0c

File tree

20 files changed

+1582
-300
lines changed

20 files changed

+1582
-300
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ genmocks:
3636
mockgen -destination=./comm/p2p/mock/conn/conn.go github.com/libp2p/go-libp2p/core/network Conn
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
39+
mockgen -source=./chains/evm/message/rhinestone.go -destination=./chains/evm/message/mock/rhinestone.go
3940
mockgen -source=./chains/evm/message/mayan.go -destination=./chains/evm/message/mock/mayan.go
4041
mockgen -source=./chains/evm/message/confirmations.go -destination=./chains/evm/message/mock/confirmations.go
4142
mockgen -source=./api/handlers/signing.go -destination=./api/handlers/mock/signing.go

api/handlers/signing.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ import (
1717
type ProtocolType string
1818

1919
const (
20-
AcrossProtocol ProtocolType = "across"
21-
MayanProtocol ProtocolType = "mayan"
20+
AcrossProtocol ProtocolType = "across"
21+
MayanProtocol ProtocolType = "mayan"
22+
RhinestoneProtocol ProtocolType = "rhinestone"
2223
)
2324

2425
type SigningBody struct {
@@ -95,6 +96,19 @@ func (h *SigningHandler) HandleSigning(w http.ResponseWriter, r *http.Request) {
9596
BorrowAmount: b.BorrowAmount.Int,
9697
})
9798
}
99+
case RhinestoneProtocol:
100+
{
101+
m = evmMessage.NewRhinestoneMessage(0, b.ChainId, &evmMessage.RhinestoneData{
102+
BundleID: b.DepositId,
103+
Nonce: b.Nonce.Int,
104+
LiquidityPool: common.HexToAddress(b.LiquidityPool),
105+
Caller: common.HexToAddress(b.Caller),
106+
ErrChn: errChn,
107+
Source: 0,
108+
Destination: b.ChainId,
109+
BorrowAmount: b.BorrowAmount.Int,
110+
})
111+
}
98112
default:
99113
JSONError(w, fmt.Errorf("invalid protocol %s", b.Protocol), http.StatusBadRequest)
100114
return

api/handlers/signing_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,40 @@ func (s *SigningHandlerTestSuite) Test_HandleSigning_MayanSuccess() {
324324
s.Equal(http.StatusAccepted, recorder.Code)
325325
}
326326

327+
func (s *SigningHandlerTestSuite) Test_HandleSigning_RhinestoneSuccess() {
328+
msgChn := make(chan []*message.Message)
329+
handler := handlers.NewSigningHandler(msgChn, s.chains)
330+
331+
input := handlers.SigningBody{
332+
DepositId: "depositID",
333+
Protocol: "rhinestone",
334+
LiquidityPool: "0xbe526bA5d1ad94cC59D7A79d99A59F607d31A657",
335+
Caller: "0xbe526bA5d1ad94cC59D7A79d99A59F607d31A657",
336+
Calldata: "0xbe5",
337+
Nonce: &handlers.BigInt{big.NewInt(1001)},
338+
BorrowAmount: &handlers.BigInt{big.NewInt(1000)},
339+
}
340+
body, _ := json.Marshal(input)
341+
342+
req := httptest.NewRequest(http.MethodPost, "/v1/chains/1/signatures", bytes.NewReader(body))
343+
req = mux.SetURLVars(req, map[string]string{
344+
"chainId": "1",
345+
})
346+
req.Header.Set("Content-Type", "application/json")
347+
348+
recorder := httptest.NewRecorder()
349+
350+
go func() {
351+
msg := <-msgChn
352+
ad := msg[0].Data.(*across.RhinestoneData)
353+
ad.ErrChn <- nil
354+
}()
355+
356+
handler.HandleSigning(recorder, req)
357+
358+
s.Equal(http.StatusAccepted, recorder.Code)
359+
}
360+
327361
type StatusHandlerTestSuite struct {
328362
suite.Suite
329363

app/app.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"github.com/sprintertech/sprinter-signing/keyshare"
3636
"github.com/sprintertech/sprinter-signing/metrics"
3737
"github.com/sprintertech/sprinter-signing/price"
38+
"github.com/sprintertech/sprinter-signing/protocol/across"
3839
"github.com/sprintertech/sprinter-signing/protocol/mayan"
3940
"github.com/sprintertech/sprinter-signing/topology"
4041
"github.com/sprintertech/sprinter-signing/tss"
@@ -149,7 +150,7 @@ func Run() error {
149150
solverConfig, err := solverConfig.FetchSolverConfig(ctx, solverConfigOpts...)
150151
panicOnError(err)
151152

152-
var hubPoolContract evmMessage.TokenMatcher
153+
var hubPoolContract across.TokenMatcher
153154
acrossPools := make(map[uint64]common.Address)
154155
mayanPools := make(map[uint64]common.Address)
155156
repayerAddresses := make(map[uint64]common.Address)
@@ -220,16 +221,20 @@ func Run() error {
220221

221222
mh := message.NewMessageHandler()
222223
if c.AcrossPool != "" {
223-
acrossMh := evmMessage.NewAcrossMessageHandler(
224+
acrossDepositFetcher := across.NewAcrossDepositFetcher(
224225
*c.GeneralChainConfig.Id,
226+
tokenStore,
225227
client,
228+
hubPoolContract,
229+
)
230+
acrossMh := evmMessage.NewAcrossMessageHandler(
231+
*c.GeneralChainConfig.Id,
226232
acrossPools,
227233
coordinator,
228234
host,
229235
communication,
230236
keyshareStore,
231-
hubPoolContract,
232-
tokenStore,
237+
acrossDepositFetcher,
233238
watcher,
234239
sigChn)
235240
go acrossMh.Listen(ctx)
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package consts
2+
3+
import (
4+
"strings"
5+
6+
"github.com/ethereum/go-ethereum/accounts/abi"
7+
)
8+
9+
var RhinestoneABI, _ = abi.JSON(strings.NewReader(`
10+
[
11+
{
12+
"type":"function",
13+
"name":"fill",
14+
"inputs":[
15+
{
16+
"name":"payload",
17+
"type":"tuple",
18+
"internalType":"struct IRhinestoneSpokePool.IntentFillPayload",
19+
"components":[
20+
{
21+
"name":"segments",
22+
"type":"tuple[]",
23+
"internalType":"struct IRhinestoneSpokePool.SegmentData[]",
24+
"components":[
25+
{
26+
"name":"tokenIn",
27+
"type":"uint256[2][]",
28+
"internalType":"uint256[2][]"
29+
},
30+
{
31+
"name":"tokenOut",
32+
"type":"uint256[2][]",
33+
"internalType":"uint256[2][]"
34+
},
35+
{
36+
"name":"originModule",
37+
"type":"address",
38+
"internalType":"address"
39+
},
40+
{
41+
"name":"originWETHAddress",
42+
"type":"address",
43+
"internalType":"address"
44+
},
45+
{
46+
"name":"originChainId",
47+
"type":"uint256",
48+
"internalType":"uint256"
49+
},
50+
{
51+
"name":"compactNonce",
52+
"type":"uint256",
53+
"internalType":"uint256"
54+
}
55+
]
56+
},
57+
{
58+
"name":"message",
59+
"type":"bytes",
60+
"internalType":"bytes"
61+
},
62+
{
63+
"name":"orchestratorSig",
64+
"type":"bytes",
65+
"internalType":"bytes"
66+
}
67+
]
68+
},
69+
{
70+
"name":"exclusiveRelayer",
71+
"type":"address",
72+
"internalType":"address"
73+
},
74+
{
75+
"name":"repaymentAddresses",
76+
"type":"address[]",
77+
"internalType":"address[]"
78+
},
79+
{
80+
"name":"repaymentChainIds",
81+
"type":"uint256[]",
82+
"internalType":"uint256[]"
83+
},
84+
{
85+
"name":"accountCreation",
86+
"type":"tuple",
87+
"internalType":"AccountCreation ",
88+
"components":[
89+
{
90+
"name":"account",
91+
"type":"address",
92+
"internalType":"address"
93+
},
94+
{
95+
"name":"initCode",
96+
"type":"bytes",
97+
"internalType":"bytes"
98+
}
99+
]
100+
}
101+
],
102+
"outputs":[
103+
104+
],
105+
"stateMutability":"nonpayable"
106+
},
107+
{
108+
"type":"function",
109+
"name":"fill",
110+
"inputs":[
111+
{
112+
"name":"payload",
113+
"type":"tuple",
114+
"internalType":"struct IRhinestoneSpokePool.IntentFillPayload",
115+
"components":[
116+
{
117+
"name":"segments",
118+
"type":"tuple[]",
119+
"internalType":"struct IRhinestoneSpokePool.SegmentData[]",
120+
"components":[
121+
{
122+
"name":"tokenIn",
123+
"type":"uint256[2][]",
124+
"internalType":"uint256[2][]"
125+
},
126+
{
127+
"name":"tokenOut",
128+
"type":"uint256[2][]",
129+
"internalType":"uint256[2][]"
130+
},
131+
{
132+
"name":"originModule",
133+
"type":"address",
134+
"internalType":"address"
135+
},
136+
{
137+
"name":"originWETHAddress",
138+
"type":"address",
139+
"internalType":"address"
140+
},
141+
{
142+
"name":"originChainId",
143+
"type":"uint256",
144+
"internalType":"uint256"
145+
},
146+
{
147+
"name":"compactNonce",
148+
"type":"uint256",
149+
"internalType":"uint256"
150+
}
151+
]
152+
},
153+
{
154+
"name":"message",
155+
"type":"bytes",
156+
"internalType":"bytes"
157+
},
158+
{
159+
"name":"orchestratorSig",
160+
"type":"bytes",
161+
"internalType":"bytes"
162+
}
163+
]
164+
},
165+
{
166+
"name":"exclusiveRelayer",
167+
"type":"address",
168+
"internalType":"address"
169+
},
170+
{
171+
"name":"repaymentAddresses",
172+
"type":"address[]",
173+
"internalType":"address[]"
174+
},
175+
{
176+
"name":"repaymentChainIds",
177+
"type":"uint256[]",
178+
"internalType":"uint256[]"
179+
}
180+
],
181+
"outputs":[
182+
183+
],
184+
"stateMutability":"nonpayable"
185+
}
186+
]
187+
`))
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// The Licensed Work is (c) 2022 Sygma
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
package contracts
5+
6+
import (
7+
"math/big"
8+
9+
"github.com/ethereum/go-ethereum/accounts/abi"
10+
"github.com/ethereum/go-ethereum/common"
11+
"github.com/sprintertech/sprinter-signing/chains/evm/calls/consts"
12+
)
13+
14+
type SegmentData struct {
15+
TokenIn [][2]*big.Int `abi:"tokenIn"`
16+
TokenOut [][2]*big.Int `abi:"tokenOut"`
17+
OriginModule common.Address `abi:"originModule"`
18+
OriginWETHAddress common.Address `abi:"originWETHAddress"`
19+
OriginChainId *big.Int `abi:"originChainId"`
20+
CompactNonce *big.Int `abi:"compactNonce"`
21+
}
22+
23+
type IntentFillPayload struct {
24+
Segments []SegmentData `abi:"segments"`
25+
Message []byte `abi:"message"`
26+
OrchestratorSig []byte `abi:"orchestratorSig"`
27+
}
28+
29+
type AccountCreation struct {
30+
Account common.Address `abi:"account"`
31+
InitCode []byte `abi:"initCode"`
32+
}
33+
34+
type FillInput struct {
35+
Payload IntentFillPayload `abi:"payload"`
36+
ExclusiveRelayer common.Address `abi:"exclusiveRelayer"`
37+
RepaymentAddresses []common.Address `abi:"repaymentAddresses"`
38+
RepaymentChainIds []*big.Int `abi:"repaymentChainIds"`
39+
AccountCreation AccountCreation `abi:"accountCreation"`
40+
}
41+
42+
type RhinestoneContract struct {
43+
abi abi.ABI
44+
}
45+
46+
func NewRhinestoneContract() *RhinestoneContract {
47+
return &RhinestoneContract{
48+
abi: consts.RhinestoneABI,
49+
}
50+
}
51+
52+
func (c *RhinestoneContract) DecodeFillCall(calldata []byte) (*FillInput, error) {
53+
var fillInput FillInput
54+
method := c.abi.Methods["fill"]
55+
res, err := method.Inputs.Unpack(calldata[4:])
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
err = method.Inputs.Copy(&fillInput, res)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
return &fillInput, nil
66+
}

0 commit comments

Comments
 (0)