Submit solution
This example demonstrates how to submit a solution to an intent using the bloXroute Gateway gRPC API.
Name: SubmitIntentSolution
SubmitIntentSolution
is a GRPC method that propagates the solution to the BDN.
SubmitIntentSolutionRequest
arguments:
Key
Description
Values
solverAddress
ETH address of the intent solver
string
intentId
UUID of the intent to solve
string
intentSolution
solution payload for the intent
byte[]
hash
Keccak256Hash of the solution payload
byte[]
signature
ECDSA signature of the hash
byte[]
SubmitIntentSolutionReply
fields:
Key
Description
Values
solutionId
UUID of the solution
string
first_seen
timestamp when the solution was first seen in BDN.
google.protobuf.Timestamp
Examples
package main
import (
"context"
"encoding/hex"
"fmt"
"log"
pb "github.com/bloXroute-Labs/gateway/v2/protobuf"
"github.com/ethereum/go-ethereum/crypto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
// intentID is the ID of the intent to which the solution is being submitted.
// replace with the actual intent ID.
const intentID = "<INTENT-ID>"
// gatewayHost is the address of the gateway to which the solution is being submitted
const gatewayHost = "127.0.0.1:5001"
// authHeader is the authorization header of your BloxRoute Account
const authHeader = "<YOUR-AUTHORIZATION-HEADER>"
func main() {
// this will use localhost CA to verify the certificate
creds := credentials.NewClientTLSFromCert(nil, "")
// Dial the gateway
conn, err := grpc.Dial(gatewayHost,
grpc.WithTransportCredentials(creds),
grpc.WithPerRPCCredentials(blxrCredentials{authorization: authHeader}))
if err != nil {
log.Fatalln("dial grpc", err)
}
// Create a client
client := pb.NewGatewayClient(conn)
// Generate the solution
solution := genIntentSolution(intentID)
// Submit the solution
resp, err := client.SubmitIntentSolution(context.Background(), solution)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("done submitting the solution to %s\nsolver address: %s\nsolution id: %s\n", gatewayHost, solution.SolverAddress, resp.SolutionId)
}
func genIntentSolution(intentID string) *pb.SubmitIntentSolutionRequest {
// The sample private key of the solver
solverPrivateKeyHex := "2b36f5c0317c13e6326e9d2e2ae39badec9d030ba44bc889318e4fa5412ad342"
// The solution is to be submitted as a byte slice.
// This is a sample solution, and you should replace it with your solution.
solutionBytes := []byte(`{"From":"0xe34f7d4a8b5b9e5d5f4d6392b4c328e2d1a2bd9f","To":"0xa24dcbb439bfb3c37ad6cce12c92427b0358cb4ee","Value":0,"Gas":950000,"MaxFeePerGas":12000000000,"Nonce":42,"Deadline":4850000,"Solver":"0x4e2f4cb6458c1c08b55a6c362f4abf818279ee02","Control":"0x1de2276df50bd8bceefb353c1a5520bc0fecda98","UserOpHash":[52,201,174,29,78,191,148,66,213,171,223,60,212,140,236,131,174,50,90,5,197,241,205,163,228,26,79,98,140,156,72,182],"BidToken":"0x0000000000000000000000000000000000000001","BidAmount":150000000000000,"Data":"SRJ0xQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAJ6tEZqZDFXPFuIOdvTCoIJnhSWMAAAAAAAAAAAAAAAAfmECoXVr1vx0XYvklva3cQgH5hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKqHvuU4AAAAAAAAAAAAAAAAAA//mXZ4LUbMBWMNH266sYsjJNaxQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI4byb8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Signature":"QKx9m3wSuO2bx2QOMyGweMaaXozxq5p6rc9VkpJg6pYRAKFq+diLTG8AEeYoscYpfSTGWCGHBJuQLvV/Q/fvuBw="}`)
// Decode the hex string to a byte slice
solverPrivateKeyBytes, err := hex.DecodeString(solverPrivateKeyHex)
if err != nil {
log.Fatalf("invalid hex string: %v", err)
}
// Use the Ethereum crypto package to create an ECDSA private key
solverPrivateKey, err := crypto.ToECDSA(solverPrivateKeyBytes)
if err != nil {
log.Fatalf("failed to create private key: %v", err)
}
// Sign the solution with the private key
solutionHash := crypto.Keccak256Hash(solutionBytes).Bytes() // need a hash to sign, so we're hashing the payload here
solutionSig, err := crypto.Sign(solutionHash, solverPrivateKey) // signing the hash
if err != nil {
log.Fatalln("could not sign the message", err)
}
// Extract the address of the solver
solverAddress := crypto.PubkeyToAddress(solverPrivateKey.PublicKey).String()
// Return the solution request
return &pb.SubmitIntentSolutionRequest{
SolverAddress: solverAddress,
IntentId: intentID,
IntentSolution: solutionBytes,
Hash: solutionHash,
Signature: solutionSig,
}
}
// blxrCredentials is a struct that implements the PerRPCCredentials interface
type blxrCredentials struct {
authorization string
}
// GetRequestMetadata is a method of the PerRPCCredentials interface
func (bc blxrCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"authorization": bc.authorization,
}, nil
}
// RequireTransportSecurity is a method of the PerRPCCredentials interface
func (bc blxrCredentials) RequireTransportSecurity() bool {
return false
}
package main
import (
"encoding/hex"
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/ethereum/go-ethereum/crypto"
"github.com/gorilla/websocket"
)
// intentID is the ID of the intent to which the solution is being submitted.
// replace with the actual intent ID.
const intentID = "<INTENT-ID>"
func main() {
dialer := websocket.DefaultDialer
// Add the following lines if you work with IP instead of DNS
// tlsConfig := &tls.Config{
// Certificates: []tls.Certificate{cert},
// InsecureSkipVerify: true,
// }
// dialer.TLSClientConfig = tlsConfig
req := genIntentSolution(intentID)
wsSubscriber, _, err := dialer.Dial("wss://virginia.eth.blxrbdn.com/ws", http.Header{"Authorization": []string{"YOUR-AUTHORIZATION-HEADER"}})
if err != nil {
fmt.Println(err)
return
}
err = wsSubscriber.WriteMessage(websocket.TextMessage, req)
if err != nil {
fmt.Println(err)
return
}
for {
_, nextNotification, err := wsSubscriber.ReadMessage()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(nextNotification)) // or process it generally
}
}
func genIntentSolution(intentID string) []byte {
// The sample private key of the solver
solverPrivateKeyHex := "2b36f5c0317c13e6326e9d2e2ae39badec9d030ba44bc889318e4fa5412ad342"
// The solution is to be submitted as a byte slice.
// This is a sample solution, and you should replace it with your solution.
solutionBytes := []byte(`{"From":"0xe34f7d4a8b5b9e5d5f4d6392b4c328e2d1a2bd9f","To":"0xa24dcbb439bfb3c37ad6cce12c92427b0358cb4ee","Value":0,"Gas":950000,"MaxFeePerGas":12000000000,"Nonce":42,"Deadline":4850000,"Solver":"0x4e2f4cb6458c1c08b55a6c362f4abf818279ee02","Control":"0x1de2276df50bd8bceefb353c1a5520bc0fecda98","UserOpHash":[52,201,174,29,78,191,148,66,213,171,223,60,212,140,236,131,174,50,90,5,197,241,205,163,228,26,79,98,140,156,72,182],"BidToken":"0x0000000000000000000000000000000000000001","BidAmount":150000000000000,"Data":"SRJ0xQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAJ6tEZqZDFXPFuIOdvTCoIJnhSWMAAAAAAAAAAAAAAAAfmECoXVr1vx0XYvklva3cQgH5hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKqHvuU4AAAAAAAAAAAAAAAAAA//mXZ4LUbMBWMNH266sYsjJNaxQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI4byb8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Signature":"QKx9m3wSuO2bx2QOMyGweMaaXozxq5p6rc9VkpJg6pYRAKFq+diLTG8AEeYoscYpfSTGWCGHBJuQLvV/Q/fvuBw="}`)
// Decode the hex string to a byte slice
solverPrivateKeyBytes, err := hex.DecodeString(solverPrivateKeyHex)
if err != nil {
log.Fatalf("invalid hex string: %v", err)
}
// Use the Ethereum crypto package to create an ECDSA private key
solverPrivateKey, err := crypto.ToECDSA(solverPrivateKeyBytes)
if err != nil {
log.Fatalf("failed to create private key: %v", err)
}
// Sign the solution with the private key
solutionHash := crypto.Keccak256Hash(solutionBytes).Bytes() // need a hash to sign, so we're hashing the payload here
solutionSig, err := crypto.Sign(solutionHash, solverPrivateKey) // signing the hash
if err != nil {
log.Fatalln("could not sign the message", err)
}
// Extract the address of the solver
solverAddress := crypto.PubkeyToAddress(solverPrivateKey.PublicKey).String()
m := map[string]interface{}{
"solver_address": solverAddress,
"intent_id": intentID,
"intent_solution": solutionBytes,
"hash": solutionHash,
"signature": solutionSig,
}
req, err := json.Marshal(m)
if err != nil {
log.Fatalln("marshal", err)
}
return []byte(fmt.Sprintf(`{"id": "1", "method": "blxr_submit_intent_solution", "params": %s}`, req))
}
Last updated