Skip to content

Commit bac8c95

Browse files
yrongalistair-singhclaravanstaden
authored
Relayer: Profit Estimation (#1510)
* basic refactor * factored out different parachains * fmt * remove fee overrides, allow it to be handled in base class * add acala * fixes after rebase * fix kusama transfer * typo * use default fee for acala * fixes for kusama * up versions * fix getLocationBalance for AH * fmt * replace import * fix asset lookup * throw if used * Fix query PNA (#1489) * Fix for PNA query * Print response * Fix smoke tests for Mainnet * Use local test account by default * More refactoring * Export types * Parameterize the test script * Kusama transfer history (#1484) * kusama transfer history * kusama transfer history * up version numbers * tune fee * For Snowbridge V2 * Revamp for penpal * Initial setup * Revamp register PNA call with fee attached * Fix weight * Chore * Fix regitry.ts * Fix building * Remove unused * For Westend * Refactor buildExportXcm * Initial script for Penpal * Fix relay config * Change log level * Add Roc to registry * Fix building xcm * More refactoring * Revert irrelevant changes * Fix build XCM * Transfer WETH from penpal * Use batch call to initialize penpal * Fix building xcm * Revamp script * Revert irrelevant changes * Improve scripts * Profit estimation * Iterate commands and sum the gas cost * Remove codes * Rename to isRelayMessageProfitable * Configure gas cost * Resolve conflicts * Configure Gas cost for token transfer * Rename as OriginalMessage --------- Co-authored-by: Alistair Singh <[email protected]> Co-authored-by: claravanstaden <[email protected]>
1 parent 955a971 commit bac8c95

File tree

6 files changed

+74
-10
lines changed

6 files changed

+74
-10
lines changed

relayer/config/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ type EthereumConfig struct {
1919
GasLimit uint64 `mapstructure:"gas-limit"`
2020
HeartbeatSecs uint64 `mapstructure:"heartbeat-secs"`
2121
PendingTxTimeoutSecs uint64 `mapstructure:"pending-tx-timeout-secs"`
22+
// The gas cost of v2_submit excludes command execution, mainly covers the verification
23+
BaseDeliveryGas uint64 `mapstructure:"base-delivery-gas"`
24+
// The gas cost of unlock ERC20 token
25+
BaseUnlockGas uint64 `mapstructure:"base-unlock-gas"`
26+
// The gas cost of mint Polkadot native asset
27+
BaseMintGas uint64 `mapstructure:"base-mint-gas"`
2228
}
2329

2430
type OFACConfig struct {

relayer/relays/parachain/beefy-listener.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func (li *BeefyListener) doScan(ctx context.Context, beefyBlockNumber uint64) er
164164
return err
165165
}
166166
for _, task := range tasks {
167-
paraNonce := (*task.MessageProofs)[0].Message.Nonce
167+
paraNonce := (*task.MessageProofs)[0].Message.OriginalMessage.Nonce
168168
waitingPeriod := (uint64(paraNonce) + li.scheduleConfig.TotalRelayerCount - li.scheduleConfig.ID) % li.scheduleConfig.TotalRelayerCount
169169
err = li.waitAndSend(ctx, task, waitingPeriod)
170170
if err != nil {
@@ -326,7 +326,7 @@ func (li *BeefyListener) generateAndValidateParasHeadsMerkleProof(input *ProofIn
326326
}
327327

328328
func (li *BeefyListener) waitAndSend(ctx context.Context, task *Task, waitingPeriod uint64) error {
329-
paraNonce := (*task.MessageProofs)[0].Message.Nonce
329+
paraNonce := (*task.MessageProofs)[0].Message.OriginalMessage.Nonce
330330
log.Info(fmt.Sprintf("waiting for nonce %d to be picked up by another relayer", paraNonce))
331331
var cnt uint64
332332
var err error

relayer/relays/parachain/ethereum-writer.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,23 +100,65 @@ func (wr *EthereumWriter) WriteChannels(
100100
task *Task,
101101
) error {
102102
for _, proof := range *task.MessageProofs {
103-
err := wr.WriteChannel(ctx, options, &proof, task.ProofOutput)
103+
profitable, err := wr.isRelayMessageProfitable(ctx, &proof)
104104
if err != nil {
105-
return fmt.Errorf("write eth gateway: %w", err)
105+
return fmt.Errorf("check message profitable: %w", err)
106+
}
107+
if profitable {
108+
err = wr.WriteChannel(ctx, options, &proof, task.ProofOutput)
109+
if err != nil {
110+
return fmt.Errorf("write eth gateway: %w", err)
111+
}
106112
}
107113
}
108114

109115
return nil
110116
}
111117

118+
func (wr *EthereumWriter) commandGas(command *CommandWrapper) uint64 {
119+
var gas uint64
120+
switch command.Kind {
121+
// ERC20 transfer
122+
case 2:
123+
// BaseUnlockGas should cover most of the ERC20 token. Specific gas costs can be set per token if needed
124+
gas = wr.config.Ethereum.BaseUnlockGas
125+
// PNA transfer
126+
case 4:
127+
gas = wr.config.Ethereum.BaseMintGas
128+
default:
129+
gas = uint64(command.MaxDispatchGas)
130+
}
131+
return gas
132+
}
133+
134+
func (wr *EthereumWriter) isRelayMessageProfitable(ctx context.Context, proof *MessageProof) (bool, error) {
135+
var result bool
136+
gasPrice, err := wr.conn.Client().SuggestGasPrice(ctx)
137+
if err != nil {
138+
return result, err
139+
}
140+
var totalDispatchGas uint64
141+
commands := proof.Message.OriginalMessage.Commands
142+
for _, command := range commands {
143+
totalDispatchGas = totalDispatchGas + wr.commandGas(&command)
144+
}
145+
totalDispatchGas = totalDispatchGas + wr.config.Ethereum.BaseDeliveryGas
146+
gasFee := new(big.Int)
147+
gasFee.Mul(gasPrice, big.NewInt(int64(totalDispatchGas)))
148+
if proof.Message.Fee.Cmp(gasFee) >= 0 {
149+
return true, nil
150+
}
151+
return false, nil
152+
}
153+
112154
// Submit sends a SCALE-encoded message to an application deployed on the Ethereum network
113155
func (wr *EthereumWriter) WriteChannel(
114156
ctx context.Context,
115157
options *bind.TransactOpts,
116158
commitmentProof *MessageProof,
117159
proof *ProofOutput,
118160
) error {
119-
message := commitmentProof.Message.IntoInboundMessage()
161+
message := commitmentProof.Message.OriginalMessage.IntoInboundMessage()
120162

121163
convertedHeader, err := convertHeader(proof.Header)
122164
if err != nil {

relayer/relays/parachain/scanner.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ func (s *Scanner) filterTasks(
160160
}
161161

162162
var messages []OutboundQueueMessage
163+
var messagesWithFee []OutboundQueueMessageWithFee
163164
raw, err := s.paraConn.API().RPC.State.GetStorageRaw(messagesKey, blockHash)
164165
if err != nil {
165166
return nil, fmt.Errorf("fetch committed messages for block %v: %w", blockHash.Hex(), err)
@@ -185,6 +186,10 @@ func (s *Scanner) filterTasks(
185186
return nil, errors.New("banned address found")
186187
}
187188
messages = append(messages, m)
189+
var messageWithFee OutboundQueueMessageWithFee
190+
messageWithFee.OriginalMessage = m
191+
messageWithFee.Fee = order.Fee
192+
messagesWithFee = append(messagesWithFee, messageWithFee)
188193
}
189194

190195
// For the outbound channel, the commitment hash is the merkle root of the messages
@@ -194,7 +199,7 @@ func (s *Scanner) filterTasks(
194199
s.paraConn.API(),
195200
blockHash,
196201
*commitmentHash,
197-
messages,
202+
messagesWithFee,
198203
)
199204
if err != nil {
200205
return nil, err
@@ -303,7 +308,7 @@ func scanForOutboundQueueProofs(
303308
api *gsrpc.SubstrateAPI,
304309
blockHash types.Hash,
305310
commitmentHash types.H256,
306-
messages []OutboundQueueMessage,
311+
messages []OutboundQueueMessageWithFee,
307312
) (*struct {
308313
proofs []MessageProof
309314
}, error) {
@@ -340,7 +345,7 @@ func fetchMessageProof(
340345
api *gsrpc.SubstrateAPI,
341346
blockHash types.Hash,
342347
messageIndex uint64,
343-
message OutboundQueueMessage,
348+
message OutboundQueueMessageWithFee,
344349
) (MessageProof, error) {
345350
var proofHex string
346351
var proof MessageProof

relayer/relays/parachain/types.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package parachain
22

33
import (
4+
"math/big"
5+
46
"github.com/snowfork/go-substrate-rpc-client/v4/scale"
57
"github.com/snowfork/go-substrate-rpc-client/v4/types"
68
"github.com/snowfork/snowbridge/relayer/chain/relaychain"
@@ -89,6 +91,12 @@ type OutboundQueueMessage struct {
8991
Commands []CommandWrapper
9092
}
9193

94+
type OutboundQueueMessageWithFee struct {
95+
OriginalMessage OutboundQueueMessage
96+
// Attached fee in Ether
97+
Fee big.Int
98+
}
99+
92100
type CommandWrapper struct {
93101
Kind types.U8
94102
MaxDispatchGas types.U64
@@ -117,6 +125,6 @@ func (m OutboundQueueMessage) IntoInboundMessage() contracts.InboundMessage {
117125
}
118126

119127
type MessageProof struct {
120-
Message OutboundQueueMessage
128+
Message OutboundQueueMessageWithFee
121129
Proof MerkleProof
122130
}

web/packages/test/config/parachain-relay.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@
3434
"sink": {
3535
"ethereum": {
3636
"endpoint": "ws://127.0.0.1:8546",
37-
"gas-limit": null
37+
"gas-limit": null,
38+
"base-delivery-gas": 100000,
39+
"base-unlock-gas": 60000,
40+
"base-mint-gas": 60000
3841
},
3942
"contracts": {
4043
"Gateway": null

0 commit comments

Comments
 (0)