I want to submit a transaction from the client to Ethereum blockchain, get back a transaction hash, and then submit this hash to my backend.
On the front-end I have a form like this:
<form id="formConfirm" method="POST" action="/step2" onSubmit="processStep1">
<input id="textWalletAddress" name="walletAddress" type="text" readonly />
<input id="textEncodedFunctionCall" th:value="${encodedFunctionCall}" readonly />
<input type="text" id="textTxHash" readonly />
<input type="submit" value="Submit" />
</form>
Inside the function, I want to submit the transaction, get back the corresponding hash, and populate a field with it so that I can push it to my backend.
async function processStep1(e) {
// edit1: trying out preventDefault
alert('Before preventDefault');
e.preventDefault();
alert('After preventDefault');
let efc = document.getElementById('textEncodedFunctionCall').value;
let walletAddress = document.getElementById('textWalletAddress').value;
const txParams = {
to: CONTRACT,
from: walletAddress,
'data': efc
};
if (window.ethereum) {
try {
const txHash = await window.ethereum.request({
method: "eth_sendTransaction",
params: [txParams]
});
document.getElementById('textTxHash').value = txHash;
// how/where do I preventDefault()?
} catch (err) {
alert('ERROR! ' err);
}
} else {
alert('MetaMask is not installed!');
}
}
The problem I encounter is that the form goes ahead with the submission without the hash. The transaction does go through (with MetaMask prompts and all) but on my backend I get an error.
I read that I am supposed to preventDefault()
but I am not sure how to apply it. Can someone guide me on how to do this? Plain JavaScript would be the most ideal as I am trying to reduce dependencies on external libraries.
Edit1: I tried preventDefault
at the beginning but I don't even see any alerts.
Edit2: Added the fields that were missing in my question
CodePudding user response:
Well I'd go with:
- Removing
onSubmit="processStep1()"
and replacing it with javascript:document.getElementById('formConfirm').onsubmit = processStep1;
. Note the lack of()
here! EDIT: as someone already pointed out, removing () inonSubmit="processStep1()"
should also do the trick - Then, your function gets an event as parameter, co you can do
async function processStep1(event) { ... }
- Now the last thing is to call
event.preventDefault()
somewhere at the beginning. Remember that you are preventing default action, so you are not submitting the form - you probably want to use fetch() to submit the form manually.
Other than that you can replace input type="submit"
with a custom button with your function on click, so you don't have to call event.preventDefault()
. But imo no big difference.
CodePudding user response:
Some errors:
onSubmit
takes a function and not a function call so it should be:
<form ... onSubmit="processStep1"> ...
- If you want to stop the form action you need to call the
preventDefault
method of the event, which stops the browser to contacting the form action at first place:
async function processStep1(event) {
// I suggest as first row so you are sure
// the form will stop before any other operation/error
// made by this function
event.preventDefault();
...
- You then need to call your backend with a
XHR
(XMLHttpRequest - MDN) because you stopped the normal call that your browser would have done (by preventing the default of the event):
function sendForm(form) {
var formData = new FormData();
var inputs = form.querySelectorAll('input');
inputs.forEach(function(input) {
var data;
// simplified version for files (since it seems you don't need it)
if (input.type === 'file') { data = input.files[0]; }
else { data = input.value; }
formData.append(input.name, data);
});
return new Promise(function(y, n) {
var xhr= new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (status === 0 || (status >= 200 && status < 400)) {
y(xhr.responseText);
}
else { n(xhr); }
};
xhr.open(form.method, form.action);
xhr.send(formData);
});
}
async function processStep1(event) {
...
// at the end of your function
try {
var response = await sendForm(this);
// threat response
} catch(ex) {
/// threat exceptions
}
}
For more info on FormData Object
(MDN) I suggest you to follow the link for a better reading.
CodePudding user response:
Here. I just put the whole code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form id="formConfirm">
<input id="textWalletAddress" name="walletAddress" type="text" />
<input id="textEncodedFunctionCall" th:value="${encodedFunctionCall}" />
<input type="text" id="textTxHash" readonly />
<input type="submit" value="Submit" />
</form>
<script>
const form = document.getElementById("formConfirm")
async function processStep1(e) {
// let efc = document.getElementById("textEncodedFunctionCall").value
// let walletAddress = document.getElementById("textWalletAddress").value
// const txParams = {
// to: CONTRACT,
// from: walletAddress,
// data: efc,
// }
// if (window.ethereum) {
// try {
// const txHash = await window.ethereum.request({
// method: "eth_sendTransaction",
// params: [txParams],
// })
// document.getElementById("textTxHash").value = txHash
// // how/where do I preventDefault()?
// } catch (err) {
// alert("ERROR! " err)
// }
// } else {
// alert("MetaMask is not installed!")
// }
document.getElementById("textTxHash").value = "HASH"
}
form.addEventListener("submit", async function (e) {
e.preventDefault()
await processStep1()
})
</script>
</body>
</html>