Skip to content

Commit 54b76d6

Browse files
committed
Implement calculation of the unlock hash that matches on-chain implementation
1 parent 5205828 commit 54b76d6

File tree

5 files changed

+231
-14
lines changed

5 files changed

+231
-14
lines changed

chains/evm/calls/consts/across.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package consts
2+
3+
const SpokePoolABI = `
4+
[
5+
{
6+
"inputs": [
7+
{
8+
"components": [
9+
{
10+
"internalType": "bytes32",
11+
"name": "depositor",
12+
"type": "bytes32"
13+
},
14+
{
15+
"internalType": "bytes32",
16+
"name": "recipient",
17+
"type": "bytes32"
18+
},
19+
{
20+
"internalType": "bytes32",
21+
"name": "exclusiveRelayer",
22+
"type": "bytes32"
23+
},
24+
{
25+
"internalType": "bytes32",
26+
"name": "inputToken",
27+
"type": "bytes32"
28+
},
29+
{
30+
"internalType": "bytes32",
31+
"name": "outputToken",
32+
"type": "bytes32"
33+
},
34+
{
35+
"internalType": "uint256",
36+
"name": "inputAmount",
37+
"type": "uint256"
38+
},
39+
{
40+
"internalType": "uint256",
41+
"name": "outputAmount",
42+
"type": "uint256"
43+
},
44+
{
45+
"internalType": "uint256",
46+
"name": "originChainId",
47+
"type": "uint256"
48+
},
49+
{
50+
"internalType": "uint256",
51+
"name": "depositId",
52+
"type": "uint256"
53+
},
54+
{
55+
"internalType": "uint32",
56+
"name": "fillDeadline",
57+
"type": "uint32"
58+
},
59+
{
60+
"internalType": "uint32",
61+
"name": "exclusivityDeadline",
62+
"type": "uint32"
63+
},
64+
{
65+
"internalType": "bytes",
66+
"name": "message",
67+
"type": "bytes"
68+
}
69+
],
70+
"internalType": "struct V3SpokePoolInterface.V3RelayData",
71+
"name": "relayData",
72+
"type": "tuple"
73+
},
74+
{
75+
"internalType": "uint256",
76+
"name": "repaymentChainId",
77+
"type": "uint256"
78+
},
79+
{
80+
"internalType": "bytes32",
81+
"name": "repaymentAddress",
82+
"type": "bytes32"
83+
}
84+
],
85+
"name": "fillRelay",
86+
"outputs": [],
87+
"stateMutability": "nonpayable",
88+
"type": "function"
89+
}
90+
]
91+
`

chains/evm/calls/events/events.go

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ package events
55

66
import (
77
"math/big"
8+
"strings"
89

10+
"github.com/ethereum/go-ethereum/accounts/abi"
911
"github.com/ethereum/go-ethereum/common"
1012
"github.com/ethereum/go-ethereum/crypto"
13+
"github.com/sprintertech/sprinter-signing/chains/evm/calls/consts"
1114
)
1215

1316
type EventSig string
@@ -34,14 +37,54 @@ type AcrossDeposit struct {
3437
OutputToken [32]byte
3538
InputAmount *big.Int
3639
OutputAmount *big.Int
37-
RepaymentChainId *big.Int
38-
OriginChainId *big.Int
40+
DestinationChainId *big.Int
3941
DepositId *big.Int
4042
QuoteTimestamp uint32
41-
FillDeadline uint32
4243
ExclusivityDeadline uint32
44+
FillDeadline uint32
45+
Depositor [32]byte
46+
Recipient [32]byte
47+
ExclusiveRelayer [32]byte
48+
Message []byte
49+
}
50+
51+
func (a *AcrossDeposit) ToV3RelayData(originChainID *big.Int) *AcrossV3RelayData {
52+
return &AcrossV3RelayData{
53+
Depositor: a.Depositor,
54+
Recipient: a.Recipient,
55+
ExclusiveRelayer: a.ExclusiveRelayer,
56+
InputToken: a.InputToken,
57+
OutputToken: a.OutputToken,
58+
InputAmount: a.InputAmount,
59+
OutputAmount: a.OutputAmount,
60+
OriginChainId: originChainID,
61+
DepositId: a.DepositId,
62+
FillDeadline: a.FillDeadline,
63+
ExclusivityDeadline: a.ExclusivityDeadline,
64+
Message: a.Message,
65+
}
66+
}
67+
68+
type AcrossV3RelayData struct {
4369
Depositor [32]byte
4470
Recipient [32]byte
45-
ExclusiveRelayer common.Address
71+
ExclusiveRelayer [32]byte
72+
InputToken [32]byte
73+
OutputToken [32]byte
74+
InputAmount *big.Int
75+
OutputAmount *big.Int
76+
OriginChainId *big.Int
77+
DepositId *big.Int
78+
FillDeadline uint32
79+
ExclusivityDeadline uint32
4680
Message []byte
4781
}
82+
83+
func (a *AcrossV3RelayData) Calldata() ([]byte, error) {
84+
abi, _ := abi.JSON(strings.NewReader(consts.SpokePoolABI))
85+
input, err := abi.Pack("fillRelay", a)
86+
if err != nil {
87+
return []byte{}, err
88+
}
89+
return input, nil
90+
}

chains/evm/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type HandlerConfig struct {
2222
type EVMConfig struct {
2323
GeneralChainConfig chain.GeneralChainConfig
2424
Admin string
25+
LiqudityPool string
2526
BlockConfirmations *big.Int
2627
BlockInterval *big.Int
2728
BlockRetryInterval time.Duration
@@ -30,6 +31,7 @@ type EVMConfig struct {
3031
type RawEVMConfig struct {
3132
chain.GeneralChainConfig `mapstructure:",squash"`
3233
Admin string `mapstructure:"admin"`
34+
LiqudityPool string `mapstructure:"liquidityPool"`
3335
BlockConfirmations int64 `mapstructure:"blockConfirmations" default:"10"`
3436
BlockInterval int64 `mapstructure:"blockInterval" default:"5"`
3537
BlockRetryInterval uint64 `mapstructure:"blockRetryInterval" default:"5"`
@@ -71,6 +73,7 @@ func NewEVMConfig(chainConfig map[string]interface{}) (*EVMConfig, error) {
7173
config := &EVMConfig{
7274
GeneralChainConfig: c.GeneralChainConfig,
7375
Admin: c.Admin,
76+
LiqudityPool: c.LiqudityPool,
7477
BlockRetryInterval: time.Duration(c.BlockRetryInterval) * time.Second,
7578
BlockConfirmations: big.NewInt(c.BlockConfirmations),
7679
BlockInterval: big.NewInt(c.BlockInterval),

chains/evm/config_test.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,15 @@ func (s *NewEVMConfigTestSuite) Test_ValidConfig() {
9191

9292
func (s *NewEVMConfigTestSuite) Test_ValidConfigWithCustomTxParams() {
9393
rawConfig := map[string]interface{}{
94-
"id": 1,
95-
"endpoint": "ws://domain.com",
96-
"name": "evm1",
97-
"from": "address",
98-
"bridge": "bridgeAddress",
99-
"admin": "adminAddress",
100-
"retry": "retryAddress",
101-
"frostKeygen": "frostKeygen",
94+
"id": 1,
95+
"endpoint": "ws://domain.com",
96+
"name": "evm1",
97+
"from": "address",
98+
"bridge": "bridgeAddress",
99+
"admin": "adminAddress",
100+
"liquidityPool": "pool",
101+
"retry": "retryAddress",
102+
"frostKeygen": "frostKeygen",
102103
"handlers": []evm.HandlerConfig{
103104
{
104105
Type: "erc20",
@@ -135,5 +136,6 @@ func (s *NewEVMConfigTestSuite) Test_ValidConfigWithCustomTxParams() {
135136
BlockInterval: big.NewInt(2),
136137
BlockRetryInterval: time.Duration(10) * time.Second,
137138
Admin: "adminAddress",
139+
LiqudityPool: "pool",
138140
})
139141
}

chains/evm/message/across.go

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import (
99
"github.com/ethereum/go-ethereum"
1010
"github.com/ethereum/go-ethereum/accounts/abi"
1111
"github.com/ethereum/go-ethereum/common"
12+
"github.com/ethereum/go-ethereum/common/math"
1213
"github.com/ethereum/go-ethereum/core/types"
14+
"github.com/ethereum/go-ethereum/crypto"
15+
"github.com/ethereum/go-ethereum/signer/core/apitypes"
1316
"github.com/libp2p/go-libp2p/core/host"
1417
"github.com/libp2p/go-libp2p/core/peer"
1518
"github.com/rs/zerolog/log"
@@ -24,6 +27,11 @@ import (
2427

2528
const (
2629
AcrossMessage = "AcrossMessage"
30+
31+
DOMAIN_NAME = ""
32+
VERSION = "v1.0.0"
33+
BORROW_TYPEHASH = "Borrow(address borrowToken,uint256 amount,address target,bytes targetCallData,uint256 nonce,uint256 deadline)"
34+
PROTOCOL_ID = 1
2735
)
2836

2937
type EventFilterer interface {
@@ -45,10 +53,16 @@ func NewAcrossMessage(source uint8, destination uint8, acrossData AcrossData) *m
4553
}
4654
}
4755

56+
type Coordinator interface {
57+
Execute(ctx context.Context, tssProcesses []tss.TssProcess, resultChn chan interface{}, coordinator peer.ID) error
58+
}
59+
4860
type AcrossMessageHandler struct {
49-
client EventFilterer
61+
client EventFilterer
62+
sourceChainID *big.Int
5063

5164
across common.Address
65+
pools map[uint64]common.Address
5266
abi abi.ABI
5367

5468
coordinator *tss.Coordinator
@@ -106,8 +120,13 @@ func (h *AcrossMessageHandler) HandleMessage(m *message.Message) (*proposal.Prop
106120
return nil, err
107121
}
108122

123+
unlockHash, err := h.unlockHash(d)
124+
if err != nil {
125+
return nil, err
126+
}
127+
109128
signing, err := signing.NewSigning(
110-
d.DepositId,
129+
new(big.Int).SetBytes(unlockHash),
111130
data.depositId.Text(16),
112131
data.depositId.Text(16),
113132
h.host,
@@ -161,3 +180,62 @@ func (h *AcrossMessageHandler) parseDeposit(l types.Log) (*events.AcrossDeposit,
161180
err := h.abi.UnpackIntoInterface(&d, "V3FundsDeposited", l.Data)
162181
return d, err
163182
}
183+
184+
func (h *AcrossMessageHandler) unlockHash(deposit *events.AcrossDeposit) ([]byte, error) {
185+
calldata, err := deposit.ToV3RelayData(h.sourceChainID).Calldata()
186+
if err != nil {
187+
return []byte{}, nil
188+
}
189+
190+
encodedData := crypto.Keccak256(
191+
crypto.Keccak256(
192+
[]byte(
193+
"Borrow(address borrowToken,uint256 amount,address target,bytes targetCallData,uint256 nonce,uint256 deadline)",
194+
),
195+
),
196+
deposit.OutputToken[12:],
197+
deposit.OutputAmount.Bytes(),
198+
deposit.Recipient[12:],
199+
calldata,
200+
common.LeftPadBytes(h.nonce(deposit).Bytes(), 32),
201+
new(big.Int).SetUint64(uint64(deposit.FillDeadline)).Bytes(),
202+
)
203+
204+
poolAddress := h.pools[h.sourceChainID.Uint64()]
205+
typedData := apitypes.TypedData{
206+
Domain: apitypes.TypedDataDomain{
207+
Name: DOMAIN_NAME,
208+
ChainId: math.NewHexOrDecimal256(deposit.DestinationChainId.Int64()),
209+
Version: VERSION,
210+
VerifyingContract: poolAddress.Hex(),
211+
},
212+
}
213+
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
214+
if err != nil {
215+
return []byte{}, err
216+
}
217+
218+
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(encodedData)))
219+
return crypto.Keccak256(rawData), nil
220+
}
221+
222+
// nonce creates a unique ID from the across deposit id, origin chain id and protocol id.
223+
// Resulting id has this format: [originChainID (8 bits)][protocolID (8 bits)][nonce (240 bits)].
224+
func (h *AcrossMessageHandler) nonce(deposit *events.AcrossDeposit) *big.Int {
225+
// Create a new big.Int
226+
nonce := new(big.Int)
227+
228+
// Set originChainID (64 bits)
229+
nonce.SetInt64(h.sourceChainID.Int64())
230+
nonce.Lsh(nonce, 256) // Shift left by 320 bits (248 + 8)
231+
232+
// Add protocolID in the middle (shifted left by 248 bits)
233+
protocolInt := big.NewInt(PROTOCOL_ID)
234+
protocolInt.Lsh(protocolInt, 248)
235+
nonce.Or(nonce, protocolInt)
236+
237+
// Add nonce at the end
238+
nonce.Or(nonce, deposit.DepositId)
239+
240+
return nonce
241+
}

0 commit comments

Comments
 (0)