Skip to content

Commit b7e6277

Browse files
First version
1 parent 01ea993 commit b7e6277

File tree

10 files changed

+565
-2
lines changed

10 files changed

+565
-2
lines changed

.github/workflows/tests.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
on:
2+
push:
3+
4+
name: Tests
5+
jobs:
6+
golangci:
7+
name: Linter
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v2
11+
- name: golangci-lint
12+
uses: golangci/golangci-lint-action@v2.3.0
13+
with:
14+
version: v1.49.0
15+
args: --timeout=2m --go=1.19
16+
security:
17+
name: Security Analyze
18+
runs-on: ubuntu-latest
19+
permissions:
20+
actions: read
21+
contents: read
22+
security-events: write
23+
steps:
24+
- name: Checkout repository
25+
uses: actions/checkout@v2
26+
- name: Initialize CodeQL
27+
uses: github/codeql-action/init@v2
28+
with:
29+
languages: 'go'
30+
- name: Autobuild
31+
uses: github/codeql-action/autobuild@v2
32+
- name: Perform CodeQL Analysis
33+
uses: github/codeql-action/analyze@v2
34+

.golangci.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
linters:
2+
enable:
3+
- goconst
4+
- gocritic
5+
- gofmt
6+
- govet
7+
- prealloc
8+
- unconvert
9+
- unused
10+
- errcheck
11+
- ineffassign
12+
- containedctx
13+
- tenv
14+

README.md

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,54 @@
1-
# sourcify-api
2-
Golang wrapper over Sourcify API
1+
# Sourcify API
2+
Golang wrapper over Sourcify API. Library implements interaction with endpoints are described in the [Sourcisy docs](https://docs.sourcify.dev/docs/api/).
3+
4+
## Install
5+
6+
```go
7+
go get github.com/dipdup-net/sourcify-api
8+
```
9+
10+
## Usage
11+
12+
Create API structure
13+
14+
```go
15+
api := sourcify.NewAPI("https://sourcify.dev/")
16+
```
17+
18+
Call one of available methods
19+
20+
```go
21+
// GetFile - gets the file from the repository server
22+
func (api *API) GetFile(ctx context.Context, chainID, address, match, filename string) (*Metadata, error)
23+
24+
// GetFileTreeFullMatches - Returns repository URLs for every file in the source tree for the desired chain and address. Searches only for full matches.
25+
func (api *API) GetFileTreeFullMatches(ctx context.Context, chainID, address string) ([]string, error)
26+
27+
// GetFileTree - returns repository URLs for every file in the source tree for the desired chain and address. Searches for full and partial matches.
28+
func (api *API) GetFileTree(ctx context.Context, chainID, address string) (*FileTree, error)
29+
30+
// GetContractAddresses - Returns all verified contracts from the repository for the desired chain. Searches for full and partial matches.
31+
func (api *API) GetContractAddresses(ctx context.Context, chainID string) (*ContractAddresses, error)
32+
33+
// CheckByAddresses - Checks if contract with the desired chain and address is verified and in the repository. It will only search for perfect matches.
34+
func (api *API) CheckByAddresses(ctx context.Context, addresses []string, chainIds []string) ([]CheckStatus, error)
35+
36+
// CheckAllByAddresses - Checks if contract with the desired chain and address is verified and in the repository. It will search for both perfect and partial matches.
37+
func (api *API) CheckAllByAddresses(ctx context.Context, addresses []string, chainIds []string) ([]CheckAllStatus, error)
38+
39+
// GetFiles - Returns all verified sources from the repository for the desired contract address and chain, including metadata.json. Searches for full and partial matches.
40+
func (api *API) GetFiles(ctx context.Context, chainID, address string) ([]Sources, error)
41+
42+
// GetFilesFullMatch - Returns all verified sources from the repository for the desired contract address and chain, including metadata.json. Searches only for full matches.
43+
func (api *API) GetFilesFullMatch(ctx context.Context, chainID, address string) ([]File, error)
44+
45+
// Chains - Returns the chains (networks) added to the Sourcify. Contains both supported, unsupported, monitored, unmonitored chains.
46+
func (api *API) Chains(ctx context.Context) ([]Chain, error)
47+
48+
// Health - Ping the server and see if it is alive and ready for requests.
49+
func (api *API) Health(ctx context.Context) (string, error)
50+
```
51+
52+
Example of using library can be found [here](/example/main.go)
53+
54+
**Verification API is not realized yet in library**

api.go

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
package sourcify
2+
3+
import (
4+
"context"
5+
"io"
6+
"net/http"
7+
"net/url"
8+
"strings"
9+
10+
"github.com/pkg/errors"
11+
)
12+
13+
// API -
14+
type API struct {
15+
baseURL string
16+
}
17+
18+
// NewAPI -
19+
func NewAPI(baseURL string) *API {
20+
return &API{baseURL: baseURL}
21+
}
22+
23+
func getClient() *http.Client {
24+
t := http.DefaultTransport.(*http.Transport).Clone()
25+
t.MaxIdleConns = 10
26+
t.MaxConnsPerHost = 10
27+
t.MaxIdleConnsPerHost = 10
28+
29+
return &http.Client{
30+
Transport: t,
31+
}
32+
}
33+
34+
func (api *API) get(ctx context.Context, link *url.URL, output any) error {
35+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, link.String(), nil)
36+
if err != nil {
37+
return err
38+
}
39+
40+
response, err := getClient().Do(req)
41+
if err != nil {
42+
return err
43+
}
44+
defer response.Body.Close()
45+
46+
switch response.StatusCode {
47+
case http.StatusOK, http.StatusNotModified:
48+
return json.NewDecoder(response.Body).Decode(output)
49+
case http.StatusNotFound:
50+
return ErrNotFound
51+
default:
52+
data, err := io.ReadAll(response.Body)
53+
if err != nil {
54+
return err
55+
}
56+
return errors.Errorf("sourcify invalid status code %d: %s", response.StatusCode, data)
57+
}
58+
}
59+
60+
func (api *API) getString(ctx context.Context, link *url.URL) (string, error) {
61+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, link.String(), nil)
62+
if err != nil {
63+
return "", err
64+
}
65+
66+
response, err := getClient().Do(req)
67+
if err != nil {
68+
return "", err
69+
}
70+
defer response.Body.Close()
71+
72+
data, err := io.ReadAll(response.Body)
73+
if err != nil {
74+
return "", err
75+
}
76+
77+
switch response.StatusCode {
78+
case http.StatusOK, http.StatusNotModified:
79+
return string(data), nil
80+
default:
81+
82+
return string(data), errors.Errorf("sourcify invalid status code %d: %s", response.StatusCode, data)
83+
}
84+
}
85+
86+
// GetFile - gets the file from the repository server
87+
func (api *API) GetFile(ctx context.Context, chainID, address, match, filename string) (*Metadata, error) {
88+
link, err := url.Parse(api.baseURL)
89+
if err != nil {
90+
return nil, err
91+
}
92+
link = link.JoinPath("server/repository/contracts")
93+
link = link.JoinPath(match)
94+
link = link.JoinPath(chainID)
95+
link = link.JoinPath(address)
96+
link = link.JoinPath(filename)
97+
98+
var metadata Metadata
99+
err = api.get(ctx, link, &metadata)
100+
return &metadata, err
101+
}
102+
103+
// GetFileTreeFullMatches - Returns repository URLs for every file in the source tree for the desired chain and address. Searches only for full matches.
104+
func (api *API) GetFileTreeFullMatches(ctx context.Context, chainID, address string) ([]string, error) {
105+
link, err := url.Parse(api.baseURL)
106+
if err != nil {
107+
return nil, err
108+
}
109+
link = link.JoinPath("server/files/tree")
110+
link = link.JoinPath(chainID)
111+
link = link.JoinPath(address)
112+
113+
var fileTree []string
114+
err = api.get(ctx, link, &fileTree)
115+
return fileTree, err
116+
}
117+
118+
// GetFileTree - returns repository URLs for every file in the source tree for the desired chain and address. Searches for full and partial matches.
119+
func (api *API) GetFileTree(ctx context.Context, chainID, address string) (*FileTree, error) {
120+
link, err := url.Parse(api.baseURL)
121+
if err != nil {
122+
return nil, err
123+
}
124+
link = link.JoinPath("server/files/tree/any")
125+
link = link.JoinPath(chainID)
126+
link = link.JoinPath(address)
127+
128+
var fileTree FileTree
129+
err = api.get(ctx, link, &fileTree)
130+
return &fileTree, err
131+
}
132+
133+
// GetContractAddresses - Returns all verified contracts from the repository for the desired chain. Searches for full and partial matches.
134+
func (api *API) GetContractAddresses(ctx context.Context, chainID string) (*ContractAddresses, error) {
135+
link, err := url.Parse(api.baseURL)
136+
if err != nil {
137+
return nil, err
138+
}
139+
link = link.JoinPath("server/files/contracts")
140+
link = link.JoinPath(chainID)
141+
142+
var addresses ContractAddresses
143+
err = api.get(ctx, link, &addresses)
144+
return &addresses, err
145+
}
146+
147+
// CheckByAddresses - Checks if contract with the desired chain and address is verified and in the repository. It will only search for perfect matches.
148+
func (api *API) CheckByAddresses(ctx context.Context, addresses []string, chainIds []string) ([]CheckStatus, error) {
149+
link, err := url.Parse(api.baseURL)
150+
if err != nil {
151+
return nil, err
152+
}
153+
link = link.JoinPath("server/check-by-addresses")
154+
values := link.Query()
155+
if len(addresses) > 0 {
156+
values.Add("addresses", strings.Join(addresses, ","))
157+
}
158+
if len(chainIds) > 0 {
159+
values.Add("chainIds", strings.Join(chainIds, ","))
160+
}
161+
link.RawQuery = values.Encode()
162+
163+
var response []CheckStatus
164+
err = api.get(ctx, link, &response)
165+
return response, err
166+
}
167+
168+
// CheckAllByAddresses - Checks if contract with the desired chain and address is verified and in the repository. It will search for both perfect and partial matches.
169+
func (api *API) CheckAllByAddresses(ctx context.Context, addresses []string, chainIds []string) ([]CheckAllStatus, error) {
170+
link, err := url.Parse(api.baseURL)
171+
if err != nil {
172+
return nil, err
173+
}
174+
link = link.JoinPath("server/check-all-by-addresses")
175+
values := link.Query()
176+
if len(addresses) > 0 {
177+
values.Add("addresses", strings.Join(addresses, ","))
178+
}
179+
if len(chainIds) > 0 {
180+
values.Add("chainIds", strings.Join(chainIds, ","))
181+
}
182+
link.RawQuery = values.Encode()
183+
184+
var response []CheckAllStatus
185+
err = api.get(ctx, link, &response)
186+
return response, err
187+
}
188+
189+
// GetFiles - Returns all verified sources from the repository for the desired contract address and chain, including metadata.json. Searches for full and partial matches.
190+
func (api *API) GetFiles(ctx context.Context, chainID, address string) ([]Sources, error) {
191+
link, err := url.Parse(api.baseURL)
192+
if err != nil {
193+
return nil, err
194+
}
195+
link = link.JoinPath("server/files/any")
196+
link = link.JoinPath(chainID)
197+
link = link.JoinPath(address)
198+
199+
var response []Sources
200+
err = api.get(ctx, link, &response)
201+
return response, err
202+
}
203+
204+
// GetFilesFullMatch - Returns all verified sources from the repository for the desired contract address and chain, including metadata.json. Searches only for full matches.
205+
func (api *API) GetFilesFullMatch(ctx context.Context, chainID, address string) ([]File, error) {
206+
link, err := url.Parse(api.baseURL)
207+
if err != nil {
208+
return nil, err
209+
}
210+
link = link.JoinPath("server/files")
211+
link = link.JoinPath(chainID)
212+
link = link.JoinPath(address)
213+
214+
var response []File
215+
err = api.get(ctx, link, &response)
216+
return response, err
217+
}
218+
219+
// Chains - Returns the chains (networks) added to the Sourcify. Contains both supported, unsupported, monitored, unmonitored chains.
220+
func (api *API) Chains(ctx context.Context) ([]Chain, error) {
221+
link, err := url.Parse(api.baseURL)
222+
if err != nil {
223+
return nil, err
224+
}
225+
link = link.JoinPath("server/chains")
226+
var response []Chain
227+
err = api.get(ctx, link, &response)
228+
return response, err
229+
}
230+
231+
// Health - Ping the server and see if it is alive and ready for requests.
232+
func (api *API) Health(ctx context.Context) (string, error) {
233+
link, err := url.Parse(api.baseURL)
234+
if err != nil {
235+
return "", err
236+
}
237+
link = link.JoinPath("server/health")
238+
return api.getString(ctx, link)
239+
}

errors.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package sourcify
2+
3+
import "errors"
4+
5+
// errors
6+
var (
7+
ErrNotFound = errors.New("file not found")
8+
)

example/main.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"log"
6+
"time"
7+
8+
"github.com/dipdup-net/sourcify-api"
9+
)
10+
11+
func main() {
12+
api := sourcify.NewAPI("https://sourcify.dev/")
13+
14+
healthCtx, healthCancel := context.WithTimeout(context.Background(), time.Second)
15+
defer healthCancel()
16+
17+
healthResponse, err := api.Health(healthCtx)
18+
if err != nil {
19+
log.Panic(err)
20+
}
21+
log.Print(healthResponse)
22+
23+
getFileCtx, getFileCancel := context.WithTimeout(context.Background(), time.Second)
24+
defer getFileCancel()
25+
26+
file, err := api.GetFile(getFileCtx, "1", "0x3A7011e7E2b32C2B52f7De1294Ff35d6ff20310F", "full_match", "metadata.json")
27+
if err != nil {
28+
log.Panic(err)
29+
}
30+
log.Println(file)
31+
}

0 commit comments

Comments
 (0)