This example demonstrates how to subscribe to solutions from the bloXroute gateway. Only a subscriber with the correct private key can receive the solution.
Name: IntentSolutions
IntentSolutions is a GRPC stream of new solutions that match the dappAddress of the subscription as they are propagated in the BDN.
IntentSolutionsRequest arguments:
Key
Description
Values
dappAddress
either ETH sender address or ETH address of DApp that should receive solutions
string
hash
Keccak256Hash of the dappAddress bytes
byte[]
signature
ECDSA signature of the hash signed by DApp private key
byte[]
IntentSolutionsReply fields (stream message):
Key
Description
Values
intentId
UUID of the intent
string
intentSolution
solution for the specific intent
byte[]
solutionId
UUID of the solution
string
Examples
packagemainimport ("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")// gatewayHost is the address of the gateway to which the subscription is being madeconst gatewayHost ="127.0.0.1:5002"// header is the authorization header of your bloXroute Accountconst header ="<YOUR-AUTHORIZATION-HEADER>"funcmain() {// 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: header}))if err !=nil { log.Fatalln("dial grpc", err) }// Create a client client := pb.NewGatewayClient(conn)// Subscribe to solutions stream, err := client.IntentSolutions(context.Background(), genIntentSolutionRequest())if err !=nil { log.Fatalln("subscribe to solutions", err) }for { fmt.Printf("listening for IntentSolutions from %s ...\n", gatewayHost)// Receive the solution from the stream until the stream is closed msg, err := stream.Recv()if err !=nil { log.Fatalln("receive from stream", err) } fmt.Println("got solution:") fmt.Println("- intent_id:", msg.IntentId) fmt.Println("- solution_id:", msg.SolutionId) fmt.Println("- solution:", hex.EncodeToString(msg.IntentSolution)) }}funcgenIntentSolutionRequest() *pb.IntentSolutionsRequest {// DApp private key used to prove the ownership of the DApp addressconst privateKeyHex ="7c4a8d09ca3762af61e59520943dc26494f8941e6e5b998321d3e84b8e4d3c16"// Convert the hex string to a private key privKey, err := crypto.HexToECDSA(privateKeyHex)if err !=nil { log.Fatalln("error converting hex to ECDSA", err) }// Extract the public key pubKey := privKey.PublicKey signerAddress := crypto.PubkeyToAddress(pubKey)// Hash the signer address hash := crypto.Keccak256Hash([]byte(signerAddress.String())).Bytes() // need a hash to sign, so we're hashing the payload here
sig, err := crypto.Sign(hash, privKey) // signing the hashif err !=nil { log.Fatalln("error signing with private key", err) }// Return the solution requestreturn&pb.IntentSolutionsRequest{ DappAddress: signerAddress.String(), Hash: hash, Signature: sig, }}// blxrCredentials is an implementation of PerRPCCredentialstypeblxrCredentialsstruct { authorization string}// GetRequestMetadata sets the authorization headerfunc (bc blxrCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {returnmap[string]string{"authorization": bc.authorization, }, nil}// RequireTransportSecurity is a method of the PerRPCCredentials interfacefunc (bc blxrCredentials) RequireTransportSecurity() bool {returnfalse}
packagemainimport ("encoding/json""fmt""log""net/http""github.com/ethereum/go-ethereum/crypto""github.com/gorilla/websocket")funcmain() { 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 :=generateIntentSolutionsRequest() 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 }}funcgenerateIntentSolutionsRequest() []byte {// Generate an ECDSA key pair using secp256k1 curve privKey, err := crypto.GenerateKey()if err !=nil { log.Fatalln("generate key", err) } pubKey := privKey.PublicKey // extract the public key dappAddress := crypto.PubkeyToAddress(pubKey) // the address of the public key hash := crypto.Keccak256Hash([]byte(dappAddress.String())).Bytes() // need a hash to sign, so we're hashing the payload here
sig, err := crypto.Sign(hash, privKey) // sign the hashif err !=nil { log.Fatalln("sign privKey", err) } m :=map[string]interface{}{"dapp_address": dappAddress.String(),"hash": hash,"signature": sig, } req, err := json.Marshal(m)if err !=nil { log.Fatalln("marshal", err) }return []byte(fmt.Sprintf(`{ "id": "2", "method": "subscribe", "params": [ "userIntentSolutionsFeed", %s]}`, req))}