src/popup/views/confirmTx.js had no unit tests and make test-e2e never
reached the confirmation screen, so the screen that decides what gets signed
had no automated coverage of its own behaviour. The arithmetic underneath is
well covered in src/shared/txValidation.js; the gap was the wiring.
The load-bearing assertion
The confirmation screen quotes the fee estimate (gasLimit * gasPrice) and
gates on the fee reserve (gasLimit * maxFeePerGas). Reading the quoted
number instead is #154 in full, and until now
that was correct by reading only. The fixture stubs the two far apart
(estimate 0.002121 ETH, reserve 0.004221 ETH) and sizes the refused sends
into the gap between them, so an estimate-reading gate accepts what a
reserve-reading gate refuses.
Demonstrated, not asserted by inspection. With feeWei = gasCostWei changed to feeWei = estimateWei in estimateGas() — the mutant the review of #197 described — the suite
goes red:
# gate probe: balance=1.0 ETH amount=0.997879 estimate=0.002121 ETH reserve=0.004221 ETH sendDisabled=false amountFeeError=false
not ok 17 - ConfirmTx gates on the fee RESERVE, not the displayed estimate (#238)
Send is ENABLED for a transfer the fee RESERVE does not cover — the spend gate is reading the displayed estimate, which is issue #154
ok 18 - ConfirmTx refuses a send that exceeds the balance outright (#238)
ok 19 - ConfirmTx refuses to send when the fee estimate fails, with its own message (#238)
# confirm-tx ERC-20 view height: 1070.5625px
ok 20 - ConfirmTx drives the ERC-20 path from pending to funded (#238)
ok 21 - ConfirmTx refuses an ERC-20 send that exceeds the token balance (#238)
# erc-20 gate probe: ethBalance=0.002121 estimate=0.002121 ETH reserve=0.004221 ETH sendDisabled=false gasError=false
not ok 22 - ConfirmTx gates the ERC-20 fee on the RESERVE, with the ERC-20 message (#238)
Send is ENABLED for an ERC-20 transfer whose fee RESERVE exceeds the ETH balance — the spend gate is reading the displayed estimate (#154)
ok 23 - ConfirmTx reports a failed ERC-20 estimate as unknown, not as a fee problem (#238)
# 21/23 tests passed
# FAILED
Both paths die, through different balances and different messages. The mutation
was reverted; git diff origin/next touches no file under src/.
What the harness gained
tests/e2e/network.js, in the established read-at-request-time style:
ethBalanceWei — hex wei answered to eth_getBalance, default zero, which
is what every pre-existing test expects.
seedTokenBalance — the ERC-20 holding that puts a token in the send
screen's dropdown, the only route to the ERC-20 confirmation path.
eth_getBlockByNumber with a baseFeePerGas. Without it ethers has no maxFeePerGas, the reserve and the estimate collapse into one number, and
the gate tests would pass while asserting nothing. That trap is written down
next to the constants.
decimals() for the stub token on eth_call (every other call still answers
a zero word, so the ENS reverse lookup is untouched). A zero there makes parseUnits() reject a fractional amount and the ERC-20 estimate fails for
the wrong reason.
failGasEstimate — a node-side refusal for eth_estimateGas.
holdGasEstimate — holds the estimate open so the pending state is observed
rather than raced. A hold that outlives its bound is reported like any other
harness fault.
ErrorCollector.expect() in tests/e2e/harness.js: a test that drives a
failure path provokes the console.error the code is supposed to emit, which
the harness fails a run on. A declaration consumes exactly one matching record,
and a declaration nothing matched fails its test just as an undeclared error
does — so it cannot be used as a mute. Both halves were verified by running
against a deliberately wrong pattern (console.error: [AutistMask] gas estimation failed: ... reported as an unexpected error) and by declaring an
expectation nothing emits (expected browser error(s) that never arrived: ...).
Coverage added
Nine tests, over both transaction types:
Send disabled while the estimate is pending, with no error message shown.
Send enabled for a comfortably funded send; the fee block quotes ~0.002121 ETH and up to 0.004221 ETH reserved separately.
Send disabled with the distinct fee-unknown message when the estimate fails,
and the over-budget message NOT shown.
Send disabled for a send past the balance outright, exact message asserted.
Send disabled for a send the reserve does not cover (both paths, above).
ERC-20 over token balance, exact message asserted.
View height constant across pending -> landed and pending -> failed, per
path: 1022.5625px for the ETH view and 1070.5625px for the ERC-20 view,
printed on every run.
TODO.md and the End-to-End Tests section of README.md updated in the same
commit.
Verification
make check: green. 19 suites, 416 tests, prettier --check clean.
make test-e2e: green, 23/23 (was 14/14 on next).
Both re-run after rebasing onto next at bd4bdca; the only conflict was TODO.md, resolved keeping both entries.
Note for the reviewer: script/lint in this repo is prettier --check run on
the host, not in a container — there is no containerised lint target to invoke
yet. That is #152, untouched
here.
Not touched, per the issue: the empty-array POST guard
(#187), TRAILING_WATCH_MS,
and the service-worker interception canary.
Closes [#238](https://git.eeqj.de/sneak/AutistMask/issues/238).
`src/popup/views/confirmTx.js` had no unit tests and `make test-e2e` never
reached the confirmation screen, so the screen that decides what gets signed
had no automated coverage of its own behaviour. The arithmetic underneath is
well covered in `src/shared/txValidation.js`; the gap was the wiring.
## The load-bearing assertion
The confirmation screen quotes the fee **estimate** (`gasLimit * gasPrice`) and
gates on the fee **reserve** (`gasLimit * maxFeePerGas`). Reading the quoted
number instead is
[#154](https://git.eeqj.de/sneak/AutistMask/issues/154) in full, and until now
that was correct by reading only. The fixture stubs the two far apart
(estimate `0.002121 ETH`, reserve `0.004221 ETH`) and sizes the refused sends
into the gap between them, so an estimate-reading gate accepts what a
reserve-reading gate refuses.
Demonstrated, not asserted by inspection. With `feeWei = gasCostWei` changed to
`feeWei = estimateWei` in `estimateGas()` — the mutant the review of
[#197](https://git.eeqj.de/sneak/AutistMask/pulls/197) described — the suite
goes red:
```
# gate probe: balance=1.0 ETH amount=0.997879 estimate=0.002121 ETH reserve=0.004221 ETH sendDisabled=false amountFeeError=false
not ok 17 - ConfirmTx gates on the fee RESERVE, not the displayed estimate (#238)
Send is ENABLED for a transfer the fee RESERVE does not cover — the spend gate is reading the displayed estimate, which is issue #154
ok 18 - ConfirmTx refuses a send that exceeds the balance outright (#238)
ok 19 - ConfirmTx refuses to send when the fee estimate fails, with its own message (#238)
# confirm-tx ERC-20 view height: 1070.5625px
ok 20 - ConfirmTx drives the ERC-20 path from pending to funded (#238)
ok 21 - ConfirmTx refuses an ERC-20 send that exceeds the token balance (#238)
# erc-20 gate probe: ethBalance=0.002121 estimate=0.002121 ETH reserve=0.004221 ETH sendDisabled=false gasError=false
not ok 22 - ConfirmTx gates the ERC-20 fee on the RESERVE, with the ERC-20 message (#238)
Send is ENABLED for an ERC-20 transfer whose fee RESERVE exceeds the ETH balance — the spend gate is reading the displayed estimate (#154)
ok 23 - ConfirmTx reports a failed ERC-20 estimate as unknown, not as a fee problem (#238)
# 21/23 tests passed
# FAILED
```
Both paths die, through different balances and different messages. The mutation
was reverted; `git diff origin/next` touches no file under `src/`.
## What the harness gained
`tests/e2e/network.js`, in the established read-at-request-time style:
- `ethBalanceWei` — hex wei answered to `eth_getBalance`, default zero, which
is what every pre-existing test expects.
- `seedTokenBalance` — the ERC-20 holding that puts a token in the send
screen's dropdown, the only route to the ERC-20 confirmation path.
- `eth_getBlockByNumber` with a `baseFeePerGas`. Without it ethers has no
`maxFeePerGas`, the reserve and the estimate collapse into one number, and
the gate tests would pass while asserting nothing. That trap is written down
next to the constants.
- `decimals()` for the stub token on `eth_call` (every other call still answers
a zero word, so the ENS reverse lookup is untouched). A zero there makes
`parseUnits()` reject a fractional amount and the ERC-20 estimate fails for
the wrong reason.
- `failGasEstimate` — a node-side refusal for `eth_estimateGas`.
- `holdGasEstimate` — holds the estimate open so the pending state is observed
rather than raced. A hold that outlives its bound is reported like any other
harness fault.
`ErrorCollector.expect()` in `tests/e2e/harness.js`: a test that drives a
failure path provokes the `console.error` the code is supposed to emit, which
the harness fails a run on. A declaration consumes exactly one matching record,
and a declaration nothing matched fails its test just as an undeclared error
does — so it cannot be used as a mute. Both halves were verified by running
against a deliberately wrong pattern (`console.error: [AutistMask] gas
estimation failed: ...` reported as an unexpected error) and by declaring an
expectation nothing emits (`expected browser error(s) that never arrived: ...`).
## Coverage added
Nine tests, over both transaction types:
- Send disabled while the estimate is pending, with no error message shown.
- Send enabled for a comfortably funded send; the fee block quotes
`~0.002121 ETH` and `up to 0.004221 ETH reserved` separately.
- Send disabled with the distinct fee-unknown message when the estimate fails,
and the over-budget message NOT shown.
- Send disabled for a send past the balance outright, exact message asserted.
- Send disabled for a send the reserve does not cover (both paths, above).
- ERC-20 over token balance, exact message asserted.
- View height constant across pending -> landed and pending -> failed, per
path: `1022.5625px` for the ETH view and `1070.5625px` for the ERC-20 view,
printed on every run.
`TODO.md` and the End-to-End Tests section of `README.md` updated in the same
commit.
## Verification
- `make check`: green. 19 suites, 416 tests, `prettier --check` clean.
- `make test-e2e`: green, 23/23 (was 14/14 on `next`).
- Both re-run after rebasing onto `next` at `bd4bdca`; the only conflict was
`TODO.md`, resolved keeping both entries.
Note for the reviewer: `script/lint` in this repo is `prettier --check` run on
the host, not in a container — there is no containerised lint target to invoke
yet. That is [#152](https://git.eeqj.de/sneak/AutistMask/issues/152), untouched
here.
Not touched, per the issue: the empty-array POST guard
([#187](https://git.eeqj.de/sneak/AutistMask/issues/187)), `TRAILING_WATCH_MS`,
and the service-worker interception canary.
The screen that decides what gets signed had no automated coverage of its
own behaviour: no unit tests, and the e2e suite never reached it. The
arithmetic underneath is well covered in src/shared/txValidation.js; the
gap was the wiring — which number reaches the gate, when the gate re-runs,
what the fee block renders, and whether Send is enabled.
The confirmation screen quotes the fee ESTIMATE (gasLimit * gasPrice) and
gates on the fee RESERVE (gasLimit * maxFeePerGas). Reading the quoted
number instead was issue #154, and until now that was correct by reading
only — a mutant swapping the two passed the whole suite. It no longer
does: two of the new tests fail on it, one per transaction type.
The harness gains a funded-balance fixture to make any of this reachable.
tests/e2e/network.js now serves a configurable ETH balance, an ERC-20
holding, a latest block with a baseFeePerGas (without which ethers has no
maxFeePerGas and the reserve and the estimate collapse into one number),
and decimals() for the stub token. It can also refuse a gas estimate, and
hold one open so the pending state can be observed rather than raced.
Nine tests, over both the native ETH and the ERC-20 path: Send disabled
while the estimate is pending, enabled once it lands, the fee block
quoting both numbers, the distinct message for an estimate that failed,
refusal for a send past the balance, refusal for a send the reserve does
not cover, and the view height constant across every one of those
transitions.
Driving a failure path means provoking the console.error the code is
supposed to emit, which the harness fails a run on. ErrorCollector gains
expect(): it consumes exactly one matching record, and a declaration
nothing matched fails its test just as an undeclared error does, so it
cannot be used to silence anything. Both halves of that were verified by
running the suite against a deliberately wrong pattern.
clawbot
self-assigned this 2026-08-12 10:53:59 +02:00
FAIL — needs-rebase. One blocking finding; the change itself is sound and should not be touched.
Not fast-forwardable onto current origin/next. Branch head 7e5d7cd forks at bd4bdca; next is now afe6dda, two commits ahead (#221 at 23712b5, #155 at afe6dda). git merge-tree --write-tree origin/next 7e5d7cd exits 1 with CONFLICT (content): Merge conflict in TODO.md — both sides insert a bullet at the top of # Completed Steps. The PR body's rebase onto bd4bdca predates both. Acceptable: rebase onto current origin/next, resolve TODO.md keeping all three bullets (this one plus the #221 and #155 entries — the #221 entry does not exist at this branch's merge-base and must not be dropped), re-run both suites, force-push. No other change wanted.
Everything else verified and passing, independently reproduced on a clean clone: make check 19 suites / 416 tests, prettier clean; make test-e2e 23/23 executed; every DoD item real; scope clean (no file under src/); commit hygiene, authorship and terminology clean.
The gate assertion is not just reproducible, it is broad. Five independent mutations to src/popup/views/confirmTx.js, each reverted:
feeWei = gasCostWei to feeWei = estimateWei — 17 and 22 die, exactly as reported.
feeWei = 0n (gate ignores the fee) — 17 and 22 die.
feeReserveWei to feeEstimateWei (reserve collapsed onto estimate) — 16, 17, 20, 22 die; test 17's "this send is not in the gap" guard fires, so it refuses to pass vacuously when the fixture spread disappears.
estimate and reserve swapped in the fee block only, gate left correct — 16, 17, 20 die.
setVisible() collapsing space with display:none instead of reserving it, i.e. the layout-shift class of #252 — 16, 17, 19, 20, 22, 23 die on the height assertions across both paths and all three transitions. The height is genuinely asserted, not merely printed. Incidentally the pending height under that mutant is 874.5625px, matching the figure measured by hand in the review of #197.
ErrorCollector.expect() cannot mask a genuine error. Four probes: a second console.error matching the declared pattern in the same code path still fails its test (consumption is exactly one); an unrelated genuine console.error injected into the declaring test's own path still fails it; an expectation nothing matches fails its test (expected browser error(s) that never arrived); and an expectation declared in test 18 does not survive into test 19 — unmatchedExpectations() is called unconditionally after every test in tests/e2e/run.js:1079 and clears the list, so there is no forward leak. ALLOWED_ERRORS, TRAILING_WATCH_MS and the service-worker interception canary are untouched.
Disclosures, none blocking. The fixture widens what the RPC stub answers without reporting: eth_getBlockByNumber now returns the latest block for any block parameter, and eth_getCode is newly stubbed — previously both would have been reported as unstubbed methods. Fixture-shape choices, no outbound traffic escapes either way. The empty-array POST hole (#187) is unchanged, neither fixed nor worsened. Tracker CI status ignored per #220; host-only script/lint not held against this PR per #152.
FAIL — `needs-rebase`. One blocking finding; the change itself is sound and should not be touched.
**Not fast-forwardable onto current `origin/next`.** Branch head `7e5d7cd` forks at `bd4bdca`; `next` is now `afe6dda`, two commits ahead ([#221](https://git.eeqj.de/sneak/AutistMask/issues/221) at `23712b5`, [#155](https://git.eeqj.de/sneak/AutistMask/issues/155) at `afe6dda`). `git merge-tree --write-tree origin/next 7e5d7cd` exits 1 with `CONFLICT (content): Merge conflict in TODO.md` — both sides insert a bullet at the top of `# Completed Steps`. The PR body's rebase onto `bd4bdca` predates both. Acceptable: rebase onto current `origin/next`, resolve `TODO.md` keeping all three bullets (this one plus the [#221](https://git.eeqj.de/sneak/AutistMask/issues/221) and [#155](https://git.eeqj.de/sneak/AutistMask/issues/155) entries — the [#221](https://git.eeqj.de/sneak/AutistMask/issues/221) entry does not exist at this branch's merge-base and must not be dropped), re-run both suites, force-push. No other change wanted.
Everything else verified and passing, independently reproduced on a clean clone: `make check` 19 suites / 416 tests, prettier clean; `make test-e2e` 23/23 executed; every DoD item real; scope clean (no file under `src/`); commit hygiene, authorship and terminology clean.
The gate assertion is not just reproducible, it is broad. Five independent mutations to `src/popup/views/confirmTx.js`, each reverted:
- `feeWei = gasCostWei` to `feeWei = estimateWei` — 17 and 22 die, exactly as reported.
- `feeWei = 0n` (gate ignores the fee) — 17 and 22 die.
- `feeReserveWei` to `feeEstimateWei` (reserve collapsed onto estimate) — 16, 17, 20, 22 die; test 17's "this send is not in the gap" guard fires, so it refuses to pass vacuously when the fixture spread disappears.
- estimate and reserve swapped in the fee block only, gate left correct — 16, 17, 20 die.
- `setVisible()` collapsing space with `display:none` instead of reserving it, i.e. the layout-shift class of [#252](https://git.eeqj.de/sneak/AutistMask/issues/252) — 16, 17, 19, 20, 22, 23 die on the height assertions across both paths and all three transitions. The height is genuinely asserted, not merely printed. Incidentally the pending height under that mutant is 874.5625px, matching the figure measured by hand in the review of [#197](https://git.eeqj.de/sneak/AutistMask/pulls/197).
`ErrorCollector.expect()` cannot mask a genuine error. Four probes: a second `console.error` matching the declared pattern in the same code path still fails its test (consumption is exactly one); an unrelated genuine `console.error` injected into the declaring test's own path still fails it; an expectation nothing matches fails its test (`expected browser error(s) that never arrived`); and an expectation declared in test 18 does not survive into test 19 — `unmatchedExpectations()` is called unconditionally after every test in `tests/e2e/run.js:1079` and clears the list, so there is no forward leak. `ALLOWED_ERRORS`, `TRAILING_WATCH_MS` and the service-worker interception canary are untouched.
Disclosures, none blocking. The fixture widens what the RPC stub answers without reporting: `eth_getBlockByNumber` now returns the `latest` block for any block parameter, and `eth_getCode` is newly stubbed — previously both would have been reported as unstubbed methods. Fixture-shape choices, no outbound traffic escapes either way. The empty-array POST hole ([#187](https://git.eeqj.de/sneak/AutistMask/issues/187)) is unchanged, neither fixed nor worsened. Tracker CI status ignored per [#220](https://git.eeqj.de/sneak/AutistMask/issues/220); host-only `script/lint` not held against this PR per [#152](https://git.eeqj.de/sneak/AutistMask/issues/152).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #238.
src/popup/views/confirmTx.jshad no unit tests andmake test-e2eneverreached the confirmation screen, so the screen that decides what gets signed
had no automated coverage of its own behaviour. The arithmetic underneath is
well covered in
src/shared/txValidation.js; the gap was the wiring.The load-bearing assertion
The confirmation screen quotes the fee estimate (
gasLimit * gasPrice) andgates on the fee reserve (
gasLimit * maxFeePerGas). Reading the quotednumber instead is
#154 in full, and until now
that was correct by reading only. The fixture stubs the two far apart
(estimate
0.002121 ETH, reserve0.004221 ETH) and sizes the refused sendsinto the gap between them, so an estimate-reading gate accepts what a
reserve-reading gate refuses.
Demonstrated, not asserted by inspection. With
feeWei = gasCostWeichanged tofeeWei = estimateWeiinestimateGas()— the mutant the review of#197 described — the suite
goes red:
Both paths die, through different balances and different messages. The mutation
was reverted;
git diff origin/nexttouches no file undersrc/.What the harness gained
tests/e2e/network.js, in the established read-at-request-time style:ethBalanceWei— hex wei answered toeth_getBalance, default zero, whichis what every pre-existing test expects.
seedTokenBalance— the ERC-20 holding that puts a token in the sendscreen's dropdown, the only route to the ERC-20 confirmation path.
eth_getBlockByNumberwith abaseFeePerGas. Without it ethers has nomaxFeePerGas, the reserve and the estimate collapse into one number, andthe gate tests would pass while asserting nothing. That trap is written down
next to the constants.
decimals()for the stub token oneth_call(every other call still answersa zero word, so the ENS reverse lookup is untouched). A zero there makes
parseUnits()reject a fractional amount and the ERC-20 estimate fails forthe wrong reason.
failGasEstimate— a node-side refusal foreth_estimateGas.holdGasEstimate— holds the estimate open so the pending state is observedrather than raced. A hold that outlives its bound is reported like any other
harness fault.
ErrorCollector.expect()intests/e2e/harness.js: a test that drives afailure path provokes the
console.errorthe code is supposed to emit, whichthe harness fails a run on. A declaration consumes exactly one matching record,
and a declaration nothing matched fails its test just as an undeclared error
does — so it cannot be used as a mute. Both halves were verified by running
against a deliberately wrong pattern (
console.error: [AutistMask] gas estimation failed: ...reported as an unexpected error) and by declaring anexpectation nothing emits (
expected browser error(s) that never arrived: ...).Coverage added
Nine tests, over both transaction types:
~0.002121 ETHandup to 0.004221 ETH reservedseparately.and the over-budget message NOT shown.
path:
1022.5625pxfor the ETH view and1070.5625pxfor the ERC-20 view,printed on every run.
TODO.mdand the End-to-End Tests section ofREADME.mdupdated in the samecommit.
Verification
make check: green. 19 suites, 416 tests,prettier --checkclean.make test-e2e: green, 23/23 (was 14/14 onnext).nextatbd4bdca; the only conflict wasTODO.md, resolved keeping both entries.Note for the reviewer:
script/lintin this repo isprettier --checkrun onthe host, not in a container — there is no containerised lint target to invoke
yet. That is #152, untouched
here.
Not touched, per the issue: the empty-array POST guard
(#187),
TRAILING_WATCH_MS,and the service-worker interception canary.
FAIL —
needs-rebase. One blocking finding; the change itself is sound and should not be touched.Not fast-forwardable onto current
origin/next. Branch head7e5d7cdforks atbd4bdca;nextis nowafe6dda, two commits ahead (#221 at23712b5, #155 atafe6dda).git merge-tree --write-tree origin/next 7e5d7cdexits 1 withCONFLICT (content): Merge conflict in TODO.md— both sides insert a bullet at the top of# Completed Steps. The PR body's rebase ontobd4bdcapredates both. Acceptable: rebase onto currentorigin/next, resolveTODO.mdkeeping all three bullets (this one plus the #221 and #155 entries — the #221 entry does not exist at this branch's merge-base and must not be dropped), re-run both suites, force-push. No other change wanted.Everything else verified and passing, independently reproduced on a clean clone:
make check19 suites / 416 tests, prettier clean;make test-e2e23/23 executed; every DoD item real; scope clean (no file undersrc/); commit hygiene, authorship and terminology clean.The gate assertion is not just reproducible, it is broad. Five independent mutations to
src/popup/views/confirmTx.js, each reverted:feeWei = gasCostWeitofeeWei = estimateWei— 17 and 22 die, exactly as reported.feeWei = 0n(gate ignores the fee) — 17 and 22 die.feeReserveWeitofeeEstimateWei(reserve collapsed onto estimate) — 16, 17, 20, 22 die; test 17's "this send is not in the gap" guard fires, so it refuses to pass vacuously when the fixture spread disappears.setVisible()collapsing space withdisplay:noneinstead of reserving it, i.e. the layout-shift class of #252 — 16, 17, 19, 20, 22, 23 die on the height assertions across both paths and all three transitions. The height is genuinely asserted, not merely printed. Incidentally the pending height under that mutant is 874.5625px, matching the figure measured by hand in the review of #197.ErrorCollector.expect()cannot mask a genuine error. Four probes: a secondconsole.errormatching the declared pattern in the same code path still fails its test (consumption is exactly one); an unrelated genuineconsole.errorinjected into the declaring test's own path still fails it; an expectation nothing matches fails its test (expected browser error(s) that never arrived); and an expectation declared in test 18 does not survive into test 19 —unmatchedExpectations()is called unconditionally after every test intests/e2e/run.js:1079and clears the list, so there is no forward leak.ALLOWED_ERRORS,TRAILING_WATCH_MSand the service-worker interception canary are untouched.Disclosures, none blocking. The fixture widens what the RPC stub answers without reporting:
eth_getBlockByNumbernow returns thelatestblock for any block parameter, andeth_getCodeis newly stubbed — previously both would have been reported as unstubbed methods. Fixture-shape choices, no outbound traffic escapes either way. The empty-array POST hole (#187) is unchanged, neither fixed nor worsened. Tracker CI status ignored per #220; host-onlyscript/lintnot held against this PR per #152.7e5d7cdcdetoecfddeb327ecfddeb327to5092cf162a