fix: carry EIP-1193 error codes through to the page (closes #274)
All checks were successful
check / check (push) Successful in 28s
All checks were successful
check / check (push) Successful in 28s
The provider rebuilt every rejection as a bare Error carrying only a message, so a dApp checking err.code === 4001 saw undefined and could not tell a user's deliberate refusal from a failure. Well-behaved sites therefore showed an error or retried instead of accepting the refusal. The code was produced correctly and did cross the extension boundary; it was lost in the last hop. Rejections now reach the page as a ProviderRpcError carrying code, and data where present. The code is passed through verbatim rather than matched against a whitelist, so a code added upstream later needs no change here. An error that genuinely has no code stays a plain Error with no code property at all, rather than advertising code: undefined -- 'code' in err is what a careful dApp asks. Messages are unchanged for every path, verified byte-for-byte against the previous provider across every background error shape. The end-to-end assertion that printed the observed code now requires it.
This commit was merged in pull request #278.
This commit is contained in:
@@ -86,9 +86,11 @@ const DAPP_URL = DAPP_ORIGIN + "/";
|
||||
// never drive the popup that has to settle it; start() files the promise
|
||||
// under a key and settle() collects it once the prompt has been dealt with.
|
||||
//
|
||||
// The rejection branch records `code` as it arrives. EIP-1193 says a user
|
||||
// rejection is a ProviderRpcError carrying code 4001; what the page can
|
||||
// actually see is recorded here rather than assumed, and asserted in run.js.
|
||||
// The rejection branch records the whole observable shape of the error as it
|
||||
// arrives — name, message, and whether a `code` is present at all as distinct
|
||||
// from its value. EIP-1193 says a user rejection is a ProviderRpcError
|
||||
// carrying code 4001; what the page can actually see is recorded here rather
|
||||
// than assumed, and asserted in run.js.
|
||||
//
|
||||
// The message log is the page's half of the boundary observation: every
|
||||
// AUTISTMASK_* message that crosses between this page and the content
|
||||
@@ -120,6 +122,7 @@ const DAPP_HTML = [
|
||||
" return {",
|
||||
" settled: 'rejected',",
|
||||
" message: String((error && error.message) || error),",
|
||||
" name: error ? error.name : undefined,",
|
||||
" hasCode: !!error && 'code' in Object(error),",
|
||||
" code: error ? error.code : undefined,",
|
||||
" };",
|
||||
|
||||
@@ -1591,15 +1591,14 @@ async function lastResponseError(page) {
|
||||
}
|
||||
|
||||
// A rejected prompt, asserted at both ends: the page's promise rejected
|
||||
// rather than hanging or resolving, and the response that crossed the
|
||||
// boundary carried EIP-1193 code 4001.
|
||||
// rather than hanging or resolving, and EIP-1193 code 4001 is present both
|
||||
// on the wire and on the Error the calling page catches.
|
||||
//
|
||||
// The code is asserted on the wire because that is the only place it
|
||||
// survives. src/content/inpage.js rebuilds the rejection as `new
|
||||
// Error(error.message)`, so the Error the calling page catches carries the
|
||||
// message and no code. That is reported rather than asserted either way —
|
||||
// locking in the current behaviour would make the gap permanent, and
|
||||
// asserting the code on the Error would fail today.
|
||||
// Both ends matter because they used to disagree. The code crossed the
|
||||
// boundary correctly and src/content/inpage.js then threw it away, rebuilding
|
||||
// every rejection as `new Error(error.message)` — so a dApp branching on
|
||||
// `err.code === 4001` saw undefined and could not tell a refusal from a
|
||||
// failure (#274). Asserting only the wire would leave that gap invisible.
|
||||
async function assertUserRejection(page, key, label) {
|
||||
const outcome = await settleRequest(page, key);
|
||||
assert(
|
||||
@@ -1621,15 +1620,32 @@ async function assertUserRejection(page, key, label) {
|
||||
" did not carry EIP-1193 code 4001 across the boundary: " +
|
||||
JSON.stringify(error),
|
||||
);
|
||||
assert(
|
||||
outcome.hasCode,
|
||||
label +
|
||||
" reached the page as an error with no code property at all, so a " +
|
||||
"dApp cannot tell the user's refusal from a failure: " +
|
||||
JSON.stringify(outcome),
|
||||
);
|
||||
assert(
|
||||
outcome.code === 4001,
|
||||
label +
|
||||
" reached the page with code " +
|
||||
JSON.stringify(outcome.code) +
|
||||
" rather than EIP-1193 4001",
|
||||
);
|
||||
assert(
|
||||
outcome.name === "ProviderRpcError",
|
||||
label +
|
||||
" reached the page as " +
|
||||
JSON.stringify(outcome.name) +
|
||||
" rather than an EIP-1193 ProviderRpcError",
|
||||
);
|
||||
console.log(
|
||||
"# " +
|
||||
label +
|
||||
": boundary code=" +
|
||||
error.code +
|
||||
" page Error.code=" +
|
||||
JSON.stringify(outcome.code) +
|
||||
" page Error carries a code=" +
|
||||
outcome.hasCode,
|
||||
": code 4001 on the wire and on the page's " +
|
||||
outcome.name,
|
||||
);
|
||||
return outcome;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user