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
b15e3ffe
Unverified
Commit
b15e3ffe
authored
Sep 20, 2023
by
OptimismBot
Committed by
GitHub
Sep 20, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #7320 from ethereum-optimism/wyatt/ufm/init-mm-metrics
Init Metamask Pass/Fail metrics
parents
54d1700f
bf4c4587
Changes
10
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
898 additions
and
439 deletions
+898
-439
docker-compose.yml
ufm-test-services/docker-compose.yml
+2
-1
metamask.json
ufm-test-services/grafana/dashboards/metamask.json
+182
-0
all.yml
ufm-test-services/grafana/provisioning/dashboards/all.yml
+9
-0
datasources.yml
...services/grafana/provisioning/datasources/datasources.yml
+0
-0
README.md
ufm-test-services/metamask/README.md
+30
-0
package.json
ufm-test-services/metamask/package.json
+5
-1
pnpm-lock.yaml
ufm-test-services/metamask/pnpm-lock.yaml
+525
-424
metamask.spec.ts
ufm-test-services/metamask/tests/metamask.spec.ts
+43
-12
prometheusUtils.ts
ufm-test-services/metamask/tests/prometheusUtils.ts
+101
-0
prometheus.yml
ufm-test-services/prometheus.yml
+1
-1
No files found.
ufm-test-services/docker-compose.yml
View file @
b15e3ffe
...
@@ -35,7 +35,8 @@ services:
...
@@ -35,7 +35,8 @@ services:
environment
:
environment
:
-
GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PWD}
-
GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PWD}
volumes
:
volumes
:
-
./datasources.yml:/etc/grafana/provisioning/datasources/datasources.yaml
-
./grafana/provisioning:/etc/grafana/provisioning
-
./grafana/dashboards:/var/lib/grafana/dashboards
security_opt
:
security_opt
:
-
"
no-new-privileges:true"
-
"
no-new-privileges:true"
...
...
ufm-test-services/grafana/dashboards/metamask.json
0 → 100644
View file @
b15e3ffe
{
"annotations"
:
{
"list"
:
[
{
"builtIn"
:
1
,
"datasource"
:
{
"type"
:
"grafana"
,
"uid"
:
"-- Grafana --"
},
"enable"
:
true
,
"hide"
:
true
,
"iconColor"
:
"rgba(0, 211, 255, 1)"
,
"name"
:
"Annotations & Alerts"
,
"type"
:
"dashboard"
}
]
},
"editable"
:
true
,
"fiscalYearStartMonth"
:
0
,
"graphTooltip"
:
0
,
"links"
:
[],
"liveNow"
:
false
,
"panels"
:
[
{
"datasource"
:
{
"type"
:
"prometheus"
,
"uid"
:
"PBFA97CFB590B2093"
},
"fieldConfig"
:
{
"defaults"
:
{
"color"
:
{
"mode"
:
"thresholds"
},
"mappings"
:
[],
"thresholds"
:
{
"mode"
:
"absolute"
,
"steps"
:
[
{
"color"
:
"red"
,
"value"
:
null
},
{
"color"
:
"yellow"
,
"value"
:
1
},
{
"color"
:
"green"
,
"value"
:
4
}
]
}
},
"overrides"
:
[]
},
"gridPos"
:
{
"h"
:
5
,
"w"
:
6
,
"x"
:
0
,
"y"
:
0
},
"id"
:
1
,
"options"
:
{
"orientation"
:
"auto"
,
"reduceOptions"
:
{
"calcs"
:
[
"lastNotNull"
],
"fields"
:
""
,
"values"
:
false
},
"showThresholdLabels"
:
false
,
"showThresholdMarkers"
:
true
},
"pluginVersion"
:
"10.1.2"
,
"targets"
:
[
{
"datasource"
:
{
"type"
:
"prometheus"
,
"uid"
:
"PBFA97CFB590B2093"
},
"disableTextWrap"
:
false
,
"editorMode"
:
"builder"
,
"expr"
:
"metamask_tx_success"
,
"fullMetaSearch"
:
false
,
"includeNullMetadata"
:
true
,
"instant"
:
false
,
"legendFormat"
:
"__auto"
,
"range"
:
true
,
"refId"
:
"A"
,
"useBackend"
:
false
}
],
"title"
:
"Number of Successful Transaction Since Last Failure"
,
"type"
:
"gauge"
},
{
"datasource"
:
{
"type"
:
"prometheus"
,
"uid"
:
"PBFA97CFB590B2093"
},
"fieldConfig"
:
{
"defaults"
:
{
"color"
:
{
"mode"
:
"thresholds"
},
"mappings"
:
[],
"thresholds"
:
{
"mode"
:
"absolute"
,
"steps"
:
[
{
"color"
:
"text"
,
"value"
:
null
},
{
"color"
:
"red"
,
"value"
:
1
}
]
}
},
"overrides"
:
[]
},
"gridPos"
:
{
"h"
:
5
,
"w"
:
6
,
"x"
:
6
,
"y"
:
0
},
"id"
:
2
,
"options"
:
{
"orientation"
:
"auto"
,
"reduceOptions"
:
{
"calcs"
:
[
"lastNotNull"
],
"fields"
:
""
,
"values"
:
false
},
"showThresholdLabels"
:
false
,
"showThresholdMarkers"
:
true
},
"pluginVersion"
:
"10.1.2"
,
"targets"
:
[
{
"datasource"
:
{
"type"
:
"prometheus"
,
"uid"
:
"PBFA97CFB590B2093"
},
"disableTextWrap"
:
false
,
"editorMode"
:
"builder"
,
"expr"
:
"metamask_tx_failure"
,
"fullMetaSearch"
:
false
,
"includeNullMetadata"
:
true
,
"instant"
:
false
,
"legendFormat"
:
"__auto"
,
"range"
:
true
,
"refId"
:
"A"
,
"useBackend"
:
false
}
],
"title"
:
"Number of Failed Transactions Since Last Success"
,
"type"
:
"gauge"
}
],
"refresh"
:
"5s"
,
"schemaVersion"
:
38
,
"style"
:
"dark"
,
"tags"
:
[],
"templating"
:
{
"list"
:
[]
},
"time"
:
{
"from"
:
"now-6h"
,
"to"
:
"now"
},
"timepicker"
:
{},
"timezone"
:
""
,
"title"
:
"UFM: Metamask"
,
"uid"
:
"f66f7076-c724-4f81-8ff9-58d6d99f2716"
,
"version"
:
1
,
"weekStart"
:
""
}
ufm-test-services/grafana/provisioning/dashboards/all.yml
0 → 100644
View file @
b15e3ffe
apiVersion
:
1
providers
:
-
name
:
'
default'
orgId
:
1
folder
:
'
'
type
:
file
disableDeletion
:
false
options
:
path
:
/var/lib/grafana/dashboards
ufm-test-services/
date
sources.yml
→
ufm-test-services/
grafana/provisioning/datasources/data
sources.yml
View file @
b15e3ffe
File moved
ufm-test-services/metamask/README.md
0 → 100644
View file @
b15e3ffe
# User Facing Monitoring - Metamask Tests
## Running Locally
### Building Docker Image
```
bash
docker build
-t
ufm-test-service-metamask
.
```
### Running the Docker Container on MacOS
The following steps were taken from
[
here
](
https://www.oddbird.net/2022/11/30/headed-playwright-in-docker/#macos
)
Apple’s operating system doesn’t include a built-in XServer, but we can use
[
XQuartz
](
https://www.xquartz.org/
)
to provide one:
1.
Install XQuartz:
`brew install --cask xquartz``
2. Open XQuartz, go to `
Preferences -> Security
`, and check `
Allow connections from network clients
`
3. Restart your computer (restarting XQuartz might not be enough)
4. Start XQuartz by executing `
xhost +localhost
` in your terminal
5. Open Docker Desktop and edit settings to give access to `
/tmp/.X11-unix
` in `
Preferences -> Resources -> File sharing
`
Once XQuartz is running with the right permissions, you can populate the environment variable and socket Docker args:
`
``
bash
docker run --rm -it
\
-e DISPLAY=host.docker.internal:0
\
-v /tmp/.X11-unix:/tmp/.X11-unix
\
ufm-test-service-metamask
```
ufm-test-services/metamask/package.json
View file @
b15e3ffe
...
@@ -20,10 +20,14 @@
...
@@ -20,10 +20,14 @@
"devDependencies"
:
{
"devDependencies"
:
{
"@metamask/test-dapp"
:
"^7.1.0"
,
"@metamask/test-dapp"
:
"^7.1.0"
,
"@playwright/test"
:
"1.37.1"
,
"@playwright/test"
:
"1.37.1"
,
"@synthetixio/synpress"
:
"3.7.2-beta.
5
"
,
"@synthetixio/synpress"
:
"3.7.2-beta.
7
"
,
"dotenv"
:
"^16.3.1"
,
"dotenv"
:
"^16.3.1"
,
"static-server"
:
"^2.2.1"
,
"static-server"
:
"^2.2.1"
,
"typescript"
:
"^5.1.6"
,
"typescript"
:
"^5.1.6"
,
"viem"
:
"^1.10.8"
"viem"
:
"^1.10.8"
},
"dependencies"
:
{
"prom-client"
:
"^14.2.0"
,
"zod"
:
"^3.22.2"
}
}
}
}
ufm-test-services/metamask/pnpm-lock.yaml
View file @
b15e3ffe
This diff is collapsed.
Click to expand it.
ufm-test-services/metamask/tests/metamask.spec.ts
View file @
b15e3ffe
import
'
dotenv/config
'
import
'
dotenv/config
'
import
{
z
}
from
'
zod
'
import
metamask
from
'
@synthetixio/synpress/commands/metamask.js
'
import
metamask
from
'
@synthetixio/synpress/commands/metamask.js
'
import
{
expect
,
test
,
type
Page
}
from
'
@playwright/test
'
import
{
expect
,
test
,
type
Page
}
from
'
@playwright/test
'
import
{
mnemonicToAccount
,
privateKeyToAccount
}
from
'
viem/accounts
'
import
{
mnemonicToAccount
,
privateKeyToAccount
}
from
'
viem/accounts
'
import
{
testWithSynpress
}
from
'
./testWithSynpressUtil
'
import
{
testWithSynpress
}
from
'
./testWithSynpressUtil
'
import
{
getMetamaskTxCounterValue
,
incrementMetamaskTxCounter
,
setMetamaskTxCounter
,
}
from
'
./prometheusUtils
'
const
env
=
z
.
object
({
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
:
z
.
string
(),
OP_GOERLI_RPC_URL
:
z
.
string
().
url
(),
METAMASK_DAPP_URL
:
z
.
string
().
url
()
}).
parse
(
process
.
env
)
const
expectedSender
=
const
expectedSender
=
process
.
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
?.
startsWith
(
'
0x
'
)
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
?.
startsWith
(
'
0x
'
)
?
privateKeyToAccount
(
?
privateKeyToAccount
(
process
.
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
as
`0x
${
string
}
`
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
as
`0x
${
string
}
`
).
address
.
toLowerCase
()
).
address
.
toLowerCase
()
:
mnemonicToAccount
(
:
mnemonicToAccount
(
process
.
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
as
string
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
as
string
).
address
.
toLowerCase
()
).
address
.
toLowerCase
()
const
expectedRecipient
=
'
0x8fcfbe8953433fd1f2e8375ee99057833e4e1e9e
'
const
expectedRecipient
=
'
0x8fcfbe8953433fd1f2e8375ee99057833e4e1e9e
'
...
@@ -35,7 +47,7 @@ testWithSynpress('Add OP Goerli network', async () => {
...
@@ -35,7 +47,7 @@ testWithSynpress('Add OP Goerli network', async () => {
name
:
'
op-goerli
'
,
name
:
'
op-goerli
'
,
rpcUrls
:
{
rpcUrls
:
{
default
:
{
default
:
{
http
:
[
process
.
env
.
OP_GOERLI_RPC_URL
],
http
:
[
env
.
OP_GOERLI_RPC_URL
],
},
},
},
},
id
:
'
420
'
,
id
:
'
420
'
,
...
@@ -49,13 +61,26 @@ testWithSynpress('Add OP Goerli network', async () => {
...
@@ -49,13 +61,26 @@ testWithSynpress('Add OP Goerli network', async () => {
},
},
})
})
try
{
await
expect
(
sharedPage
.
locator
(
'
#chainId
'
)).
toHaveText
(
expectedChainId
)
await
expect
(
sharedPage
.
locator
(
'
#chainId
'
)).
toHaveText
(
expectedChainId
)
}
catch
(
error
)
{
await
setMetamaskTxCounter
(
true
,
0
)
await
incrementMetamaskTxCounter
(
false
)
throw
error
}
})
})
test
(
`Connect wallet with
${
expectedSender
}
`
,
async
()
=>
{
test
(
`Connect wallet with
${
expectedSender
}
`
,
async
()
=>
{
await
sharedPage
.
click
(
'
#connectButton
'
)
await
sharedPage
.
click
(
'
#connectButton
'
)
await
metamask
.
acceptAccess
()
await
metamask
.
acceptAccess
()
try
{
await
expect
(
sharedPage
.
locator
(
'
#accounts
'
)).
toHaveText
(
expectedSender
)
await
expect
(
sharedPage
.
locator
(
'
#accounts
'
)).
toHaveText
(
expectedSender
)
}
catch
(
error
)
{
await
setMetamaskTxCounter
(
true
,
0
)
await
incrementMetamaskTxCounter
(
false
)
throw
error
}
})
})
test
(
'
Send an EIP-1559 transaciton and verfiy success
'
,
async
()
=>
{
test
(
'
Send an EIP-1559 transaciton and verfiy success
'
,
async
()
=>
{
...
@@ -76,17 +101,14 @@ test('Send an EIP-1559 transaciton and verfiy success', async () => {
...
@@ -76,17 +101,14 @@ test('Send an EIP-1559 transaciton and verfiy success', async () => {
})
})
})
})
await
metamask
.
confirmTransaction
()
await
metamask
.
confirmTransaction
AndWaitForMining
()
const
txHash
=
await
txHashPromise
const
txHash
=
await
txHashPromise
// Waiting for Infura (Metamask given provider) to index our transaction
await
sharedPage
.
waitForTimeout
(
10
_000
)
// Metamask test dApp allows us access to the Metamask RPC provider via loading this URL.
// Metamask test dApp allows us access to the Metamask RPC provider via loading this URL.
// The RPC reponse will be populated onto the page that's loaded.
// The RPC reponse will be populated onto the page that's loaded.
// More info here: https://github.com/MetaMask/test-dapp/tree/main#usage
// More info here: https://github.com/MetaMask/test-dapp/tree/main#usage
await
sharedPage
.
goto
(
await
sharedPage
.
goto
(
`
${
process
.
env
.
METAMASK_DAPP_URL
}
/request.html?method=eth_getTransactionReceipt¶ms=["
${
txHash
}
"]`
`
${
env
.
METAMASK_DAPP_URL
}
/request.html?method=eth_getTransactionReceipt¶ms=["
${
txHash
}
"]`
)
)
// Waiting for RPC response to be populated on the page
// Waiting for RPC response to be populated on the page
...
@@ -98,5 +120,14 @@ test('Send an EIP-1559 transaciton and verfiy success', async () => {
...
@@ -98,5 +120,14 @@ test('Send an EIP-1559 transaciton and verfiy success', async () => {
''
''
)
)
)
)
try
{
expect
(
transaction
.
status
).
toBe
(
'
0x1
'
)
expect
(
transaction
.
status
).
toBe
(
'
0x1
'
)
await
setMetamaskTxCounter
(
false
,
0
)
await
incrementMetamaskTxCounter
(
true
)
}
catch
(
error
)
{
await
setMetamaskTxCounter
(
true
,
0
)
await
incrementMetamaskTxCounter
(
false
)
throw
error
}
})
})
ufm-test-services/metamask/tests/prometheusUtils.ts
0 → 100644
View file @
b15e3ffe
import
'
dotenv/config
'
import
{
z
}
from
'
zod
'
import
{
Counter
,
Pushgateway
}
from
'
prom-client
'
const
env
=
z
.
object
({
PROMETHEUS_SERVER_URL
:
z
.
string
().
url
(),
PROMETHEUS_PUSHGATEWAY_URL
:
z
.
string
().
url
(),
})
.
parse
(
process
.
env
)
const
txSuccessMetricName
=
'
metamask_tx_success
'
const
txFailureMetricName
=
'
metamask_tx_failuree
'
const
txSuccessCounter
=
new
Counter
({
name
:
txSuccessMetricName
,
help
:
'
A counter signifying the number of successful transactions sent with Metamask since last failure
'
,
})
const
txFailureCounter
=
new
Counter
({
name
:
txFailureMetricName
,
help
:
'
A counter signifying the number of failed transactions sent with Metamask since last successful transaction
'
,
})
export
const
getMetamaskTxCounterValue
=
async
(
isSuccess
:
boolean
)
=>
{
const
metricName
=
isSuccess
?
txSuccessMetricName
:
txFailureMetricName
const
prometheusMetricQuery
=
`
${
env
.
PROMETHEUS_SERVER_URL
}
/api/v1/query?query=
${
metricName
}
`
const
response
=
await
fetch
(
prometheusMetricQuery
)
if
(
!
response
.
ok
)
{
console
.
error
(
response
.
status
)
console
.
error
(
response
.
statusText
)
throw
new
Error
(
`Failed to fetch metric from:
${
prometheusMetricQuery
}
`
)
}
// The following is an example of the expect response from prometheusMetricQuery
// for response.json().data.result[0]:
// [
// {
// metric: {
// __name__: 'metamask_tx_success',
// exported_job: 'metamask_tx_count',
// instance: 'pushgateway:9091',
// job: 'pushgateway'
// },
// value: [ 1695250414.474, '0' ]
// }
// ]
try
{
const
responseJson
=
z
.
object
({
data
:
z
.
object
({
result
:
z
.
array
(
z
.
object
({
value
:
z
.
tuple
([
z
.
number
(),
z
.
number
().
or
(
z
.
string
().
transform
((
value
)
=>
parseInt
(
value
))),
]),
})
),
}),
})
.
parse
(
await
response
.
json
())
return
responseJson
.
data
.
result
[
0
].
value
[
1
]
}
catch
(
error
)
{
if
(
error
.
message
===
"
Cannot read properties of undefined (reading 'value')
"
)
{
console
.
warn
(
`No data found for metric
${
metricName
}
in Prometheus`
)
return
undefined
}
throw
error
}
}
export
const
setMetamaskTxCounter
=
async
(
isSuccess
:
boolean
,
valueToSetTo
:
number
)
=>
{
const
metricName
=
isSuccess
?
txSuccessMetricName
:
txFailureMetricName
const
txCounter
=
isSuccess
?
txSuccessCounter
:
txFailureCounter
txCounter
.
reset
()
console
.
log
(
`Setting
${
metricName
}
to
${
valueToSetTo
}
`
)
txCounter
.
inc
(
valueToSetTo
)
const
pushGateway
=
new
Pushgateway
(
env
.
PROMETHEUS_PUSHGATEWAY_URL
)
await
pushGateway
.
pushAdd
({
jobName
:
'
metamask_tx_count
'
})
}
export
const
incrementMetamaskTxCounter
=
async
(
isSuccess
:
boolean
)
=>
{
const
metricName
=
isSuccess
?
txSuccessMetricName
:
txFailureMetricName
const
currentMetricValue
=
(
await
getMetamaskTxCounterValue
(
true
))
??
0
console
.
log
(
`Current value of
${
metricName
}
is
${
currentMetricValue
}
, incrementing to
${
currentMetricValue
+
1
}
`
)
await
setMetamaskTxCounter
(
isSuccess
,
currentMetricValue
+
1
)
}
ufm-test-services/prometheus.yml
View file @
b15e3ffe
global
:
global
:
scrape_interval
:
5
s
scrape_interval
:
2
s
scrape_configs
:
scrape_configs
:
-
job_name
:
'
pushgateway'
-
job_name
:
'
pushgateway'
...
...
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