Skip to content

Commit b406540

Browse files
committed
feat(wasm-solana): add Transaction.id getter
Add id getter to Transaction and VersionedTransaction classes. Returns the first signature as base58, or "UNSIGNED" if no valid signature. Ticket: BTC-3002
1 parent 7f6112a commit b406540

5 files changed

Lines changed: 110 additions & 0 deletions

File tree

packages/wasm-solana/js/transaction.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ export class Transaction {
8787
return this._wasm.num_signatures;
8888
}
8989

90+
/**
91+
* Get the transaction ID (first signature as base58).
92+
*
93+
* For Solana, the transaction ID is the first signature.
94+
* Returns "UNSIGNED" if the transaction has no valid signatures.
95+
*
96+
* @example
97+
* ```typescript
98+
* const tx = Transaction.fromBytes(txBytes);
99+
* tx.addSignature(pubkey, signature);
100+
* console.log(tx.id); // Base58 encoded signature
101+
* ```
102+
*/
103+
get id(): string {
104+
return this._wasm.id;
105+
}
106+
90107
/**
91108
* Get the signable message payload (what gets signed)
92109
* This is the serialized message that signers sign

packages/wasm-solana/js/versioned.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,16 @@ export class VersionedTransaction {
168168
return this.inner.num_signatures;
169169
}
170170

171+
/**
172+
* Get the transaction ID (first signature as base58).
173+
*
174+
* For Solana, the transaction ID is the first signature.
175+
* Returns "UNSIGNED" if the transaction has no valid signatures.
176+
*/
177+
get id(): string {
178+
return this.inner.id;
179+
}
180+
171181
/**
172182
* Get the signable message payload.
173183
*/

packages/wasm-solana/src/wasm/transaction.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::error::WasmSolanaError;
99
use crate::transaction::{Transaction, TransactionExt};
1010
use crate::versioned::{detect_transaction_version, TxVersion, VersionedTransactionExt};
1111
use solana_message::VersionedMessage;
12+
use solana_sdk::bs58;
1213
use solana_transaction::versioned::VersionedTransaction;
1314
use wasm_bindgen::prelude::*;
1415

@@ -56,6 +57,24 @@ impl WasmTransaction {
5657
self.inner.num_signatures()
5758
}
5859

60+
/// Get the transaction ID (first signature as base58).
61+
///
62+
/// For Solana, the transaction ID is the first signature.
63+
/// Returns "UNSIGNED" if the first signature is all zeros (unsigned transaction).
64+
#[wasm_bindgen(getter)]
65+
pub fn id(&self) -> String {
66+
if let Some(sig) = self.inner.signatures.first() {
67+
let bytes: &[u8] = sig.as_ref();
68+
// Check if signature is all zeros (unsigned)
69+
if bytes.iter().all(|&b| b == 0) {
70+
return "UNSIGNED".to_string();
71+
}
72+
bs58::encode(bytes).into_string()
73+
} else {
74+
"UNSIGNED".to_string()
75+
}
76+
}
77+
5978
/// Get the signable message payload (what gets signed).
6079
///
6180
/// This is the serialized message that signers sign.
@@ -242,6 +261,24 @@ impl WasmVersionedTransaction {
242261
self.inner.num_signatures()
243262
}
244263

264+
/// Get the transaction ID (first signature as base58).
265+
///
266+
/// For Solana, the transaction ID is the first signature.
267+
/// Returns "UNSIGNED" if the first signature is all zeros (unsigned transaction).
268+
#[wasm_bindgen(getter)]
269+
pub fn id(&self) -> String {
270+
if let Some(sig) = self.inner.signatures.first() {
271+
let bytes: &[u8] = sig.as_ref();
272+
// Check if signature is all zeros (unsigned)
273+
if bytes.iter().all(|&b| b == 0) {
274+
return "UNSIGNED".to_string();
275+
}
276+
bs58::encode(bytes).into_string()
277+
} else {
278+
"UNSIGNED".to_string()
279+
}
280+
}
281+
245282
/// Get the signable message payload.
246283
#[wasm_bindgen]
247284
pub fn signable_payload(&self) -> js_sys::Uint8Array {

packages/wasm-solana/test/transaction.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@ describe("Transaction", () => {
126126
assert.strictEqual(instr.programId, "11111111111111111111111111111111");
127127
});
128128

129+
describe("id getter", () => {
130+
it("should return UNSIGNED for unsigned transaction", () => {
131+
const tx = Transaction.fromBytes(TEST_TX_BYTES);
132+
// The test transaction has an all-zeros signature (unsigned)
133+
assert.strictEqual(tx.id, "UNSIGNED");
134+
});
135+
136+
it("should return base58 signature after signing", () => {
137+
const tx = Transaction.fromBytes(TEST_TX_BYTES);
138+
const feePayer = tx.feePayer;
139+
140+
// Add a non-zero signature
141+
const signature = new Uint8Array(64);
142+
for (let i = 0; i < 64; i++) signature[i] = i + 1;
143+
tx.addSignature(feePayer, signature);
144+
145+
// ID should now be a base58-encoded string of the signature
146+
const id = tx.id;
147+
assert.notStrictEqual(id, "UNSIGNED");
148+
assert.ok(id.length > 20); // base58 encoded 64 bytes should be ~80+ chars
149+
});
150+
});
151+
129152
describe("signerIndex", () => {
130153
it("should return signer index for fee payer", () => {
131154
const tx = Transaction.fromBytes(TEST_TX_BYTES);

packages/wasm-solana/test/versioned.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,27 @@ describe("VersionedTransaction", () => {
9292
assert.strictEqual(tx.feePayer, tx2.feePayer);
9393
});
9494
});
95+
96+
describe("id getter", () => {
97+
it("should return UNSIGNED for unsigned transaction", () => {
98+
const tx = VersionedTransaction.fromBase64(LEGACY_TX_BASE64);
99+
// The test transaction has an all-zeros signature (unsigned)
100+
assert.strictEqual(tx.id, "UNSIGNED");
101+
});
102+
103+
it("should return base58 signature after signing", () => {
104+
const tx = VersionedTransaction.fromBase64(LEGACY_TX_BASE64);
105+
const feePayer = tx.feePayer;
106+
107+
// Add a non-zero signature
108+
const signature = new Uint8Array(64);
109+
for (let i = 0; i < 64; i++) signature[i] = i + 1;
110+
tx.addSignature(feePayer, signature);
111+
112+
// ID should now be a base58-encoded string of the signature
113+
const id = tx.id;
114+
assert.notStrictEqual(id, "UNSIGNED");
115+
assert.ok(id.length > 20); // base58 encoded 64 bytes should be ~80+ chars
116+
});
117+
});
95118
});

0 commit comments

Comments
 (0)