Is it possible to send transactions directly from a web application?
I am using the algosdk 1.17.0 npm package in my web application. (Note that I have managed to get around the “buffer not defined” issue by using a poly-fill)
However, I am getting issues with sending transactions (POST endpoints). I have tried sending transactions using the AlgoExplorer endpoint for the testnet and my local sandbox node.
When I use Algod (v1) class, I get what appears to be a CORS error.
I have also tried using a fetch request, but am unsure about how to structure the body and I do not think I will get a different response anyway.
It is OK to use a server/serverless function as a proxy between my web app and the algod node.
However, this can only be done for testnet/mainnet nodes (cannot connect to local nodes).
How do we send a transaction from a web application to our local sandbox node? Is it possible?
Hi @plasmatech8, like you said the error in your image originates from the CORS policy.
To send a transaction from a web application to our local sandbox node you simply set the parameters resolving to your local sandbox when you create the client, which seems you have done, however CORS blocks it.
I personally never had much success getting around CORS policies, but depending on your browser/language you will have to google the solution that works for you to disable it.
Do you have to use the local sandbox?
Alternatively you can connect to the TestNet:
I’ve not seen a cors issue using the sandbox alone.
Are you running on the same machine as the sandbox?
do you have some different hostname for the site you’re serving?
Is it definitely the Sandbox algod running on port 4001?
Have you changed any of your chrome settings?
Hi @plasmatech8, I’m getting the “buffer not defined” error, too. Can you share the polyfill solution that you used? I see a lot of different approaches on the Web, but if yours works, then it’s probably better to try yours first.
Thank you, @gorazd. I found another workaround that was more specific to my use-case because your solution produced the window is undefined error. So I tried to dynamically load Buffer based on your code after the window object is loaded, but then the buffer not defined error occurred again. It seems the solution to this problem is heavily dependent upon which bundler the app is using. For these reasons, I don’t think Buffer should be used at all in the SDK because it can easily be replaced with Uint8Array, which is functionally equivalent and works in both Node and the browser contexts.
In my case, I’m using Vite; so the solutions above don’t work for reasons that I don’t have time to troubleshoot right now. But maybe they will work for others who are using Webpack.
fyi, I am using the SvelteKit web framework (which uses Vite) and using this for my polyfill:
import { Buffer } from 'buffer';
import { browser } from '$app/env';
if (browser) window.Buffer = Buffer;
However, I recently found that I did not need it for MyAlgoConnect. I think this is because I set it up so that it only instantiates the MyAlgoConnect object in client-side (using the connect function in a class that I made) - so it avoids the Vite server-side rendering mechanism.
I deleted the code I was using and now I am getting an "path" has been externalized for browser compatibility and cannot be accessed in client code error from using functions in algosdk and I cannot remember out how I got around that problem…
But I have tried to re-do the code using fetch requests to get the same CORs error as before:
<script>
async function sendRequest() {
const MyAlgoConnect = (await import('@randlabs/myalgo-connect')).default;
const algosdk = (await import('algosdk')).default;
const algodToken = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
const algodServer = 'http://localhost';
const algodPort = 4001;
// 1. Init clients
// const client = new algosdk.Algodv2(algodToken, algodServer, algodPort);
const myalgo = new MyAlgoConnect();
// 2. Connect and get accounts
const accounts = await myalgo.connect();
console.log(accounts);
// 3. Get suggested params
// const sp = await client.getTransactionParams().do();
// ^^^ Does not work due to error: "path" has been externalized
// for browser compatibility and cannot be accessed in client code
const spRes = await fetch(`${algodServer}:${algodPort}/v2/transactions/params`, {
headers: {
'X-Algo-API-Token': algodToken
}
});
const spJson = await spRes.json();
const sp = {
fee: spJson['fee'],
genesisHash: spJson['genesis-hash'],
genesisID: spJson['genesis-id'],
minFee: spJson['min-fee'],
lastRound: spJson['last-round'],
firstRound: spJson['last-round'] - 10 || 0
};
console.log(sp);
// 4. Build transaction
const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
from: 'JROO4EICILSFPLNI7FMGAI3NX45ZP4B4TLQ4OK4N7D4CCR7QODEDVUYH7A',
to: 'MK37YS6S7XI4L4LQ6SIO47W6KUP6RHWP6VPCTGZZWMKI4EBBPZU4ZVCWHE',
amount: 1_000_000,
suggestedParams: sp
});
console.log(txn);
// 5. Sign transaction
const txnSigned = await myalgo.signTransaction(txn.toByte());
console.log(txnSigned);
// 6. Send transaction
// const res = await client.sendRawTransaction(txnSigned.blob).do();
// ^^^ Does not work due to error: "path" has been externalized
// for browser compatibility and cannot be accessed in client code
const res = await fetch(`${algodServer}:${algodPort}/v2/transactions`, {
method: 'POST',
body: txnSigned.blob.toString(),
headers: {
'X-Algo-API-Token': algodToken
}
});
console.log(res);
}
</script>
<button class="btn" on:click={sendRequest}>Send Request</button>
It looks like the spRes query is working properly, but sending the transaction fails.
I think the issue is that your /v2/transactions query fails with 400, and CORS may not be enabled on 400 errors.
I see you are not using fetch directly rather than using the SDK, because of some issue. Can you show the detailed issue you are seeing?
The SDK handles many operations that may be difficult to get right manually using fetch.
It is possible you are missing some headers: hdrs['Content-Type'] = 'application/x-binary';
It is also possible you should not use .toString().
I installed path-browserify and I think it is working now.
I tried doing this before along (and a bunch of other things), didn’t see any changes then - but I might have assumed that the hot-reload was sufficient to see changes, where I really need to do a complete restart of my dev server.
In my svelte.config.js (which would be similar in an ordinary vite config)