Categorygithub.com/linkpoolio/bridges
modulepackage
0.0.0-20200226172010-aa6f132d477e
Repository: https://github.com/linkpoolio/bridges.git
Documentation: pkg.go.dev

# README

Build Status codecov Go Report Card

Bridges is a Chainlink adaptor framework, lowering the barrier of entry for anyone to create their own:

  • A tested hardened library that removes the need to build your own HTTP server, allowing you to just focus on adapter requirements.
  • Simple interface to allow you to build an adapter that confides to Chainlink schema.
  • Kept up to date with any changes, meaning no extra work for existing adapters to support new schema changes or features.
  • Supports running in serverless environments such as AWS Lambda & GCP functions with minimal effort.

Contents

  1. Code Examples
  2. Running in AWS Lambda
  3. Running in GCP Functions
  4. Example Implementations

Code Examples

  • CryptoCompare: Simplest example.
  • API Aggregator: Aggregates multiple endpoints using mean/median/mode.
  • Wolfram Alpha: Short answers API, non-JSON, uses string splitting.
  • Gas Station: Single answer response, no authentication.
  • Asset Price: A more complex example that aggregates crypto asset prices from multiple exchanges by weighted volume.

Running in Docker

After implementing your bridge, if you'd like to run it in Docker, you can reference the Dockerfiles in examples to then use as a template for your own Dockerfile.

Running in AWS Lambda

After you've completed implementing your bridge, you can then test it in AWS Lambda. To do so:

  1. Build the executable:
    GO111MODULE=on GOOS=linux GOARCH=amd64 go build -o bridge
    
  2. Add the file to a ZIP archive:
    zip bridge.zip ./bridge
    
  3. Upload the the zip file into AWS and then use bridge as the handler.
  4. Set the LAMBDA environment variable to true in AWS for the adaptor to be compatible with Lambda.

Running in GCP Functions

Due to the difference in running Go within GCP Functions, it requires specific considerations for it be supported within your bridge:

  • Bridge implementation cannot be within the main package
  • An extra Handler function within your implementation:
    func Handler(w http.ResponseWriter, r *http.Request) {
        bridges.NewServer(&Example{}).Handler(w, r)
    }
    
  • A go.mod and go.sum within the sub-package that contains the Handler function

For an example implementation for GCP Functions, view the asset price adapter.

You can then use the gcloud CLI tool to deploy it, for example:

gcloud functions deploy bridge --runtime go111 --entry-point Handler --trigger-http

Example Implementations

Basic

Bridges works by providing a simple interface to confide to. The interface contains two functions, Run and Opts. The Run function is called on each HTTP request, Opts is called on start-up. Below is a very basic implementation that returns the value as passed in by Chainlink, set back as newValue in the response:

package main

import (
	"github.com/linkpoolio/bridges"
)

type MyAdapter struct{}

func (ma *MyAdapter) Run(h *bridge.Helper) (interface{}, error) {
	return map[string]string{"newValue": h.GetParam("value")}, nil
}

func (ma *MyAdapter) Opts() *bridge.Opts {
	return &bridge.Opts{
		Name:   "MyAdapter",
		Lambda: true,
	}
}

func main() {
	bridge.NewServer(&MyAdaptor{}).Start(8080)
}

Unauthenticated HTTP Calls

The bridges library provides a helper object that intends to make actions like performing HTTP calls simpler, removing the need to write extensive error handling or the need to have the knowledge of Go's in-built http libraries.

For example, this below implementation uses the HTTPCall function to make a simple unauthenticated call to ETH Gas Station:

package main

import (
	"github.com/linkpoolio/bridges"
)

type GasStation struct{}

func (gs *GasStation) Run(h *bridges.Helper) (interface{}, error) {
	obj := make(map[string]interface{})
	err := h.HTTPCall(
		http.MethodGet,
		"https://ethgasstation.info/json/ethgasAPI.json",
		&obj,
	)
	return obj, err
}

func (gs *GasStation) Opts() *bridges.Opts {
	return &bridges.Opts{
		Name:   "GasStation",
		Lambda: true,
	}
}

func main() {
	bridges.NewServer(&GasStation{}).Start(8080)
}

Authenticated HTTP Calls

Bridges also provides an interface to support authentication methods when making HTTP requests to external sources. By default, bridges supports authentication via HTTP headers or GET parameters.

Below is a modified version of the WolframAlpha adapter, showing authentication setting the appid header from the APP_ID environment variable:

package main

import (
	"errors"
    "fmt"
	"github.com/linkpoolio/bridges"
	"net/http"
	"os"
	"strings"
)

type WolframAlpha struct{}

func (cc *WolframAlpha) Run(h *bridges.Helper) (interface{}, error) {
	b, err := h.HTTPCallRawWithOpts(
		http.MethodGet,
		"https://api.wolframalpha.com/v1/result",
		bridges.CallOpts{
			Auth: bridges.NewAuth(bridges.AuthParam, "appid", os.Getenv("APP_ID")),
			Query: map[string]interface{}{
				"i": h.GetParam("query"),
			},
		},
	)
	return fmt.Sprint(b), err
}

func (cc *WolframAlpha) Opts() *bridges.Opts {
	return &bridges.Opts{
		Name:   "WolframAlpha",
		Lambda: true,
	}
}

func main() {
	bridges.NewServer(&WolframAlpha{}).Start(8080)
}

Contributing

We welcome all contributors, please raise any issues for any feature request, issue or suggestion you may have.

# Packages

No description provided by the author

# Functions

NewAuth returns a pointer of an Auth implementation based on the type that was passed in.
No description provided by the author
NewServer returns a new Server with the bridges in a map indexed by its path.
ParseInterface parse a bytes slice into a JSON object.
ParseInterface attempts to coerce the input interface and parse it into a JSON object.

# Constants

No description provided by the author
No description provided by the author

# Structs

CallOpts are the options given into a http call method.
Header is the Auth implementation that requires a header to be set.
Helper is given to the receiving bridge to use on run, giving the bridge the visibility to the input parameters from the node request and having simple functions for making http calls.
Based on https://github.com/smartcontractkit/chainlink/blob/master/core/store/models/common.go#L128.
Opts is the options for each bridge.
Query is the Auth implementation that requires GET param set.
Result represents a Chainlink JobRun.
Server holds pointers to the bridges indexed by their paths and the bridge to be mounted in Lambda.

# Interfaces

Auth is the generic interface for how the client passes in their API key for authentication.
Bridge is the interface that can be implemented for custom Chainlink bridges.