Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions packages/wasm-solana/js/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ export class Transaction {
return this._wasm.num_signatures;
}

/**
* Get the transaction ID (first signature as base58).
*
* For Solana, the transaction ID is the first signature.
* Returns "UNSIGNED" if the transaction has no valid signatures.
*
* @example
* ```typescript
* const tx = Transaction.fromBytes(txBytes);
* tx.addSignature(pubkey, signature);
* console.log(tx.id); // Base58 encoded signature
* ```
*/
get id(): string {
return this._wasm.id;
}

/**
* Get the signable message payload (what gets signed)
* This is the serialized message that signers sign
Expand Down Expand Up @@ -114,6 +131,14 @@ export class Transaction {
return this._wasm.to_bytes();
}

/**
* Serialize to network broadcast format.
* @returns The transaction as bytes ready for broadcast
*/
toBroadcastFormat(): Uint8Array {
return this.toBytes();
}

/**
* Get all account keys as Pubkey instances
* @returns Array of account public keys
Expand Down
18 changes: 18 additions & 0 deletions packages/wasm-solana/js/versioned.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ export class VersionedTransaction {
return this.inner.num_signatures;
}

/**
* Get the transaction ID (first signature as base58).
*
* For Solana, the transaction ID is the first signature.
* Returns "UNSIGNED" if the transaction has no valid signatures.
*/
get id(): string {
return this.inner.id;
}

/**
* Get the signable message payload.
*/
Expand Down Expand Up @@ -199,6 +209,14 @@ export class VersionedTransaction {
return Buffer.from(this.toBytes()).toString("base64");
}

/**
* Serialize to network broadcast format.
* @returns The transaction as bytes ready for broadcast
*/
toBroadcastFormat(): Uint8Array {
return this.toBytes();
}

/**
* Get static account keys (accounts stored directly in the message).
* For versioned transactions, additional accounts may be referenced via ALTs.
Expand Down
37 changes: 37 additions & 0 deletions packages/wasm-solana/src/wasm/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::error::WasmSolanaError;
use crate::transaction::{Transaction, TransactionExt};
use crate::versioned::{detect_transaction_version, TxVersion, VersionedTransactionExt};
use solana_message::VersionedMessage;
use solana_sdk::bs58;
use solana_transaction::versioned::VersionedTransaction;
use wasm_bindgen::prelude::*;

Expand Down Expand Up @@ -56,6 +57,24 @@ impl WasmTransaction {
self.inner.num_signatures()
}

/// Get the transaction ID (first signature as base58).
///
/// For Solana, the transaction ID is the first signature.
/// Returns "UNSIGNED" if the first signature is all zeros (unsigned transaction).
#[wasm_bindgen(getter)]
pub fn id(&self) -> String {
if let Some(sig) = self.inner.signatures.first() {
let bytes: &[u8] = sig.as_ref();
// Check if signature is all zeros (unsigned)
if bytes.iter().all(|&b| b == 0) {
return "UNSIGNED".to_string();
}
bs58::encode(bytes).into_string()
} else {
"UNSIGNED".to_string()
}
}

/// Get the signable message payload (what gets signed).
///
/// This is the serialized message that signers sign.
Expand Down Expand Up @@ -242,6 +261,24 @@ impl WasmVersionedTransaction {
self.inner.num_signatures()
}

/// Get the transaction ID (first signature as base58).
///
/// For Solana, the transaction ID is the first signature.
/// Returns "UNSIGNED" if the first signature is all zeros (unsigned transaction).
#[wasm_bindgen(getter)]
pub fn id(&self) -> String {
if let Some(sig) = self.inner.signatures.first() {
let bytes: &[u8] = sig.as_ref();
// Check if signature is all zeros (unsigned)
if bytes.iter().all(|&b| b == 0) {
return "UNSIGNED".to_string();
}
bs58::encode(bytes).into_string()
} else {
"UNSIGNED".to_string()
}
}

/// Get the signable message payload.
#[wasm_bindgen]
pub fn signable_payload(&self) -> js_sys::Uint8Array {
Expand Down
23 changes: 23 additions & 0 deletions packages/wasm-solana/test/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,29 @@ describe("Transaction", () => {
assert.strictEqual(instr.programId, "11111111111111111111111111111111");
});

describe("id getter", () => {
it("should return UNSIGNED for unsigned transaction", () => {
const tx = Transaction.fromBytes(TEST_TX_BYTES);
// The test transaction has an all-zeros signature (unsigned)
assert.strictEqual(tx.id, "UNSIGNED");
});

it("should return base58 signature after signing", () => {
const tx = Transaction.fromBytes(TEST_TX_BYTES);
const feePayer = tx.feePayer;

// Add a non-zero signature
const signature = new Uint8Array(64);
for (let i = 0; i < 64; i++) signature[i] = i + 1;
tx.addSignature(feePayer, signature);

// ID should now be a base58-encoded string of the signature
const id = tx.id;
assert.notStrictEqual(id, "UNSIGNED");
assert.ok(id.length > 20); // base58 encoded 64 bytes should be ~80+ chars
});
});

describe("signerIndex", () => {
it("should return signer index for fee payer", () => {
const tx = Transaction.fromBytes(TEST_TX_BYTES);
Expand Down
25 changes: 25 additions & 0 deletions packages/wasm-solana/test/versioned.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,29 @@ describe("VersionedTransaction", () => {
assert.strictEqual(tx.feePayer, tx2.feePayer);
});
});

describe("id getter", () => {
it("should return UNSIGNED for unsigned transaction", () => {
const bytes = Buffer.from(LEGACY_TX_BASE64, "base64");
const tx = VersionedTransaction.fromBytes(bytes);
// The test transaction has an all-zeros signature (unsigned)
assert.strictEqual(tx.id, "UNSIGNED");
});

it("should return base58 signature after signing", () => {
const bytes = Buffer.from(LEGACY_TX_BASE64, "base64");
const tx = VersionedTransaction.fromBytes(bytes);
const feePayer = tx.feePayer;

// Add a non-zero signature
const signature = new Uint8Array(64);
for (let i = 0; i < 64; i++) signature[i] = i + 1;
tx.addSignature(feePayer, signature);

// ID should now be a base58-encoded string of the signature
const id = tx.id;
assert.notStrictEqual(id, "UNSIGNED");
assert.ok(id.length > 20); // base58 encoded 64 bytes should be ~80+ chars
});
});
});