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
68553660
Unverified
Commit
68553660
authored
Oct 03, 2023
by
Wyatt Barnes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Init Metamask Fee Estimation Tracking
parent
25c436c4
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
386 additions
and
30 deletions
+386
-30
metamask.json
ufm-test-services/grafana/dashboards/metamask.json
+225
-2
metamask.spec.ts
ufm-test-services/metamask/tests/metamask.spec.ts
+92
-24
prometheusUtils.ts
ufm-test-services/metamask/tests/prometheusUtils.ts
+69
-4
No files found.
ufm-test-services/grafana/dashboards/metamask.json
View file @
68553660
...
@@ -18,7 +18,6 @@
...
@@ -18,7 +18,6 @@
"editable"
:
true
,
"editable"
:
true
,
"fiscalYearStartMonth"
:
0
,
"fiscalYearStartMonth"
:
0
,
"graphTooltip"
:
0
,
"graphTooltip"
:
0
,
"id"
:
1
,
"links"
:
[],
"links"
:
[],
"liveNow"
:
false
,
"liveNow"
:
false
,
"panels"
:
[
"panels"
:
[
...
@@ -36,6 +35,7 @@
...
@@ -36,6 +35,7 @@
"axisCenteredZero"
:
true
,
"axisCenteredZero"
:
true
,
"axisColorMode"
:
"series"
,
"axisColorMode"
:
"series"
,
"axisGridShow"
:
false
,
"axisGridShow"
:
false
,
"axisLabel"
:
""
,
"axisPlacement"
:
"auto"
,
"axisPlacement"
:
"auto"
,
"barAlignment"
:
0
,
"barAlignment"
:
0
,
"drawStyle"
:
"line"
,
"drawStyle"
:
"line"
,
...
@@ -134,6 +134,229 @@
...
@@ -134,6 +134,229 @@
],
],
"title"
:
"Self Transferring on OP Goerli (positive number = success, negative = failures)"
,
"title"
:
"Self Transferring on OP Goerli (positive number = success, negative = failures)"
,
"type"
:
"timeseries"
"type"
:
"timeseries"
},
{
"datasource"
:
{
"type"
:
"prometheus"
,
"uid"
:
"PBFA97CFB590B2093"
},
"fieldConfig"
:
{
"defaults"
:
{
"color"
:
{
"mode"
:
"fixed"
},
"custom"
:
{
"axisCenteredZero"
:
false
,
"axisColorMode"
:
"text"
,
"axisGridShow"
:
false
,
"axisLabel"
:
""
,
"axisPlacement"
:
"auto"
,
"axisSoftMin"
:
0
,
"barAlignment"
:
0
,
"drawStyle"
:
"line"
,
"fillOpacity"
:
0
,
"gradientMode"
:
"none"
,
"hideFrom"
:
{
"legend"
:
false
,
"tooltip"
:
false
,
"viz"
:
false
},
"insertNulls"
:
false
,
"lineInterpolation"
:
"stepAfter"
,
"lineWidth"
:
1
,
"pointSize"
:
5
,
"scaleDistribution"
:
{
"type"
:
"linear"
},
"showPoints"
:
"never"
,
"spanNulls"
:
false
,
"stacking"
:
{
"group"
:
"A"
,
"mode"
:
"none"
},
"thresholdsStyle"
:
{
"mode"
:
"off"
}
},
"decimals"
:
0
,
"mappings"
:
[],
"thresholds"
:
{
"mode"
:
"absolute"
,
"steps"
:
[
{
"color"
:
"text"
,
"value"
:
null
}
]
}
},
"overrides"
:
[
{
"matcher"
:
{
"id"
:
"byName"
,
"options"
:
"metamask_self_send_fee_estimation_low"
},
"properties"
:
[
{
"id"
:
"displayName"
,
"value"
:
"Low (Slow 🐢)"
},
{
"id"
:
"color"
,
"value"
:
{
"fixedColor"
:
"green"
,
"mode"
:
"fixed"
}
}
]
},
{
"matcher"
:
{
"id"
:
"byName"
,
"options"
:
"metamask_self_send_fee_estimation_medium"
},
"properties"
:
[
{
"id"
:
"displayName"
,
"value"
:
"Medium (Market 🦊)"
},
{
"id"
:
"color"
,
"value"
:
{
"fixedColor"
:
"orange"
,
"mode"
:
"fixed"
}
}
]
},
{
"matcher"
:
{
"id"
:
"byName"
,
"options"
:
"metamask_self_send_fee_estimation_high"
},
"properties"
:
[
{
"id"
:
"displayName"
,
"value"
:
"High (Aggressive 🦍)"
}
]
},
{
"matcher"
:
{
"id"
:
"byName"
,
"options"
:
"metamask_self_send_fee_estimation_actual"
},
"properties"
:
[
{
"id"
:
"displayName"
,
"value"
:
"Actual transaction fee"
},
{
"id"
:
"color"
,
"value"
:
{
"fixedColor"
:
"blue"
,
"mode"
:
"fixed"
}
}
]
}
]
},
"gridPos"
:
{
"h"
:
11
,
"w"
:
24
,
"x"
:
0
,
"y"
:
8
},
"id"
:
2
,
"options"
:
{
"legend"
:
{
"calcs"
:
[
"last"
],
"displayMode"
:
"table"
,
"placement"
:
"bottom"
,
"showLegend"
:
true
},
"timezone"
:
[
"browser"
],
"tooltip"
:
{
"mode"
:
"single"
,
"sort"
:
"none"
}
},
"targets"
:
[
{
"datasource"
:
{
"type"
:
"prometheus"
,
"uid"
:
"PBFA97CFB590B2093"
},
"disableTextWrap"
:
false
,
"editorMode"
:
"builder"
,
"expr"
:
"metamask_self_send_fee_estimation_low"
,
"fullMetaSearch"
:
false
,
"includeNullMetadata"
:
true
,
"instant"
:
false
,
"legendFormat"
:
"__auto"
,
"range"
:
true
,
"refId"
:
"A"
,
"useBackend"
:
false
},
{
"datasource"
:
{
"type"
:
"prometheus"
,
"uid"
:
"PBFA97CFB590B2093"
},
"disableTextWrap"
:
false
,
"editorMode"
:
"builder"
,
"expr"
:
"metamask_self_send_fee_estimation_medium"
,
"fullMetaSearch"
:
false
,
"hide"
:
false
,
"includeNullMetadata"
:
true
,
"instant"
:
false
,
"legendFormat"
:
"__auto"
,
"range"
:
true
,
"refId"
:
"B"
,
"useBackend"
:
false
},
{
"datasource"
:
{
"type"
:
"prometheus"
,
"uid"
:
"PBFA97CFB590B2093"
},
"disableTextWrap"
:
false
,
"editorMode"
:
"builder"
,
"expr"
:
"metamask_self_send_fee_estimation_high"
,
"fullMetaSearch"
:
false
,
"hide"
:
false
,
"includeNullMetadata"
:
true
,
"instant"
:
false
,
"legendFormat"
:
"__auto"
,
"range"
:
true
,
"refId"
:
"C"
,
"useBackend"
:
false
},
{
"datasource"
:
{
"type"
:
"prometheus"
,
"uid"
:
"PBFA97CFB590B2093"
},
"disableTextWrap"
:
false
,
"editorMode"
:
"builder"
,
"expr"
:
"metamask_self_send_fee_estimation_actual"
,
"fullMetaSearch"
:
false
,
"hide"
:
false
,
"includeNullMetadata"
:
true
,
"instant"
:
false
,
"legendFormat"
:
"__auto"
,
"range"
:
true
,
"refId"
:
"D"
,
"useBackend"
:
false
}
],
"title"
:
"Self Transferring on OP Goerli Fee Estimates"
,
"type"
:
"timeseries"
}
}
],
],
"refresh"
:
"5s"
,
"refresh"
:
"5s"
,
...
@@ -144,7 +367,7 @@
...
@@ -144,7 +367,7 @@
"list"
:
[]
"list"
:
[]
},
},
"time"
:
{
"time"
:
{
"from"
:
"now-3
0m
"
,
"from"
:
"now-3
h
"
,
"to"
:
"now"
"to"
:
"now"
},
},
"timepicker"
:
{},
"timepicker"
:
{},
...
...
ufm-test-services/metamask/tests/metamask.spec.ts
View file @
68553660
import
'
dotenv/config
'
import
'
dotenv/config
'
import
{
z
}
from
'
zod
'
import
{
z
}
from
'
zod
'
import
metamask
from
'
@synthetixio/synpress/commands/metamask.js
'
import
metamask
from
'
@synthetixio/synpress/commands/metamask.js
'
import
synpressPlaywright
from
'
@synthetixio/synpress/commands/playwright.js
'
import
{
confirmPageElements
}
from
'
@synthetixio/synpress/pages/metamask/notification-page.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
{
formatGwei
,
parseGwei
}
from
'
viem
'
import
{
testWithSynpress
}
from
'
./testWithSynpressUtil
'
import
{
testWithSynpress
}
from
'
./testWithSynpressUtil
'
import
{
import
{
incrementSelfSendTxGauge
,
incrementSelfSendTxGauge
,
setFeeEstimationGauge
,
}
from
'
./prometheusUtils
'
}
from
'
./prometheusUtils
'
const
env
=
z
.
object
({
const
env
=
z
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
:
z
.
string
(),
.
object
({
METAMASK_OP_GOERLI_RPC_URL
:
z
.
string
().
url
(),
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
:
z
.
string
(),
METAMASK_DAPP_URL
:
z
.
string
().
url
()
METAMASK_OP_GOERLI_RPC_URL
:
z
.
string
().
url
(),
}).
parse
(
process
.
env
)
METAMASK_DAPP_URL
:
z
.
string
().
url
(),
})
const
expectedSender
=
.
parse
(
process
.
env
)
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
?.
startsWith
(
'
0x
'
)
?
privateKeyToAccount
(
const
expectedSender
=
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
?.
startsWith
(
'
0x
'
)
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
as
`0x
${
string
}
`
?
privateKeyToAccount
(
).
address
.
toLowerCase
()
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
as
`0x
${
string
}
`
:
mnemonicToAccount
(
).
address
.
toLowerCase
()
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
as
string
:
mnemonicToAccount
(
).
address
.
toLowerCase
()
env
.
METAMASK_SECRET_WORDS_OR_PRIVATEKEY
as
string
).
address
.
toLowerCase
()
const
expectedRecipient
=
expectedSender
const
expectedRecipient
=
expectedSender
let
sharedPage
:
Page
let
sharedPage
:
Page
...
@@ -119,25 +124,51 @@ test('Send an EIP-1559 transaction and verify success', async () => {
...
@@ -119,25 +124,51 @@ test('Send an EIP-1559 transaction and verify success', async () => {
})
})
})
})
const
notificationPage
=
await
synpressPlaywright
.
switchToMetamaskNotification
()
const
lowFeeEstimate
=
await
getFeeEstimateInGwei
(
confirmPageElements
.
gasOptionLowButton
,
'
Low
'
,
notificationPage
)
const
highFeeEstimate
=
await
getFeeEstimateInGwei
(
confirmPageElements
.
gasOptionHighButton
,
'
Aggressive
'
,
notificationPage
)
// Medium needs to be last because that's the gas option we want to submit the tx with
const
mediumFeeEstimate
=
await
getFeeEstimateInGwei
(
confirmPageElements
.
gasOptionMediumButton
,
'
Market
'
,
notificationPage
)
await
metamask
.
confirmTransactionAndWaitForMining
()
await
metamask
.
confirmTransactionAndWaitForMining
()
const
txHash
=
await
txHashPromise
const
txHash
=
await
txHashPromise
const
transactionReceiptPromise
=
new
Promise
<
Record
<
string
,
string
>>
(
(
resolve
)
=>
{
sharedPage
.
on
(
'
load
'
,
async
()
=>
{
const
responseText
=
await
sharedPage
.
locator
(
'
body > main
'
).
innerText
()
const
transactionReceipt
=
JSON
.
parse
(
responseText
.
replace
(
'
Response:
'
,
''
)
)
resolve
(
transactionReceipt
)
})
}
)
// 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 re
s
ponse 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
(
`
${
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
const
transactionReceipt
=
await
transactionReceiptPromise
await
sharedPage
.
waitForTimeout
(
2
_000
)
const
transactionReceipt
=
JSON
.
parse
(
(
await
sharedPage
.
locator
(
'
body > main
'
).
innerText
()).
replace
(
'
Response:
'
,
''
)
)
try
{
try
{
expect
(
transactionReceipt
.
status
).
toBe
(
'
0x1
'
)
expect
(
transactionReceipt
.
status
).
toBe
(
'
0x1
'
)
...
@@ -149,4 +180,41 @@ test('Send an EIP-1559 transaction and verify success', async () => {
...
@@ -149,4 +180,41 @@ test('Send an EIP-1559 transaction and verify success', async () => {
throw
error
throw
error
}
}
console
.
log
(
'
Sent an EIP-1559 transaction and verified success
'
)
console
.
log
(
'
Sent an EIP-1559 transaction and verified success
'
)
await
setFeeEstimationGauge
(
'
low
'
,
lowFeeEstimate
)
await
setFeeEstimationGauge
(
'
medium
'
,
mediumFeeEstimate
)
await
setFeeEstimationGauge
(
'
high
'
,
highFeeEstimate
)
await
setFeeEstimationGauge
(
'
actual
'
,
getActualTransactionFee
(
transactionReceipt
))
})
})
const
getFeeEstimateInGwei
=
async
(
gasOptionButton
:
string
,
waitForText
:
'
Low
'
|
'
Market
'
|
'
Aggressive
'
,
notificationPage
:
Page
)
=>
{
const
regexParseEtherValue
=
/
(\d
+
\.\d
+
)\s
OPG/
await
synpressPlaywright
.
waitAndClick
(
confirmPageElements
.
editGasFeeButton
,
notificationPage
)
await
synpressPlaywright
.
waitAndClick
(
gasOptionButton
,
notificationPage
)
await
synpressPlaywright
.
waitForText
(
`
${
confirmPageElements
.
editGasFeeButton
}
.edit-gas-fee-button__label`
,
waitForText
,
notificationPage
)
const
feeValue
=
(
await
synpressPlaywright
.
waitAndGetValue
(
confirmPageElements
.
totalLabel
,
notificationPage
)
).
match
(
regexParseEtherValue
)[
1
]
return
parseInt
(
parseGwei
(
feeValue
).
toString
())
}
const
getActualTransactionFee
=
(
transactionReceipt
:
Record
<
string
,
string
>
)
=>
{
const
effectiveGasPrice
=
BigInt
(
transactionReceipt
.
effectiveGasPrice
)
const
l2GasUsed
=
BigInt
(
transactionReceipt
.
gasUsed
)
const
l1Fee
=
BigInt
(
transactionReceipt
.
l1Fee
)
return
parseInt
(
formatGwei
(
effectiveGasPrice
*
l2GasUsed
+
l1Fee
,
'
wei
'
))
}
ufm-test-services/metamask/tests/prometheusUtils.ts
View file @
68553660
import
'
dotenv/config
'
import
'
dotenv/config
'
import
{
z
}
from
'
zod
'
import
{
z
}
from
'
zod
'
import
{
Gauge
,
Pushgateway
}
from
'
prom-client
'
import
{
Gauge
,
Pushgateway
,
Registry
}
from
'
prom-client
'
const
env
=
z
const
env
=
z
.
object
({
.
object
({
...
@@ -10,10 +10,41 @@ const env = z
...
@@ -10,10 +10,41 @@ const env = z
.
parse
(
process
.
env
)
.
parse
(
process
.
env
)
const
selfSendTransactionMetricName
=
'
metamask_self_send
'
const
selfSendTransactionMetricName
=
'
metamask_self_send
'
const
feeEstimateLowMetricName
=
'
metamask_self_send_fee_estimation_low
'
const
feeEstimateMediumMetricName
=
'
metamask_self_send_fee_estimation_medium
'
const
feeEstimateHighMetricName
=
'
metamask_self_send_fee_estimation_high
'
const
feeEstimateActualMetricName
=
'
metamask_self_send_fee_estimation_actual
'
const
selfSendRegistry
=
new
Registry
()
const
feeEstimateLowRegistry
=
new
Registry
()
const
feeEstimateMediumRegistry
=
new
Registry
()
const
feeEstimateHighRegistry
=
new
Registry
()
const
feeEstimateActualRegistry
=
new
Registry
()
const
selfSendGauge
=
new
Gauge
({
const
selfSendGauge
=
new
Gauge
({
name
:
selfSendTransactionMetricName
,
name
:
selfSendTransactionMetricName
,
help
:
'
A gauge signifying the number of transactions sent with Metamask
'
,
help
:
'
A gauge signifying the number of transactions sent with Metamask
'
,
registers
:
[
selfSendRegistry
]
})
const
feeEstimateLowGauge
=
new
Gauge
({
name
:
feeEstimateLowMetricName
,
help
:
'
A gauge signifying the latest fee estimation from Metamask for Low transaction speed
'
,
registers
:
[
feeEstimateLowRegistry
]
})
const
feeEstimateMediumGauge
=
new
Gauge
({
name
:
feeEstimateMediumMetricName
,
help
:
'
A gauge signifying the latest fee estimation from Metamask for Medium transaction speed
'
,
registers
:
[
feeEstimateMediumRegistry
]
})
const
feeEstimateHighGauge
=
new
Gauge
({
name
:
feeEstimateHighMetricName
,
help
:
'
A gauge signifying the latest fee estimation from Metamask for High transaction speed
'
,
registers
:
[
feeEstimateHighRegistry
]
})
const
feeEstimateActualGauge
=
new
Gauge
({
name
:
feeEstimateActualMetricName
,
help
:
'
A gauge signifying the latest actual transaction fee
'
,
registers
:
[
feeEstimateActualRegistry
]
})
})
export
const
getSelfSendGaugeValue
=
async
()
=>
{
export
const
getSelfSendGaugeValue
=
async
()
=>
{
...
@@ -71,10 +102,10 @@ export const getSelfSendGaugeValue = async () => {
...
@@ -71,10 +102,10 @@ export const getSelfSendGaugeValue = async () => {
}
}
export
const
setSelfSendTxGauge
=
async
(
valueToSetTo
:
number
)
=>
{
export
const
setSelfSendTxGauge
=
async
(
valueToSetTo
:
number
)
=>
{
console
.
log
(
`Setting
${
selfSendTransactionMetricName
}
to
${
valueToSetTo
}
`
)
console
.
log
(
`Setting
${
selfSendTransactionMetricName
}
to
${
valueToSetTo
}
...
`
)
selfSendGauge
.
set
(
valueToSetTo
)
selfSendGauge
.
set
(
valueToSetTo
)
const
pushGateway
=
new
Pushgateway
(
env
.
PROMETHEUS_PUSHGATEWAY_URL
)
const
pushGateway
=
new
Pushgateway
(
env
.
PROMETHEUS_PUSHGATEWAY_URL
,
undefined
,
selfSendRegistry
)
await
pushGateway
.
pushAdd
({
jobName
:
'
metamask_self_send_tx_count
'
})
await
pushGateway
.
pushAdd
({
jobName
:
'
metamask_self_send_tx_count
'
})
}
}
...
@@ -89,7 +120,41 @@ export const incrementSelfSendTxGauge = async (isSuccess: boolean) => {
...
@@ -89,7 +120,41 @@ export const incrementSelfSendTxGauge = async (isSuccess: boolean) => {
}
}
console
.
log
(
console
.
log
(
`Current value of
${
selfSendTransactionMetricName
}
is
${
currentMetricValue
}
, incrementing to
${
newMetricValue
}
`
`Current value of
${
selfSendTransactionMetricName
}
is
${
currentMetricValue
}
, incrementing to
${
newMetricValue
}
...
`
)
)
await
setSelfSendTxGauge
(
newMetricValue
)
await
setSelfSendTxGauge
(
newMetricValue
)
}
}
export
const
setFeeEstimationGauge
=
async
(
txSpeed
:
'
low
'
|
'
medium
'
|
'
high
'
|
'
actual
'
,
fee
:
number
)
=>
{
console
.
log
(
txSpeed
!==
'
actual
'
?
`Setting Metamask fee estimation for
${
txSpeed
}
to
${
fee
}
...`
:
`Setting actual transaction fee to
${
fee
}
`
)
let
prometheusRegistry
:
Registry
switch
(
txSpeed
)
{
case
'
low
'
:
feeEstimateLowGauge
.
set
(
fee
)
prometheusRegistry
=
feeEstimateLowRegistry
break
;
case
'
medium
'
:
feeEstimateMediumGauge
.
set
(
fee
)
prometheusRegistry
=
feeEstimateMediumRegistry
break
;
case
'
high
'
:
feeEstimateHighGauge
.
set
(
fee
)
prometheusRegistry
=
feeEstimateHighRegistry
break
;
case
'
actual
'
:
feeEstimateActualGauge
.
set
(
fee
)
prometheusRegistry
=
feeEstimateActualRegistry
break
;
default
:
throw
new
Error
(
`unsupported transaction speed given:
${
txSpeed
}
`
)
}
const
pushGateway
=
new
Pushgateway
(
env
.
PROMETHEUS_PUSHGATEWAY_URL
,
undefined
,
prometheusRegistry
)
await
pushGateway
.
pushAdd
({
jobName
:
`metamask_self_send_tx_fee_estimation_
${
txSpeed
}
`
})
}
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