fix: a send the confirmation screen refuses moves nothing on it (closes #583)
Sign & Send wrote its refusals, a network changed since review and a gas limit or fee over its bound, into the errors box above the password field. That box reserves one empty line, so a refusal grew it and pushed the password field and Sign & Send down. Each refusal now has its own box with its sentence fixed in index.html, holding its space from the first paint and only made visible, as #558 did for the balance messages. One sentence is true of all three bounds and quotes no number; the full reason is logged. A Chrome test raises the stub node's fee only at signing and checks nothing moved; a unit test refuses a gas limit over its bound into the same box. Model: opus-5-5
This commit was merged in pull request #608.
This commit is contained in:
@@ -291,9 +291,12 @@ function show(txInfo) {
|
||||
|
||||
renderValidation(txInfo);
|
||||
|
||||
// Reset password field and error
|
||||
// Reset password field and error, and any refusal of an earlier Sign &
|
||||
// Send.
|
||||
$("confirm-tx-password").value = "";
|
||||
hideError("confirm-tx-password-error");
|
||||
setVisible("confirm-network-changed-error", false);
|
||||
setVisible("confirm-fee-bound-error", false);
|
||||
|
||||
// Gas estimate — show placeholder then fetch async
|
||||
$("confirm-fee").style.visibility = "visible";
|
||||
@@ -556,16 +559,17 @@ async function estimateGas(txInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
// Populate the transaction this send describes, enforce the fee bound against
|
||||
// the fees that were actually filled in, then sign and broadcast it. Apart
|
||||
// from a max ETH send, which passes its estimate's fee fields as `fees`, the
|
||||
// send pins no fee fields, so ethers fills maxFeePerGas and the gas limit from
|
||||
// what the configured RPC node answers, with nothing otherwise bounding what a
|
||||
// hostile node can set — the dApp path's ceilings never reached this one.
|
||||
// Populating before the check is what makes assertWithinCeilings() see the
|
||||
// same numbers that would be signed; it throws an ApprovalMismatchError when
|
||||
// the product gasLimit × maxFeePerGas is over the bound, which the caller
|
||||
// shows in the reserved error area rather than sending.
|
||||
// Populate the transaction this send describes, enforce the bounds against the
|
||||
// gas limit and fees that were actually filled in, then sign and broadcast it.
|
||||
// Apart from a max ETH send, which passes its estimate's fee fields as `fees`,
|
||||
// the send pins no fee fields, so ethers fills maxFeePerGas and the gas limit
|
||||
// from what the configured RPC node answers, with nothing otherwise bounding
|
||||
// what a hostile node can set — the dApp path's ceilings never reached this
|
||||
// one. Populating before the check is what makes assertWithinCeilings() see
|
||||
// the same numbers that would be signed; it throws an ApprovalMismatchError
|
||||
// when the gas limit, a fee per gas, or the product gasLimit × maxFeePerGas is
|
||||
// over its bound, and the caller then shows the one refusal reserved for all
|
||||
// three rather than sending.
|
||||
async function populateVerifyAndSend(connectedSigner, tx, fees = null) {
|
||||
let request;
|
||||
if (tx.token === "ETH") {
|
||||
@@ -593,15 +597,6 @@ async function populateVerifyAndSend(connectedSigner, tx, fees = null) {
|
||||
return connectedSigner.sendTransaction(populated);
|
||||
}
|
||||
|
||||
// Show a full-sentence send failure in the reserved errors box, the same
|
||||
// element and markup renderValidation() uses for the invalid-amount message,
|
||||
// so it never moves anything on the screen.
|
||||
function showSendError(message) {
|
||||
const el = $("confirm-errors");
|
||||
el.innerHTML = `<div class="text-xs">${escapeHtml(message)}</div>`;
|
||||
el.style.visibility = "visible";
|
||||
}
|
||||
|
||||
async function checkRecipientHistory(txInfo) {
|
||||
try {
|
||||
const provider = reviewedNetworkProvider(txInfo);
|
||||
@@ -640,6 +635,10 @@ function init(_ctx) {
|
||||
onViewLeave("confirm-tx", clearPassword);
|
||||
|
||||
$("btn-confirm-send").addEventListener("click", async () => {
|
||||
// A press shows only its own refusal, if any.
|
||||
setVisible("confirm-network-changed-error", false);
|
||||
setVisible("confirm-fee-bound-error", false);
|
||||
|
||||
const wallet = state.wallets[state.selectedWallet];
|
||||
// Every Send button refuses a defective wallet before this screen,
|
||||
// but the popup also reopens onto it from a saved view.
|
||||
@@ -654,11 +653,7 @@ function init(_ctx) {
|
||||
// Settings after a switch draws this screen again from the saved
|
||||
// transaction, as a popup reopened onto it does.
|
||||
if (currentNetwork().chainId !== pendingTx.chainId) {
|
||||
showSendError(
|
||||
"The active network changed after this transaction was" +
|
||||
" reviewed, so it was not sent. Please go back and review" +
|
||||
" it again.",
|
||||
);
|
||||
setVisible("confirm-network-changed-error", true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -722,12 +717,16 @@ function init(_ctx) {
|
||||
txStatus.showWait(pendingTx, tx.hash);
|
||||
} catch (e) {
|
||||
decryptedSecret = null;
|
||||
// A fee over the bound is refused before anything is broadcast, so
|
||||
// there is no transaction that may have reached the network to warn
|
||||
// about: the message stays on the confirmation screen where the
|
||||
// user can go back, rather than routing to the sent/failed screen.
|
||||
// A gas limit or fee over its bound is refused before anything is
|
||||
// broadcast, so there is no transaction that may have reached the
|
||||
// network to warn about: the refusal stays on the confirmation
|
||||
// screen where the user can go back, rather than routing to the
|
||||
// sent/failed screen. Its sentence is fixed in index.html, so that
|
||||
// its box can hold its space from the first paint; the reason in
|
||||
// full, naming the bound, is logged.
|
||||
if (e && e.approvalMismatch) {
|
||||
showSendError(e.message);
|
||||
log.errorf("send refused:", e.message);
|
||||
setVisible("confirm-fee-bound-error", true);
|
||||
return;
|
||||
}
|
||||
const hash = tx ? tx.hash : null;
|
||||
|
||||
Reference in New Issue
Block a user