Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
aec0e03f
Unverified
Commit
aec0e03f
authored
Aug 23, 2023
by
mergify[bot]
Committed by
GitHub
Aug 23, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into dependabot/npm_and_yarn/markdownlint-0.30.0
parents
42a0d0af
0677bc6c
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
70 additions
and
90 deletions
+70
-90
.gitignore
indexer/.gitignore
+1
-2
api.go
indexer/api/api.go
+16
-10
api_test.go
indexer/api/api_test.go
+3
-3
cli.go
indexer/cmd/indexer/cli.go
+34
-45
main.go
indexer/cmd/indexer/main.go
+4
-7
config.go
indexer/config/config.go
+5
-8
indexer-refresh
indexer/indexer-refresh
+0
-0
disk.go
op-challenger/fault/disk.go
+3
-3
pnpm-lock.yaml
pnpm-lock.yaml
+4
-12
No files found.
indexer/.gitignore
View file @
aec0e03f
docker-compose.dev.yml
docker-compose.dev.yml
.env
.env
indexer
/indexer
indexer/api/api.go
View file @
aec0e03f
package
api
package
api
import
(
import
(
"context"
"fmt"
"fmt"
"net/http"
"net/http"
"github.com/ethereum-optimism/optimism/indexer/api/routes"
"github.com/ethereum-optimism/optimism/indexer/api/routes"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-service/httputil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5"
)
)
...
@@ -13,25 +15,29 @@ import (
...
@@ -13,25 +15,29 @@ import (
const
ethereumAddressRegex
=
`^0x[a-fA-F0-9]{40}$`
const
ethereumAddressRegex
=
`^0x[a-fA-F0-9]{40}$`
type
Api
struct
{
type
Api
struct
{
log
log
.
Logger
Router
*
chi
.
Mux
Router
*
chi
.
Mux
}
}
func
NewApi
(
bv
database
.
BridgeTransfersView
,
logger
log
.
Logger
)
*
Api
{
func
NewApi
(
logger
log
.
Logger
,
bv
database
.
BridgeTransfersView
)
*
Api
{
logger
.
Info
(
"Initializing API..."
)
r
:=
chi
.
NewRouter
()
r
:=
chi
.
NewRouter
()
h
:=
routes
.
NewRoutes
(
logger
,
bv
,
r
)
h
:=
routes
.
NewRoutes
(
logger
,
bv
,
r
)
api
:=
&
Api
{
Router
:
r
}
r
.
Get
(
"/healthz"
,
h
.
HealthzHandler
)
r
.
Get
(
"/healthz"
,
h
.
HealthzHandler
)
r
.
Get
(
fmt
.
Sprintf
(
"/api/v0/deposits/{address:%s}"
,
ethereumAddressRegex
),
h
.
L1DepositsHandler
)
r
.
Get
(
fmt
.
Sprintf
(
"/api/v0/deposits/{address:%s}"
,
ethereumAddressRegex
),
h
.
L1DepositsHandler
)
r
.
Get
(
fmt
.
Sprintf
(
"/api/v0/withdrawals/{address:%s}"
,
ethereumAddressRegex
),
h
.
L2WithdrawalsHandler
)
r
.
Get
(
fmt
.
Sprintf
(
"/api/v0/withdrawals/{address:%s}"
,
ethereumAddressRegex
),
h
.
L2WithdrawalsHandler
)
return
&
Api
{
log
:
logger
,
Router
:
r
}
return
api
}
}
func
(
a
*
Api
)
Listen
(
port
string
)
error
{
func
(
a
*
Api
)
Listen
(
ctx
context
.
Context
,
port
int
)
error
{
return
http
.
ListenAndServe
(
port
,
a
.
Router
)
a
.
log
.
Info
(
"starting api server"
,
"port"
,
port
)
server
:=
http
.
Server
{
Addr
:
fmt
.
Sprintf
(
":%d"
,
port
),
Handler
:
a
.
Router
}
err
:=
httputil
.
ListenAndServeContext
(
ctx
,
&
server
)
if
err
!=
nil
{
a
.
log
.
Error
(
"api server shutdown"
,
"err"
,
err
)
}
else
{
a
.
log
.
Info
(
"api server shutdown"
)
}
return
err
}
}
indexer/api/api_test.go
View file @
aec0e03f
...
@@ -77,7 +77,7 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.
...
@@ -77,7 +77,7 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.
}
}
func
TestHealthz
(
t
*
testing
.
T
)
{
func
TestHealthz
(
t
*
testing
.
T
)
{
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlInfo
)
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlInfo
)
api
:=
NewApi
(
&
MockBridgeTransfersView
{},
logger
)
api
:=
NewApi
(
logger
,
&
MockBridgeTransfersView
{}
)
request
,
err
:=
http
.
NewRequest
(
"GET"
,
"/healthz"
,
nil
)
request
,
err
:=
http
.
NewRequest
(
"GET"
,
"/healthz"
,
nil
)
assert
.
Nil
(
t
,
err
)
assert
.
Nil
(
t
,
err
)
...
@@ -89,7 +89,7 @@ func TestHealthz(t *testing.T) {
...
@@ -89,7 +89,7 @@ func TestHealthz(t *testing.T) {
func
TestL1BridgeDepositsHandler
(
t
*
testing
.
T
)
{
func
TestL1BridgeDepositsHandler
(
t
*
testing
.
T
)
{
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlInfo
)
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlInfo
)
api
:=
NewApi
(
&
MockBridgeTransfersView
{},
logger
)
api
:=
NewApi
(
logger
,
&
MockBridgeTransfersView
{}
)
request
,
err
:=
http
.
NewRequest
(
"GET"
,
fmt
.
Sprintf
(
"/api/v0/deposits/%s"
,
mockAddress
),
nil
)
request
,
err
:=
http
.
NewRequest
(
"GET"
,
fmt
.
Sprintf
(
"/api/v0/deposits/%s"
,
mockAddress
),
nil
)
assert
.
Nil
(
t
,
err
)
assert
.
Nil
(
t
,
err
)
...
@@ -101,7 +101,7 @@ func TestL1BridgeDepositsHandler(t *testing.T) {
...
@@ -101,7 +101,7 @@ func TestL1BridgeDepositsHandler(t *testing.T) {
func
TestL2BridgeWithdrawalsByAddressHandler
(
t
*
testing
.
T
)
{
func
TestL2BridgeWithdrawalsByAddressHandler
(
t
*
testing
.
T
)
{
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlInfo
)
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlInfo
)
api
:=
NewApi
(
&
MockBridgeTransfersView
{},
logger
)
api
:=
NewApi
(
logger
,
&
MockBridgeTransfersView
{}
)
request
,
err
:=
http
.
NewRequest
(
"GET"
,
fmt
.
Sprintf
(
"/api/v0/withdrawals/%s"
,
mockAddress
),
nil
)
request
,
err
:=
http
.
NewRequest
(
"GET"
,
fmt
.
Sprintf
(
"/api/v0/withdrawals/%s"
,
mockAddress
),
nil
)
assert
.
Nil
(
t
,
err
)
assert
.
Nil
(
t
,
err
)
...
...
indexer/c
li
/cli.go
→
indexer/c
md/indexer
/cli.go
View file @
aec0e03f
package
cli
package
main
import
(
import
(
"context"
"context"
"fmt"
"strconv"
"github.com/ethereum-optimism/optimism/indexer"
"github.com/ethereum-optimism/optimism/indexer"
"github.com/ethereum-optimism/optimism/indexer/api"
"github.com/ethereum-optimism/optimism/indexer/api"
...
@@ -16,26 +14,25 @@ import (
...
@@ -16,26 +14,25 @@ import (
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2"
)
)
type
Cli
struct
{
var
(
GitVersion
string
ConfigFlag
=
&
cli
.
StringFlag
{
GitCommit
string
Name
:
"config"
,
GitDate
string
Value
:
"./indexer.toml"
,
app
*
cli
.
App
Aliases
:
[]
string
{
"c"
},
Flags
[]
cli
.
Flag
Usage
:
"path to config file"
,
}
EnvVars
:
[]
string
{
"INDEXER_CONFIG"
},
}
)
func
runIndexer
(
ctx
*
cli
.
Context
)
error
{
func
runIndexer
(
ctx
*
cli
.
Context
)
error
{
logger
:=
log
.
NewLogger
(
log
.
ReadCLIConfig
(
ctx
))
logger
:=
log
.
NewLogger
(
log
.
ReadCLIConfig
(
ctx
))
cfg
,
err
:=
config
.
LoadConfig
(
logger
,
ctx
.
String
(
ConfigFlag
.
Name
))
configPath
:=
ctx
.
String
(
ConfigFlag
.
Name
)
cfg
,
err
:=
config
.
LoadConfig
(
logger
,
configPath
)
if
err
!=
nil
{
if
err
!=
nil
{
logger
.
Error
(
"failed to load config"
,
"err"
,
err
)
logger
.
Error
(
"failed to load config"
,
"err"
,
err
)
return
err
return
err
}
}
db
,
err
:=
database
.
NewDB
(
cfg
.
DB
)
db
,
err
:=
database
.
NewDB
(
cfg
.
DB
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
@@ -48,6 +45,7 @@ func runIndexer(ctx *cli.Context) error {
...
@@ -48,6 +45,7 @@ func runIndexer(ctx *cli.Context) error {
indexerCtx
,
indexerCancel
:=
context
.
WithCancel
(
context
.
Background
())
indexerCtx
,
indexerCancel
:=
context
.
WithCancel
(
context
.
Background
())
go
func
()
{
go
func
()
{
opio
.
BlockOnInterrupts
()
opio
.
BlockOnInterrupts
()
logger
.
Error
(
"caught interrupt, shutting down..."
)
indexerCancel
()
indexerCancel
()
}()
}()
...
@@ -56,47 +54,35 @@ func runIndexer(ctx *cli.Context) error {
...
@@ -56,47 +54,35 @@ func runIndexer(ctx *cli.Context) error {
func
runApi
(
ctx
*
cli
.
Context
)
error
{
func
runApi
(
ctx
*
cli
.
Context
)
error
{
logger
:=
log
.
NewLogger
(
log
.
ReadCLIConfig
(
ctx
))
logger
:=
log
.
NewLogger
(
log
.
ReadCLIConfig
(
ctx
))
cfg
,
err
:=
config
.
LoadConfig
(
logger
,
ctx
.
String
(
ConfigFlag
.
Name
))
configPath
:=
ctx
.
String
(
ConfigFlag
.
Name
)
cfg
,
err
:=
config
.
LoadConfig
(
logger
,
configPath
)
if
err
!=
nil
{
if
err
!=
nil
{
logger
.
Error
(
"failed to load config"
,
"err"
,
err
)
logger
.
Error
(
"failed to load config"
,
"err"
,
err
)
return
err
return
err
}
}
db
,
err
:=
database
.
NewDB
(
cfg
.
DB
)
db
,
err
:=
database
.
NewDB
(
cfg
.
DB
)
if
err
!=
nil
{
if
err
!=
nil
{
logger
.
Crit
(
"Failed to connect to database"
,
"err"
,
err
)
logger
.
Crit
(
"Failed to connect to database"
,
"err"
,
err
)
}
}
server
:=
api
.
NewApi
(
db
.
BridgeTransfers
,
logger
)
apiCtx
,
apiCancel
:=
context
.
WithCancel
(
context
.
Background
())
api
:=
api
.
NewApi
(
logger
,
db
.
BridgeTransfers
)
return
server
.
Listen
(
strconv
.
Itoa
(
cfg
.
API
.
Port
))
go
func
()
{
}
opio
.
BlockOnInterrupts
()
logger
.
Error
(
"caught interrupt, shutting down..."
)
var
(
apiCancel
()
ConfigFlag
=
&
cli
.
StringFlag
{
}()
Name
:
"config"
,
Value
:
"./indexer.toml"
,
Aliases
:
[]
string
{
"c"
},
Usage
:
"path to config file"
,
EnvVars
:
[]
string
{
"INDEXER_CONFIG"
},
}
)
// make a instance method on Cli called Run that runs cli
return
api
.
Listen
(
apiCtx
,
cfg
.
API
.
Port
)
// and returns an error
func
(
c
*
Cli
)
Run
(
args
[]
string
)
error
{
return
c
.
app
.
Run
(
args
)
}
}
func
NewCli
(
GitVersion
string
,
GitCommit
string
,
GitDate
string
)
*
Cli
{
func
newCli
(
GitCommit
string
,
GitDate
string
)
*
cli
.
App
{
flags
:=
[]
cli
.
Flag
{
ConfigFlag
}
flags
:=
[]
cli
.
Flag
{
ConfigFlag
}
flags
=
append
(
flags
,
log
.
CLIFlags
(
"INDEXER"
)
...
)
flags
=
append
(
flags
,
log
.
CLIFlags
(
"INDEXER"
)
...
)
app
:=
&
cli
.
App
{
return
&
cli
.
App
{
Version
:
fmt
.
Sprintf
(
"%s-%s"
,
GitVersion
,
params
.
VersionWithCommit
(
GitCommit
,
GitDate
)),
Version
:
params
.
VersionWithCommit
(
GitCommit
,
GitDate
),
Description
:
"An indexer of all optimism events with a serving api layer"
,
Description
:
"An indexer of all optimism events with a serving api layer"
,
EnableBashCompletion
:
true
,
Commands
:
[]
*
cli
.
Command
{
Commands
:
[]
*
cli
.
Command
{
{
{
Name
:
"api"
,
Name
:
"api"
,
...
@@ -110,11 +96,14 @@ func NewCli(GitVersion string, GitCommit string, GitDate string) *Cli {
...
@@ -110,11 +96,14 @@ func NewCli(GitVersion string, GitCommit string, GitDate string) *Cli {
Description
:
"Runs the indexing service"
,
Description
:
"Runs the indexing service"
,
Action
:
runIndexer
,
Action
:
runIndexer
,
},
},
{
Name
:
"version"
,
Description
:
"print version"
,
Action
:
func
(
ctx
*
cli
.
Context
)
error
{
cli
.
ShowVersion
(
ctx
)
return
nil
},
},
},
},
}
}
return
&
Cli
{
app
:
app
,
Flags
:
flags
,
}
}
}
indexer/cmd/indexer/main.go
View file @
aec0e03f
...
@@ -3,20 +3,17 @@ package main
...
@@ -3,20 +3,17 @@ package main
import
(
import
(
"os"
"os"
"github.com/ethereum-optimism/optimism/indexer/cli"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
)
)
var
(
var
(
GitVersion
=
""
GitCommit
=
""
GitCommit
=
""
GitDate
=
""
GitDate
=
""
)
)
func
main
()
{
func
main
()
{
app
:=
cli
.
NewCli
(
GitVersion
,
GitCommit
,
GitDate
)
app
:=
newCli
(
GitCommit
,
GitDate
)
if
err
:=
app
.
Run
(
os
.
Args
);
err
!=
nil
{
if
err
:=
app
.
Run
(
os
.
Args
);
err
!=
nil
{
log
.
Crit
(
"
Application failed"
,
"message
"
,
err
)
log
.
Crit
(
"
application failed"
,
"err
"
,
err
)
}
}
}
}
indexer/config/config.go
View file @
aec0e03f
...
@@ -99,20 +99,18 @@ type MetricsConfig struct {
...
@@ -99,20 +99,18 @@ type MetricsConfig struct {
// LoadConfig loads the `indexer.toml` config file from a given path
// LoadConfig loads the `indexer.toml` config file from a given path
func
LoadConfig
(
logger
geth_log
.
Logger
,
path
string
)
(
Config
,
error
)
{
func
LoadConfig
(
logger
geth_log
.
Logger
,
path
string
)
(
Config
,
error
)
{
logger
.
Info
(
"Loading config file"
,
"path"
,
path
)
logger
.
Debug
(
"loading config"
,
"path"
,
path
)
var
conf
Config
var
conf
Config
data
,
err
:=
os
.
ReadFile
(
path
)
data
,
err
:=
os
.
ReadFile
(
path
)
if
err
!=
nil
{
if
err
!=
nil
{
return
conf
,
err
return
conf
,
err
}
}
data
=
[]
byte
(
os
.
ExpandEnv
(
string
(
data
)))
data
=
[]
byte
(
os
.
ExpandEnv
(
string
(
data
)))
logger
.
Debug
(
"parsed config file"
,
"data"
,
string
(
data
))
logger
.
Debug
(
"Decoding config file"
,
"data"
,
string
(
data
))
if
_
,
err
:=
toml
.
Decode
(
string
(
data
),
&
conf
);
err
!=
nil
{
if
_
,
err
:=
toml
.
Decode
(
string
(
data
),
&
conf
);
err
!=
nil
{
logger
.
Info
(
"
Failed to decode config file"
,
"message
"
,
err
)
logger
.
Info
(
"
failed to decode config file"
,
"err
"
,
err
)
return
conf
,
err
return
conf
,
err
}
}
...
@@ -125,7 +123,6 @@ func LoadConfig(logger geth_log.Logger, path string) (Config, error) {
...
@@ -125,7 +123,6 @@ func LoadConfig(logger geth_log.Logger, path string) (Config, error) {
}
}
}
}
logger
.
Debug
(
"Loaded config file"
,
conf
)
logger
.
Info
(
"loaded config"
)
return
conf
,
nil
return
conf
,
nil
}
}
indexer/indexer-refresh
deleted
100755 → 0
View file @
42a0d0af
File deleted
op-challenger/fault/disk.go
View file @
aec0e03f
...
@@ -13,7 +13,7 @@ import (
...
@@ -13,7 +13,7 @@ import (
const
gameDirPrefix
=
"game-"
const
gameDirPrefix
=
"game-"
// diskManager coordinates
// diskManager coordinates
the storage of game data on disk.
type
diskManager
struct
{
type
diskManager
struct
{
datadir
string
datadir
string
}
}
...
@@ -42,11 +42,11 @@ func (d *diskManager) RemoveAllExcept(keep []common.Address) error {
...
@@ -42,11 +42,11 @@ func (d *diskManager) RemoveAllExcept(keep []common.Address) error {
name
:=
entry
.
Name
()[
len
(
gameDirPrefix
)
:
]
name
:=
entry
.
Name
()[
len
(
gameDirPrefix
)
:
]
addr
:=
common
.
HexToAddress
(
name
)
addr
:=
common
.
HexToAddress
(
name
)
if
addr
==
(
common
.
Address
{})
{
if
addr
==
(
common
.
Address
{})
{
//
Couldn't parse the directory name to an address so mustn't be a game directory
//
Ignore directories with non-address names.
continue
continue
}
}
if
slices
.
Contains
(
keep
,
addr
)
{
if
slices
.
Contains
(
keep
,
addr
)
{
//
We need to preserve this data
//
Preserve data for games we should keep.
continue
continue
}
}
if
err
:=
os
.
RemoveAll
(
filepath
.
Join
(
d
.
datadir
,
entry
.
Name
()));
err
!=
nil
{
if
err
:=
os
.
RemoveAll
(
filepath
.
Join
(
d
.
datadir
,
entry
.
Name
()));
err
!=
nil
{
...
...
pnpm-lock.yaml
View file @
aec0e03f
...
@@ -13,7 +13,7 @@ importers:
...
@@ -13,7 +13,7 @@ importers:
version
:
2.26.0
version
:
2.26.0
'
@codechecks/client'
:
'
@codechecks/client'
:
specifier
:
^0.1.11
specifier
:
^0.1.11
version
:
0.1.1
1
(typescript@5.1.6)
version
:
0.1.1
2
(typescript@5.1.6)
devDependencies
:
devDependencies
:
'
@babel/eslint-parser'
:
'
@babel/eslint-parser'
:
specifier
:
^7.18.2
specifier
:
^7.18.2
...
@@ -1204,8 +1204,8 @@ packages:
...
@@ -1204,8 +1204,8 @@ packages:
prettier
:
2.8.8
prettier
:
2.8.8
dev
:
false
dev
:
false
/@codechecks/client@0.1.1
1
(typescript@5.1.6)
:
/@codechecks/client@0.1.1
2
(typescript@5.1.6)
:
resolution
:
{
integrity
:
sha512-
dSIzHnGNcXxDZtnVQEXWQHXH2v9KrpnK4mDGDxdwSu3l00rOIVwJcttj0wzx0bC0Q6gs65VsQdZH4gkanLdXO
A==
}
resolution
:
{
integrity
:
sha512-
2GHHvhO3kaOyxFXxOaiznlY8ARmz33/p+WQdhc2y6wzWw5eOl2wSwg1eZxx3LsWlAnB963Y4bd1YjZcGIhKRz
A==
}
engines
:
{
node
:
'
>=6'
}
engines
:
{
node
:
'
>=6'
}
hasBin
:
true
hasBin
:
true
dependencies
:
dependencies
:
...
@@ -1221,7 +1221,7 @@ packages:
...
@@ -1221,7 +1221,7 @@ packages:
lodash
:
4.17.21
lodash
:
4.17.21
marked
:
0.7.0
marked
:
0.7.0
marked-terminal
:
3.3.0(marked@0.7.0)
marked-terminal
:
3.3.0(marked@0.7.0)
mkdirp
:
0.5.
5
mkdirp
:
0.5.
6
ms
:
2.1.3
ms
:
2.1.3
promise
:
8.1.0
promise
:
8.1.0
request
:
2.88.2
request
:
2.88.2
...
@@ -11916,19 +11916,11 @@ packages:
...
@@ -11916,19 +11916,11 @@ packages:
engines
:
{
node
:
'
>=
8.0.0'
}
engines
:
{
node
:
'
>=
8.0.0'
}
dev
:
false
dev
:
false
/mkdirp@0.5.5
:
resolution
:
{
integrity
:
sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
}
hasBin
:
true
dependencies
:
minimist
:
1.2.8
dev
:
false
/mkdirp@0.5.6
:
/mkdirp@0.5.6
:
resolution
:
{
integrity
:
sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
}
resolution
:
{
integrity
:
sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
}
hasBin
:
true
hasBin
:
true
dependencies
:
dependencies
:
minimist
:
1.2.8
minimist
:
1.2.8
dev
:
true
/mkdirp@1.0.4
:
/mkdirp@1.0.4
:
resolution
:
{
integrity
:
sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
}
resolution
:
{
integrity
:
sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment