I am attempting to create a button on my dapp to send Ethereum. I would like to log a bit of the data in my own database after the transaction is successful. I don't want to log the data in my db until AFTER I know the transaction was successful. Is there a way to do this in Javascript?
Here is my code:
sendEthButton.addEventListener('click', () => {
let userETH = document.getElementById("inputId").value;
var wei_val = userETH*10e17;
var hexString = wei_val.toString(16);
ethereum
.request({
method: 'eth_sendTransaction',
params: [
{
from: accounts[0],
to: my_address,
//value: '0x6f05b59d3b20000',
value: hexString,
gasPrice: '',
gas: '',
},
],
})
.then((txHash) => console.log(txHash))
.catch((error) => console.error);
});
I know that I can test for the txHash, but that doesn't indicate whether or not the transaction was successful, just whether or not it was successfully initiated.
How can I test for a successful transaction through JS?
Related
After a long discussion with ChatGPT, I managed to write code that redirects the user to the Stripe payment page and then captures an event when the transaction is successfully completed.
The problem is that my fetch request has already received a response from the /checkout endpoint and is not waiting for a response from /webhook. And I would like my API to return a properly generated response after successfully finalizing the transaction. What am I doing wrong?
First, I send a request to the /checkout endpoint, which takes care of generating the payment link and sending it back:
fetch('http://localhost:3001/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
items: [
{
id: 0,
},
],
}),
})
.then((res) => {
if (res.ok) return res.json();
return res.json().then((e) => console.error(e));
})
.then(({url}) => {
console.log(url);
window.location = url;
})
.catch((e) => {
console.log(e);
});
This code when I press the button redirects me to the Stripe payment page.
Endpoint /checkout:
app.post('/checkout', async (req, res) => {
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: req.body.items.map(({id}) => {
const storeItem = storeItems.get(id);
return {
price_data: {
currency: 'pln',
product_data: {
name: storeItem.name,
},
unit_amount: storeItem.priceInCents,
},
quantity: 1,
};
}),
mode: 'payment',
success_url: `${process.env.CLIENT_URL}/success.html`,
cancel_url: `${process.env.CLIENT_URL}/cancel.html`,
});
console.log(session.url);
res.json({url: session.url});
} catch (e) {
// If there is an error send it to the client
console.log(e.message);
res.status(500).json({error: e.message});
}
});
I connected StripeCLI to my server using stripe listen --forward-to localhost:3001/webhook. Now I can capture the successful transaction event using the /webhook endpoint, but I have no way to return The transaction was successful to the client:
app.post('/webhook', (req, res) => {
const event = req.body;
if (event.type === 'checkout.session.completed') {
res.send('The transaction was successful');
}
});
After the suceesful payment the customer should be redirected back to your website. Where you can create success page.
success_url: `${process.env.CLIENT_URL}/success.html`,
If you want to get some data back from the Strapi after the paymant is successful page you can add this
success_url: `${process.env.CLIENT_URL}/success.html?&session_id={CHECKOUT_SESSION_ID}`
At the succes page you just deconstruct the data. And do whatever you want with them :)
If you deconstruct the object for example like this: (Next.js)
const stripe = require("stripe")(`${process.env.STRIPE_SECRET_KEY}`);
export async function getServerSideProps(params) {
const order = await stripe.checkout.sessions.retrieve(
params.query.session_id,
{
expand: ["line_items"],
},
);
const shippingRate = await stripe.shippingRates.retrieve(
"shr_1MJv",
);
return { props: { order, shippingRate } };
}
export default function Success({ order, shippingRate }) {
const route = useRouter();
Yo can log out the whole object to see whats inside
console.log(order);
If the payment was sucessfull you should get in prop variable: payment_status: "paid"
Stripe will automatically redirect the client to the success_url that you specified when you created a Stripe session.
You can use the webhook for saving the order in the database for example, but not to redirect the client.
I'm developing a MERN chat app and trying to replicate the feature which WhatsApp provides -> message sent/seen status, so for that I need to check whether the message I/user sent is successfully synced/updated/created in my MongoDB, I know similar functionality can be achieved in AWS Amplify using the event outboxMutationEnqueued which says Dispatched when a local change has been newly staged for synchronization with the cloud, so it works like whenever we are trying to push something to synchronize with the cloud, this event is going to be fired, and once it is finished, outboxMutationProcessed is going to be triggered which says Dispatched when a local change has finished syncrhonization with the cloud and is updated locally.
So we can listen to these events whenever we are trying to send a message, and once our message mutation is processed we are going to receive outboxMutationProcessed, and then we can update the status of the message to sent or single tick or delivered.
import Amplify, {Hub} from 'aws-amplify';
useEffect(() => {
const listener = Hub.listen('datastore', async (hubData) => {
const {event, data} = hubData.payload;
if (event === 'networkStatus') {
console.log('User has a network connection: ', data.active);
}
if (event === 'outboxMutationProcessed') {
if (data.model === Message)
console.log('Mutation has been synced with the cloud: ', data);
// set the message status to delivered.
}
})
return () => listener();
}, []);
So, the question, do we have something similar in MongoDB? I'm currently using React Native, Node, Express, Sockets, Mongoose, MongoDB.
Currently, my API end point and collections (for creating a new message and saving in to db):
I have 3 collections: users, 'messages', 'chatRooms'.
const mongoose = require('mongoose');
const MessageSchema = mongoose.Schema({
roomId: String,
senderId: String,
text: String,
status: String, // INQUEUE, SENT, DELIVERED, READ
}, {
timestamps: true,
});
module.exports = mongoose.model('Message', MessageSchema);
router.post('/create_message', checkAuth, async (req, res) => {
const {chatRoomId, senderId, text} = req.body;
try {
const message = new Message({
chatRoomId,
senderId,
text,
});
const result = await message.save();
return res.status(200).json({
type: 'success',
data: result,
});
} catch (error) {
return res.status(422).send({error: `${error.message}`});
}
});
// SOCKET IMPLEMENTATION FOR REALTIME FEATURE
socket.on('listener', async data => {
io.to(id).emit('new_message', data);
const message = new Message({
chatRoomId: data.roomId,
senderId: data.senderId,
text: data.text,
status: data.status, // data.status = 'INQUEUE'
});
await message.save();
// maybe something here...? not sure
// data.status = 'SENT' after successful creation of document.
});
Maybe an event which we can fire, during the await message.save(...something here...), and if it is successfully saved in our DB, we can send it to our frontend or using socket?
If anyone could provide an example, it would be really helpful!
Edit: I changed this up a bit.
schemas:
const UserSchema = new mongoose.Schema({
name: String,
online: Boolean,
isActive: Boolean,
})
const RoomSchema = new mongoose.Schema({
name: String,
members: [{ type: mongoose.Schema.Types.ObjectId, ref: 'users' }],
})
const MessageSchema = new mongoose.Schema({
roomId: { type: mongoose.Schema.Types.ObjectId, ref: 'rooms' },
senderId: { type: mongoose.Schema.Types.ObjectId, ref: 'users' },
text: String,
deliveredTo: [{ user: { type: mongoose.Schema.Types.ObjectId, ref: 'users' }, timestamp: Date }],
readBy: [{ user: { type: mongoose.Schema.Types.ObjectId, ref: 'users' }, timestamp: Date }],
})
app.js
Mongo.connect(uri, (err, client) => {
mongoose.connect(uri)
socketClient.on('connection', socket => {
//
//on load all users for login select
User.find().then(users => socket.emit('users', users))
....
//
//setting pipeline for readStream to only watch for changes in the messages collection
//where the roomId field is the current room the user has loaded
//setting fulldocument : 'updateLookup' option will always return the fulldocuemnt with any updates
//without this, the fulldocument will only be returned on our insert change
const pipeline = [{ $match: { 'fullDocument.roomId': ObjectId(room.id) } }]
const collection = client.db('chat').collection('messages')
const messageStream = collection.watch(pipeline, { fullDocument: 'updateLookup' })
messageStream.on('change', async function (change) {
const updates = change.updateDescription?.updatedFields
//
//if a new document is created in messages, then push that message to the client
if (change.operationType.match(/insert/i)) {
const doc = await Message.findById(change.fullDocument._id).populate(
'deliveredTo.user readBy.user'
)
const status = await getStatus(doc, room.id)
socket.emit('push message', { doc, status: status })
//
//if delivered to has been updated on a message then send to client
} else if (updates?.deliveredTo) {
const status = await getStatus(change.fullDocument, room.id)
socket.emit('status change', {
id: change.fullDocument._id,
status: status,
user: change.fullDocument.senderId,
})
} else if (updates?.readBy) {
//
//if readby has been updated on a message then send to client
const status = await getStatus(change.fullDocument, room.id)
socket.emit('status change', {
id: change.fullDocument._id,
status: status,
user: change.fullDocument.senderId,
})
}
})
...
})
})
When it goes to transfer, it shows the wrong token in the metamask
(async ()=>{
const contract = new web3.eth.Contract(ABI, contractAddress);
const transfer = await contract.methods.transfer(reciever, 1);
const data = await transfer.encodeABI();
if(window.ethereum.chainId == '0x61'){
ethereum
.request({
method: 'eth_sendTransaction',
params: [
{
from: ethereum.selectedAddress,
to: reciever,
gasPrice: '1000000',
gas: '',
data: data,
},
],
})
.then((txHash) => console.log(txHash))
.catch((error) => console.error);
} else {
ethereum.request({ method: 'wallet_switchEthereumChain', params:[{chainId: '0x61'}]})
}
})()
It should show the token, but it shows differently,
When transferring tokens, the transaction needs to be processed by the contract address (not by the token receiver). Note that the contract receiver is passed as the first argument of the transfer() function.
Solution: Replace to: reciever with to: contractAddress in the params section of your snippet.
I connect trust wallet like this:
//**Connect wallet:**
import WalletConnect from "#walletconnect/client";
import QRCodeModal from "#walletconnect/qrcode-modal";
const connector = new WalletConnect({
bridge: "https://bridge.walletconnect.org", // Required
qrcodeModal: QRCodeModal,
});
document.onreadystatechange = () => {
// Create a connector
// Check if connection is already established
if (!connector.connected) {
// create new session
connector.createSession();
}
}
When wallet connected I tried to sign a transfer message to transfer coins (I've tried with binance chain and thorchain - not working)
This is example how I sign msg:
const network = 931; // thorchain(rune)
const tx = {
fee: {
amounts: [
{
denom: "rune",
amount: "0.01"
}
],
gas: "2000000"
},
memo:"test",
"messages":[{
"sendCoinsMessage": {
fromAddress: 'thor1mkda02h8hsevykxwnnxs93tgtvgtz5djxteat0',
toAddress: "thor1mkda02h8hsevykxwnnxs93tgtvgtz5djxteat0",
amounts: [
{
denom: "rune",
amount: "1"
}
],
}
}]//end
};
Then I format request and sign it:
const request = self.connector._formatRequest({
method: 'trust_signTransaction',
params: [
{
network,
transaction: JSON.stringify(tx),
},
],
});
connector
._sendCallRequest(request)
.then(result => {
// Returns transaction signed in json or encoded format
console.log(result);
})
.catch(error => {
// Error returned when rejected
console.log('error')
console.error(error);
});
},
This is what I see in my trust wallet:
As a response I get from console this:
{"mode":"block","tx":{"fee":{"amount":[],"gas":"0"},"memo":"","msg":[],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A2yB9NhfIeEwTEDbs0ssZQcqtL/OWGuHqooeFllERot3"},"signature":"+kO2W2MfcSBwgLUF3zJUQK4e01YvIGXK8juzojEkE/RrVgrZJPRsthweuto4FJ1QK/MjUWuGlJiC+MjktlBexA=="}]}}
But the transaction is not sent to blockchain (If I go to blockchain exlorer I will not find it)
Also as you can notice in the response from console fee and gas always 0
What do I do?
UPD I have also tried method trust_sendTransaction instead of trust_signTransaction but didn't help
I build nodejs server, and now I'm testing it with mocha.
I have problem with async requests. I send my object to API, and then check record for with object in DB. I need use only co library and generators.
There's error:
TypeError: Cannot read property 'id' of null
It depends on insertUser object is null, but I don't know why object from database is null.
API works fine, and sequilize works fine.
it('it should POST a user', () => {
return co(function *() {
let user = {
name: "testInsertUser",
password: "12345"
};
let res = yield chai.request(server)
.post('/api/users')
.send(user);
res.should.have.status(HTTPStatus.OK);
res.body.should.be.a('object');
res.body.should.have.property('name').eql(user.name);
res.body.should.not.have.property('password');
//Find user in db
let insertUser =
yield models.User.findOne({
where: {
name: user.name,
password: user.password
}
});
res.body.should.have.property('id').eql(insertUser.id);
});
});
I solve my problem.
Code is fine, but password in db is hashing and I check hash password and order password