js-algorand-sdk icon indicating copy to clipboard operation
js-algorand-sdk copied to clipboard

Function to write Txn objects to file

Open barnjamin opened this issue 3 years ago • 3 comments

Problem

When debugging transactions generated with the JS SDK a function like the Python SDK write_to_file would be great

https://github.com/algorand/py-algorand-sdk/blob/master/algosdk/transaction.py#L1526

Solution

In a nodejs context this should be straight forward by calling txn.bytesToSign() for unsigned or stxn.blob and writing bytes to a file.

In a web context it is a little more difficult, I've been using the following to force a download of a named list of transactions to a file:

export function download_txns(name, txns) {
    let b = new Uint8Array(0);
    for(const txn in txns){
        b = concatTypedArrays(b, txns[txn])
    }
    var blob = new Blob([b], {type: "application/octet-stream"});

    var link = document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    link.download = name;
    link.click();
}

export function concatTypedArrays(a, b) { // a, b TypedArray of same type
    var c = new (a.constructor)(a.length + b.length);
    c.set(a, 0);
    c.set(b, a.length);
    return c;
}

barnjamin avatar Oct 25 '21 10:10 barnjamin

Have you ever successfully written a txn to a file in a nodejs context? Whenever I do and attempt to sign the txn via goal, I always get the error:

msgpack decode error [pos 1]: only encoded map or array can be decoded into a struct

For completeness, I created a txn following the documentation (changing the addresses to ones in my sandbox) and then wrote that to a file:

// Construct the transaction
let params = await algodClient.getTransactionParams().do();
// comment out the next two lines to use suggested fee
params.fee = 1000;
params.flatFee = true;

const receiver = "RECEIVER...";
const enc = new TextEncoder();
const note = enc.encode("Hello World");
let amount = 1000000; // equals 1 ALGO
let sender = "SENDER...";
let txn = algosdk.makePaymentTxnWithSuggestedParams(sender, receiver, amount, undefined, note, params);

fs.writeFileSync('./test.txn', txn.bytesToSign());

Then attempt to sign it from goal:

goal clerk sign -i test.txn -o signed.txn

If I do the same process using the python SDK, it works no problem. It seems like it should be straight forward, so maybe I'm just missing something?

AustP avatar Jan 03 '22 00:01 AustP

@AustP the bytesToSign method does not serialize the transaction to a format recognized by goal; it's used to prepare the raw bytes to for immediate signing.

Instead, use the toByte method or algosdk.encodeUnsignedTransaction function to serialize it properly as an unsigned transaction. That should work, but let us know if it doesn't.

jasonpaulos avatar Jan 03 '22 17:01 jasonpaulos

@jasonpaulos

Using both toByte and encodeUnsignedTransaction gives the following error upon signing:

msgpack decode error [pos 5]: no matching struct field found when decoding stream map with key amt

AustP avatar Jan 03 '22 18:01 AustP

@jasonpaulos for context this seems to resolve the error https://discord.com/channels/491256308461207573/1103428629624533023/1103428629624533023 but both toByte or encodeUnsignedTransaction aren't working as of now

aorumbayev avatar Aug 24 '23 10:08 aorumbayev

My mistake before, goal expects a SignedTransaction, while the methods I mentioned above are for encoding unsigned Transactions.

We have since added encodeUnsignedSimulateTransaction, which will produce an encoded SignedTransaction (without signature of course) that goal is able to consume. We added this primarily for submitting transactions without signatures to the simulate endpoint, but it should work for this use case just as well.

I'm closing this issue now since I believe this function is the solution, but please feel free to comment if this doesn't work and I'll reopen.

jasonpaulos avatar Aug 30 '23 14:08 jasonpaulos