Skip to content

Commit d495d01

Browse files
KMontag42khadni
andauthored
update the svr searcher documentation and add code snippets in ts/go (#2542)
* update the svr searcher documentation and add code snippets in ts/go * nit editorial update * nit numbering * nit editorial update * nit editorial update * nit editorial update * update feed addresses * Implement dynamic fetching of SVR feed addresses --------- Co-authored-by: Karim <[email protected]>
1 parent 4f9ad52 commit d495d01

File tree

8 files changed

+402
-31
lines changed

8 files changed

+402
-31
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
type MevBundle struct {
2+
Hash common.Hash
3+
Tx []byte
4+
CanRevert bool
5+
}
6+
7+
type Inclusion struct {
8+
Block *big.Int
9+
MaxBlock *big.Int
10+
}
11+
12+
type BundleParams struct {
13+
Body []MevBundle
14+
inclusion Inclusion
15+
}
16+
17+
// ... create and sign your transaction
18+
tx := types.NewTransaction(...)
19+
signedTx, err := types.SignTx(tx, ...)
20+
txBytes := signedTx.MarshalBinary()
21+
22+
bundle := []MevBundle {
23+
{
24+
Hash: event.EventHash,
25+
},
26+
{
27+
Tx: &txBytes,
28+
CanRevert: false,
29+
},
30+
}
31+
32+
params := BundleParams {
33+
Body: bundle,
34+
Inclusion: Inclusion{blockNumber, blockNumber+1},
35+
}
36+
37+
byteParams, err := json.Marshal(params)
38+
body := []byte(fmt.Sprintf(`{"jsonrpc":"2.0","method":"mev_sendBundle","params":["%s"], "id":1}`, string(byteParams)))
39+
bodyReader := bytes.NewReader(body)
40+
postReq, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://relay.flashbots.net/", bodyReader)
41+
42+
hashedBody := crypto.Keccak256Hash(body).Hex()
43+
signedMessage, err := crypto.Sign(accounts.TextHash([]byte(hashedBody)), privKey)
44+
postReq.Header.Add("X-Flashbots-signature", address.String()+":"+hexutil.Encode(signedMessage))
45+
resp, err := http.DefaultClient.Do(postReq)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
async function sendBackrunBundle(pendingTx: IPendingTransaction) {
2+
const currentBlock = await provider.getBlockNumber()
3+
const signedBackrunTx = await buildDummyBackrunTx(pendingTx)
4+
const bundleParams: BundleParams = {
5+
inclusion: {
6+
block: currentBlock + 1,
7+
maxBlock: currentBlock + TARGET_BUNDLE_BLOCK_DELAY,
8+
},
9+
body: [{ hash: pendingTx.hash }, { tx: signedBackrunTx, canRevert: false }],
10+
}
11+
12+
const sendBundleResult = await mevShareClient.sendBundle(bundleParams)
13+
return sendBundleResult
14+
}
15+
16+
async function buildDummyBackrunTx(pendingTx: IPendingTransaction) {
17+
const backrunTx = { data: {}, maxFeePerGas: 22000, maxPriorityFeePerGas: 22000 }
18+
const signedBackrunTx = await executorWallet.signTransaction(backrunTx)
19+
return signedBackrunTx
20+
}
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
decoded, err := hex.DecodeString(strings.TrimPrefix(strings.ToLower(tx.CallData), "0x"))
2+
contractAddress := "0x" + strings.ToLower(strings.TrimLeft(hex.EncodeToString(decoded[4:36]), "0"))
3+
4+
ff := strings.ToLower(strings.TrimLeft(hex.EncodeToString(decoded[100:104]), "0"))
5+
if strings.Compare(ff, "ba0cb29e") != 0 {
6+
return nil, fmt.Errorf("event doesn't invoke svr method: ba0cb29e, instead: %s", ff)
7+
}
8+
9+
transmitSecondaryElems := make(map[string]interface{})
10+
err := getTransmissionTypes().UnpackIntoMap(transmitSecondaryElems, decoded[104:])
11+
12+
report, exists := transmitSecondaryElems["report"]
13+
return MedianFromReport(report.([]byte))
14+
15+
func MedianFromReport(report []byte) (*big.Int, error) {
16+
reportElems := map[string]interface{}{}
17+
err := getReportTypes().UnpackIntoMap(reportElems, report)
18+
observationsIface, ok := reportElems["observations"]
19+
observations, ok := observationsIface.([]*big.Int)
20+
median := observations[len(observations)/2]
21+
return median, nil
22+
}
23+
24+
func getReportTypes() abi.Arguments {
25+
return abi.Arguments([]abi.Argument{
26+
{Name: "observationsTimestamp", Type: mustNewType("uint32")},
27+
{Name: "rawObservers", Type: mustNewType("bytes32")},
28+
{Name: "observations", Type: mustNewType("int192[]")},
29+
{Name: "juelsPerFeeCoin", Type: mustNewType("int192")},
30+
})
31+
}
32+
33+
func getTransmissionTypes() abi.Arguments {
34+
return abi.Arguments([]abi.Argument{
35+
{Name: "reportContext", Type: mustNewType("bytes32[3]")},
36+
{Name: "report", Type: mustNewType("bytes")},
37+
{Name: "rs", Type: mustNewType("bytes32[]")},
38+
{Name: "ss", Type: mustNewType("bytes32[]")},
39+
{Name: "rawVs", Type: mustNewType("bytes32")},
40+
})
41+
}
42+
43+
func mustNewType(t string) abi.Type {
44+
result, err := abi.NewType(t, "", nil)
45+
return result
46+
}
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
const decodeInterface = new ethers.Interface(EXAMPLE_ABI)
2+
3+
function decodeForwarderCall(callData: string) {
4+
const decodedForward = decodeInterface.decodeFunctionData("forward", callData)
5+
return { to: decodedForward[0], callData: decodedForward[1] }
6+
}
7+
8+
function decodeTransmitSecondary(callData: string) {
9+
try {
10+
const decodedTransmitSecondary = decodeInterface.decodeFunctionData("transmitSecondary", callData)
11+
return { report: decodedTransmitSecondary[1] }
12+
} catch (error) {
13+
console.error("not transmit secondary call", error)
14+
return {}
15+
}
16+
}
17+
18+
function decodeReport(reportRaw: string) {
19+
const decodedReport = ethers.AbiCoder.defaultAbiCoder().decode(["uint32", "bytes32", "int192[]", "int192"], reportRaw)
20+
21+
return { observations: decodedReport[2] }
22+
}
23+
24+
function updatedPrice(observations: bigint[]) {
25+
const medianObservation = observations[Math.floor(observations.length / 2)]
26+
return medianObservation
27+
}
28+
29+
async function processTransaction(pendingTx: IPendingTransaction) {
30+
if (pendingTx.functionSelector !== CHAINLINK_PRICE_FEED_FUNCTION_SELECTOR) {
31+
return null
32+
}
33+
34+
const { to, callData } = decodeForwarderCall(pendingTx.callData)
35+
const { report } = decodeTransmitSecondary(callData)
36+
if (report) {
37+
const { observations } = decodeReport(report)
38+
const newPrice = updatedPrice(observations)
39+
}
40+
41+
// calculate health factors for affected users ...
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[
2+
{
3+
"type": "function",
4+
"name": "forward",
5+
"inputs": [
6+
{ "name": "to", "type": "address" },
7+
{ "name": "callData", "type": "bytes" }
8+
],
9+
"outputs": [],
10+
"stateMutability": "nonpayable"
11+
},
12+
{
13+
"type": "function",
14+
"name": "transmitSecondary",
15+
"inputs": [
16+
{ "name": "reportContext", "type": "bytes32[3]", "internalType": "bytes32[3]" },
17+
{ "name": "report", "type": "bytes", "internalType": "bytes" },
18+
{ "name": "rs", "type": "bytes32[]", "internalType": "bytes32[]" },
19+
{ "name": "ss", "type": "bytes32[]", "internalType": "bytes32[]" },
20+
{ "name": "rawVs", "type": "bytes32", "internalType": "bytes32" }
21+
],
22+
"outputs": [],
23+
"stateMutability": "nonpayable"
24+
}
25+
]
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
type MevShareEvent struct {
2+
Hash common.Hash `json:"hash"`
3+
Txs []PendingTransaction `json:"txs"`
4+
}
5+
6+
type PendingTransaction struct {
7+
To common.Address `json:"to"`
8+
FunctionSelector string `json:"functionSelector"`
9+
CallData string `json:"callData"`
10+
}
11+
12+
req, err := http.NewRequestWithContext(
13+
ctx,
14+
"GET",
15+
"https://mev-share.flashbots.net",
16+
nil,
17+
)
18+
19+
// ... read the event data from the response
20+
21+
var event MevShareEvent
22+
err := json.Unmarshal(responseData, &event)
23+
24+
// ... check if tx is calling the forward method
25+
if strings.Compare(
26+
strings.ToLower(tx.FunctionSelector),
27+
"0x6fadcf72",
28+
) != 0 {
29+
continue
30+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import MevShareClient, { IPendingBundle, IPendingTransaction } from "@flashbots/mev-share-client"
2+
3+
const mevShareClient = MevShareClient.useEthereumMainnet(authSigner)
4+
5+
const txHandler = mevShareClient.on("transaction", async (tx: IPendingTransaction) => {
6+
if (tx.functionSelector === "6fadc72") {
7+
// Do something with the pending tx here.
8+
}
9+
})
10+
11+
// call before your program terminates:
12+
txHandler.close()
13+
bundleHandler.close()

0 commit comments

Comments
 (0)