Web3 - can ETH transaction be cancelled? - node.js

I am trying to create a website where ethereum transaction can be made.
If I make an eth transaction using eth.sendTransaction({from:sender, to:receiver, value: amount}) can this transaction be cancelled?
I am asking this because I see that it doesn't take any promise or callback parameters, meaning I would have no idea whether the transaction has been made successfully or not?
Is it possible for web3 transactions to be cancelled? and if it is, how do I make sure that I get notified whether transaction has been made or not? (preferrably using promises or callback rather than having to check wallet every time)

It’s technically possible to cancel a transaction, but highly unlikely. Essentially, the only way to do so would be to try to send another transaction using the same account/nonce combination and hope it gets mined before the transaction you want to cancel is mined. It’s merely a side effect of how nonce is used to guarantee transaction order rather than a cancellation feature.
For your other question, web3.eth.sendTransaction does take a callback. It’s an optional second parameter after the options object and uses the error/result callback style. From the Web3js API:
web3.eth.sendTransaction(transactionObject [, callback])
Function - (optional) If you pass a callback the HTTP request is made asynchronous.
Using callbacks
If you want to make an asynchronous request, you can pass an optional callback as the last parameter to most functions. All callbacks are using an error first callback style

To cancel some ethereum pending transaction you need to:
Create a new transaction where you will send 0 ETH to yourselves.
You should increase the value of gas fees
You need to use the same nonce of the pending transaction.
Using web3js you can do something like this:
async function main() {
require('dotenv').config();
const { API_URL, PRIVATE_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
const myAddress = //put your address
const nonce = //put the nonce of pending tx
const maxFeePerGas = //increase the gas fee value comparated to the pending tx
const transaction = {
'to': myAddress,
'value': 0,
'gas': 21000,
'maxFeePerGas': maxFeePerGas,
'nonce': nonce
};
const signedTx = await web3.eth.accounts.signTransaction(transaction, PRIVATE_KEY);
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(error, hash) {
if (!error) {
console.log("The hash of your transaction is: ", hash);
} else {
console.log(error)
}
});
}
main();

Related

Anchor: How to ask the Nodejs backend to co-sign a transaction from the frontend without using Serum's Multisig?

Ideally, I want my Nodejs backend (which secures a keypair) to co-sign a transaction coming from the frontend app (assuming the user will sign the transaction using his / her Solana wallet). The pubkey of the backend will be also included as a signer account to the anchor program.
I can't see any tutorial from Anchor doing this, hopefully it makes sense. I was thinking that the frontend will call a backend API passing down the serialize parameters, on which the backend will sign, and will return a signature to the frontend with its public key.
I have no idea on:
What's the correct Anchor / Solana web3 to use to sign the transaction
How can I add the returning signature (of the backend) to the transaction
I initially don't want to use Multisig with this approach as it will just overcomplicate things. Just need some confirmation from the backend before submitting the transaction.
You probably want to do something like this (backend):
// create the transaction, with user's public key as the feePayer
// Sign the transaction as the backend
// We must partial sign because the transaction still requires the user signature
transaction.partialSign(backendKeypair)
// Serialize the transaction and convert to base64 to return it
const serializedTransaction = transaction.serialize({
// We will need the buyer to sign this transaction after it's returned to them
requireAllSignatures: false
})
const base64 = serializedTransaction.toString('base64')
// return base64 to the frontend
Then from the frontend:
// make sure you're passing user's public key in this request
const response = await fetch(`/your-api/`, {
method: 'POST',
...
})
// assuming your API returns JSON
const json = await response.json()
// Deserialize the transaction from the response
const transaction = Transaction.from(Buffer.from(json.transaction, 'base64'));
// have the user sign transaction using their connected wallet

Solana web3.Transaction.from return null

Here my code:
const web3 = require('#solana/web3.js');
const connection = new web3.Connection('https://solana-api.projectserum.com');
connection.onAccountChange(
wallet.publicKey,
(updatedAccountInfo, context) => {
let tx = web3.Transaction.from(updatedAccountInfo.data);
console.log('TX: ', tx);
},
'confirmed',
);
When Solana comes to my wallet, or when I send Solana via Solana CLI, the onAccountChange event is triggered, but shows null:
What am I doing wrong and how do I read the transaction data?
The callback for onAccountChange() returns an AccountInfo not a transaction - So you can read information about the account (Lamports, Owner, ...), but not the info on the transaction that triggered the change. Typescript will help datatype resolution.
https://solana-labs.github.io/solana-web3.js/classes/Connection.html#onAccountChange
You could use onAccountChange to listen for events when account changes in any way. In the callback that you've provided to the onAccountChange, you can call getConfirmedSignaturesForAddress2 to get transactions signatures and later on you can call getTransactions by providing signatures you've received in previous step.

Capture PayPal Order

When integrating with PayPal's V2 Orders API using the NodeJS SDK, when is the correct time to fulfil the order (e.g. ship a product to the customer).
async function captureOrder(orderId) {
try {
const request = new checkoutNodeJssdk.orders.OrdersCaptureRequest(orderId);
request.requestBody({});
const response = await payPalClient.client().execute(request);
// Is it safe to assume, if the above line didn't reject, the order has been successfully captured?...or do we need to verify the status of the order / capture etc.
return response;
}
catch (e) {
console.log(e)
}
}
If you want to do a positive check before shipment, you could check for a status of COMPLETED, which is the value documented in the API reference for success https://developer.paypal.com/docs/api/orders/v2/#orders_capture
A recoverable funding failure issue of INSTRUMENT_DECLINED, and any non-recoverable situations, should be dealt with according to the logic in https://developer.paypal.com/demo/checkout/#/pattern/server

How to insert documents to couchdb in bulk by chaincode

How to insert/write documents to couchdb in bulk by chaincode? It seems that the chaincode shim library(https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim) doesn't have such API.
For reading documents, it seems that there is an API named "GetQueryResult"(https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStub.GetQueryResult). In the string parameter "query", we can construct bulk get request.
But for inserting/writing documents, is there any bulk API for chaincode? Thanks in advance.
When chaincode executes, each PutState() adds a key/value to the transaction's proposed writeset. You can call PutState() multiple times in a chaincode, and the final set of keys/values will appear in the transaction's proposed writeset. Note that nothing is written to CouchDB at chaincode execution time.
Only when the transaction is submitted for ordering, will the transaction appear in a block that gets processed by all peers. Each peer validates the transactions and then applies the writesets of all valid transactions in the block to the CouchDB state database. Note that the state database commit is indeed using the CouchDB bulk update API (HTTP _bulk_docs), so you automatically get the desired bulk update performance in CouchDB.
If there are many key/value updates in the block, Fabric will actually group them into batches of 1000 (configurable using core.yaml maxBatchUpdateSize property) when committing to CouchDB to avoid any issues with excessively large payloads. Finally, to ensure all writes appear to Fabric as an atomic commit, Fabric writes a final savepoint per block to the CouchDB state database and flushes the CouchDB data to disk. This ensures any chaincode executions get a consistent view of the state data, and ensures Fabric can recover from any peer crash with full data integrity.
If you mean you want to batch multiple transactions in one batch then it is not advisable to do so because if they are updating the same value then while looking at the history, the final change can only be tracked. So, it is better to submit individual transactions.
Not sure if this answers your question but by document, lets assume you mean a PDF file.
This has two functions, read a key value pair and write one. Nothing special here.
'use strict';
const { Contract } = require('fabric-contract-api');
Class SomeName extends Contract {
async initLedger(ctx) { }
async writeDocument(ctx, documentId, documentAsString) {
await ctx.stub.putState(documentId, Buffer.from(documentAsString));
};
async getDocument(ctx, documentId) {
const docAsBytes = await ctx.stub.getState(documentId);
return carAsBytes.toString();
};
}
Now comes the server side code
const { FileSystemWallet, Gateway } = require('fabric-network');
async main() {
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = new FileSystemWallet(walletPath); // can be wherever your path is
const gateway = new Gateway();
await gateway.connect(ccp, { wallet, identity: 'user1', discovery: { enabled: false } });
const network = await gateway.getNetwork('mychannel');
// Get the contract from the network.
const contract = network.getContract('SomeName');
// Now over here lets assume you get the PDF file from a POST request using Multer
// We get the PDF file as a buffer, then convert to a string and send it
let pdfFile = req.file;
await contract.submitTransaction('writeDocument', req.file.buffer.toString('utf-8);
}
and if you want to retrieve the PDF file
const getDocument = async (req, res, next) => {
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = new FileSystemWallet(walletPath); // can be wherever your path is
const gateway = new Gateway();
await gateway.connect(ccp, { wallet, identity: 'user1', discovery: { enabled: false } });
const network = await gateway.getNetwork('mychannel');
// Get the contract from the network.
const contract = network.getContract('SomeName');
const pdfAsString= await contract.evaluateTransaction('getDocument', req.body.documentId);
// Now do whatever with this
};
So I hope this answers your question regarding document upload, that being said its not a good practice to upload documents on a blockchain as it can slow down the transaction speed significantly.
In that case, you can store the document on a local server such as mongoDB and then refer a sha256 hash of the document on the blockchain to cross check the authenticity of the document at a later stage.

Call a smart contract function using INFURA

I can not understand how I can prepare the transaction, sign it and then send it using INFURA. I expect to get something wrong with the NodeJS code I posted. Can anyone kindly help me solve this problem?
Note: I am able to send the transaction on my smart contract, but the EVM reverts the execution.
Part of the Smart Contract (Solidity) (the function that I want to call):
function testFunction() public pure returns (string memory) {
return "testSuccess";
}
The function used in NodeJS:
const printRequestTransaction = async (gcodeHash) => {
log(`Preparing a transaction to register that the gcode has been sent`.yellow);
// With every new transaction send using a specific wallet address, it needed to increase a nonce which is tied to the sender wallet
let nonce = await web3.eth.getTransactionCount(web3.eth.defaultAccount);
// Fetch the current transaction gas prices from https://ethgasstation.info/
let gasPrices = await getCurrentGasPrices();
// Build a new transaction object and sign it locally
let details = {
"to": process.env.SMART_CONTRACT_ADDRESS,
//"value": web3.utils.toHex(web3.utils.toWei(amountToSend.toString(), 'ether')),
"gas": 210000,
"gasPrice": gasPrices.low * 1000000000, // converts the gwei price to wei
"nonce": nonce,
//"data": gcodeHash,
"function": "testFunction",
"chainId": 3 // EIP 155 chainId - mainnet: 1, ropsten: 3, rinkeby: 4 (https://ethereum.stackexchange.com/questions/17051/how-to-select-a-network-id-or-is-there-a-list-of-network-ids/17101#17101)
};
const transaction = new EthereumTx(details);
// This is where the transaction is authorized on Local PC behalf. The private key is what unlocks Local PC wallet.
transaction.sign(Buffer.from(process.env.WALLET_PRIVATE_KEY, 'hex'));
// Compress the transaction info down into a transportable object
const serializedTransaction = transaction.serialize();
// The Web3 library is able to automatically determine the "from" address based on Local PC private key
// Submit the raw transaction details to the provider configured above (INFURA)
const transactionHash = await web3.eth.sendSignedTransaction('0x' + serializedTransaction.toString('hex')).then(function (receipt) {
log(`result of the invokation: ${receipt})`.red);
return receipt.transactionHash;
}).catch((err) => {
log(`error occurred: ${err})`.red);
});
//const transactionHash = "exampleHash";
_transactionHash = transactionHash;
// Now it is known the transaction ID, so let's build the public Etherscan url where the transaction details can be viewed.
const url = `https://ropsten.etherscan.io/tx/${transactionHash}`;
log(url.cyan);
log(`Note: please allow for 30 seconds before transaction appears on Etherscan`.yellow)
};
Output:
error occurred: Error: Transaction has been reverted by the EVM:
{
"blockHash": "0x6205c1b8ce2fde3693e85843dce684ff11472e9df01fd850bc99e5771b3262d5",
"blockNumber": 5024524,
"contractAddress": null,
"cumulativeGasUsed": "0x50170f",
"from": "0x8882528C7104e146E0500203C353C09922575385",
"gasUsed": "0x5236",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x0",
"to": "0x1732089F6F6EeFA942871707c5AE1909B60774EB",
"transactionHash": "0xf748c9a6bdb19e776db4d8a9802d2bf5f8b588158da387029529249aca00a499",
"transactionIndex": 1
})
I would like to pay particular attention to the let details = {...}; object for which I am not very sure it is correct, I have a little confusion about this.

Resources