Skip to content

Commit c322e00

Browse files
committed
Implement across token matching
1 parent 664d249 commit c322e00

File tree

5 files changed

+210
-6
lines changed

5 files changed

+210
-6
lines changed

chains/evm/calls/consts/across.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,35 @@ import (
66
"github.com/ethereum/go-ethereum/accounts/abi"
77
)
88

9+
var HubPoolABI, _ = abi.JSON(strings.NewReader(`
10+
[
11+
{
12+
"inputs": [
13+
{
14+
"internalType": "uint256",
15+
"name": "destinationChainId",
16+
"type": "uint256"
17+
},
18+
{
19+
"internalType": "address",
20+
"name": "l1Token",
21+
"type": "address"
22+
}
23+
],
24+
"name": "poolRebalanceRoute",
25+
"outputs": [
26+
{
27+
"internalType": "address",
28+
"name": "destinationToken",
29+
"type": "address"
30+
}
31+
],
32+
"stateMutability": "view",
33+
"type": "function"
34+
}
35+
]
36+
`))
37+
938
var SpokePoolABI, _ = abi.JSON(strings.NewReader(`
1039
[
1140
{
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// The Licensed Work is (c) 2022 Sygma
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
package contracts
5+
6+
import (
7+
"fmt"
8+
"math/big"
9+
10+
"github.com/ethereum/go-ethereum/accounts/abi"
11+
"github.com/ethereum/go-ethereum/common"
12+
"github.com/sprintertech/sprinter-signing/chains/evm/calls/consts"
13+
"github.com/sygmaprotocol/sygma-core/chains/evm/client"
14+
"github.com/sygmaprotocol/sygma-core/chains/evm/contracts"
15+
)
16+
17+
type HubPoolContract struct {
18+
contracts.Contract
19+
client client.Client
20+
tokens map[string]common.Address
21+
}
22+
23+
func NewHubPoolContract(
24+
client client.Client,
25+
address common.Address,
26+
l1Tokens map[string]common.Address,
27+
) *HubPoolContract {
28+
return &HubPoolContract{
29+
Contract: contracts.NewContract(address, consts.HubPoolABI, nil, client, nil),
30+
client: client,
31+
tokens: l1Tokens,
32+
}
33+
}
34+
35+
func (c *HubPoolContract) DestinationToken(destinationChainId *big.Int, symbol string) (common.Address, error) {
36+
tokenAddress, ok := c.tokens[symbol]
37+
if !ok {
38+
return common.Address{}, fmt.Errorf("no hub pool token configured for symbol %s", symbol)
39+
}
40+
41+
res, err := c.CallContract("poolRebalanceRoute", destinationChainId, tokenAddress)
42+
if err != nil {
43+
return common.Address{}, err
44+
}
45+
46+
out := *abi.ConvertType(res[0], new(common.Address)).(*common.Address)
47+
if out.Hex() == (common.Address{}).Hex() {
48+
return common.Address{}, fmt.Errorf("rebalance route not configured for %s", symbol)
49+
}
50+
51+
return out, nil
52+
}

chains/evm/message/across.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ import (
3131
const (
3232
AcrossMessage = "AcrossMessage"
3333

34-
DOMAIN_NAME = "LiquidityPool"
35-
VERSION = "1.0.0"
36-
PROTOCOL_ID = 1
37-
BLOCK_RANGE = 1000
34+
ZERO_ADDRESS = "0000000000000000000000000000000000000000000000000000000000000000"
35+
DOMAIN_NAME = "LiquidityPool"
36+
VERSION = "1.0.0"
37+
PROTOCOL_ID = 1
38+
BLOCK_RANGE = 1000
3839

3940
TIMEOUT = 10 * time.Minute
4041
)
@@ -72,13 +73,18 @@ type TokenPricer interface {
7273
TokenPrice(symbol string) (float64, error)
7374
}
7475

76+
type TokenMatcher interface {
77+
DestinationToken(destinationChainId *big.Int, symbol string) (common.Address, error)
78+
}
79+
7580
type AcrossMessageHandler struct {
7681
client EventFilterer
7782
chainID uint64
7883

7984
tokens map[string]evm.TokenConfig
8085
confirmations map[uint64]uint64
8186
blocktime time.Duration
87+
tokenMatcher TokenMatcher
8288
tokenPricer TokenPricer
8389
pools map[uint64]common.Address
8490

@@ -87,7 +93,7 @@ type AcrossMessageHandler struct {
8793
comm comm.Communication
8894
fetcher signing.SaveDataFetcher
8995

90-
sigChn chan interface{}
96+
sigChn chan any
9197
}
9298

9399
func NewAcrossMessageHandler(
@@ -99,7 +105,8 @@ func NewAcrossMessageHandler(
99105
comm comm.Communication,
100106
fetcher signing.SaveDataFetcher,
101107
tokenPricer TokenPricer,
102-
sigChn chan interface{},
108+
tokenMatcher TokenMatcher,
109+
sigChn chan any,
103110
tokens map[string]evm.TokenConfig,
104111
confirmations map[uint64]uint64,
105112
blocktime time.Duration,
@@ -117,6 +124,7 @@ func NewAcrossMessageHandler(
117124
confirmations: confirmations,
118125
blocktime: blocktime,
119126
tokenPricer: tokenPricer,
127+
tokenMatcher: tokenMatcher,
120128
}
121129
}
122130

@@ -237,6 +245,15 @@ func (h *AcrossMessageHandler) minimalConfirmations(d *events.AcrossDeposit) (ui
237245
return 0, err
238246
}
239247

248+
if common.Bytes2Hex(d.OutputToken[:]) == ZERO_ADDRESS {
249+
address, err := h.tokenMatcher.DestinationToken(d.DestinationChainId, symbol)
250+
if err != nil {
251+
return 0, err
252+
}
253+
254+
d.OutputToken = common.BytesToHash(address.Bytes())
255+
}
256+
240257
price, err := h.tokenPricer.TokenPrice(symbol)
241258
if err != nil {
242259
return 0, err

chains/evm/message/across_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type AcrossMessageHandlerTestSuite struct {
3333
mockHost *mock_host.MockHost
3434
mockFetcher *mock_tss.MockSaveDataFetcher
3535
mockPricer *mock_message.MockTokenPricer
36+
mockMatcher *mock_message.MockTokenMatcher
3637

3738
handler *message.AcrossMessageHandler
3839
sigChn chan interface{}
@@ -60,6 +61,7 @@ func (s *AcrossMessageHandlerTestSuite) SetupTest() {
6061
s.mockFetcher.EXPECT().GetKeyshare().AnyTimes().Return(keyshare.ECDSAKeyshare{}, nil)
6162

6263
s.mockPricer = mock_message.NewMockTokenPricer(ctrl)
64+
s.mockMatcher = mock_message.NewMockTokenMatcher(ctrl)
6365

6466
pools := make(map[uint64]common.Address)
6567
pools[2] = common.HexToAddress("0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5")
@@ -74,8 +76,13 @@ func (s *AcrossMessageHandlerTestSuite) SetupTest() {
7476
Address: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
7577
Decimals: 18,
7678
}
79+
tokens["USDC"] = evm.TokenConfig{
80+
Address: common.HexToAddress("0x3355df6d4c9c3035724fd0e3914de96a5a83aaf4"),
81+
Decimals: 6,
82+
}
7783
confirmations := make(map[uint64]uint64)
7884
confirmations[1000] = 100
85+
confirmations[2000] = 200
7986

8087
s.handler = message.NewAcrossMessageHandler(
8188
1,
@@ -86,6 +93,7 @@ func (s *AcrossMessageHandlerTestSuite) SetupTest() {
8693
s.mockCommunication,
8794
s.mockFetcher,
8895
s.mockPricer,
96+
s.mockMatcher,
8997
s.sigChn,
9098
tokens,
9199
confirmations,
@@ -259,3 +267,62 @@ func (s *AcrossMessageHandlerTestSuite) Test_HandleMessage_ValidLog() {
259267
err = <-errChn
260268
s.Nil(err)
261269
}
270+
271+
func (s *AcrossMessageHandlerTestSuite) Test_HandleMessage_ZeroOutputToken() {
272+
s.mockCommunication.EXPECT().Broadcast(
273+
gomock.Any(),
274+
gomock.Any(),
275+
comm.AcrossMsg,
276+
fmt.Sprintf("%d-%s", 1, comm.AcrossSessionID),
277+
).Return(nil)
278+
p, _ := pstoremem.NewPeerstore()
279+
s.mockHost.EXPECT().Peerstore().Return(p)
280+
281+
s.mockEventFilterer.EXPECT().TransactionReceipt(
282+
gomock.Any(),
283+
gomock.Any(),
284+
).Return(&types.Receipt{}, fmt.Errorf("missing transaction receipt"))
285+
s.mockEventFilterer.EXPECT().TransactionReceipt(gomock.Any(), gomock.Any()).Return(&types.Receipt{
286+
BlockNumber: big.NewInt(200),
287+
}, nil)
288+
289+
log, _ := hex.DecodeString("0000000000000000000000003355df6d4c9c3035724fd0e3914de96a5a83aaf40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d7cbe22000000000000000000000000000000000000000000000000000000006d789ac90000000000000000000000000000000000000000000000000000000067ce09230000000000000000000000000000000000000000000000000000000067ce5ea7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051d55999c7cd91b17af7276cbecd647dbc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000")
290+
s.mockEventFilterer.EXPECT().LatestBlock().Return(big.NewInt(400), nil).AnyTimes()
291+
s.mockEventFilterer.EXPECT().FilterLogs(gomock.Any(), gomock.Any()).Return([]types.Log{
292+
{
293+
Removed: false,
294+
Data: log,
295+
Topics: []common.Hash{
296+
{},
297+
{},
298+
{},
299+
{},
300+
},
301+
},
302+
}, nil)
303+
s.mockCoordinator.EXPECT().Execute(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
304+
s.mockPricer.EXPECT().TokenPrice("USDC").Return(0.99, nil)
305+
s.mockMatcher.EXPECT().DestinationToken(gomock.Any(), "USDC").Return(common.Address{}, nil)
306+
307+
errChn := make(chan error, 1)
308+
ad := message.AcrossData{
309+
ErrChn: errChn,
310+
DepositId: big.NewInt(100),
311+
Nonce: big.NewInt(101),
312+
LiquidityPool: common.HexToAddress("0xbe526bA5d1ad94cC59D7A79d99A59F607d31A657"),
313+
Caller: common.HexToAddress("0x5ECF7351930e4A251193aA022Ef06249C6cBfa27"),
314+
}
315+
m := &coreMessage.Message{
316+
Data: ad,
317+
Source: 1,
318+
Destination: 2,
319+
}
320+
321+
prop, err := s.handler.HandleMessage(m)
322+
323+
s.Nil(prop)
324+
s.Nil(err)
325+
326+
err = <-errChn
327+
s.Nil(err)
328+
}

chains/evm/message/mock/across.go

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)