Skip to content
This repository was archived by the owner on Jan 14, 2026. It is now read-only.

Commit 904dbcc

Browse files
authored
feat: add note field support and input transaction ID retrieval (#30)
* feat: add note field support and input transaction ID retrieval Add the ability to set a custom note on the user-signed input transaction and retrieve its transaction ID for tracking purposes. Changes: - Add optional `note` config to SwapComposerConfig and newSwap() - Add `getInputTransactionId()` method to SwapComposer - Track the input transaction index during swap processing - Add comprehensive tests for note configuration and ID retrieval The note is only applied to the user-signed payment or asset transfer transaction (not pre-signed or middleware transactions). The transaction ID is available after calling buildGroup(), sign(), or execute(). * docs: add note and getInputTransactionId to README Document the new transaction tracking features: - Add 'Transaction Tracking' section with usage example - Add 'note' parameter to newSwap() API reference table - Add 'getInputTransactionId()' to SwapComposer methods table
1 parent 91d270a commit 904dbcc

File tree

4 files changed

+462
-4
lines changed

4 files changed

+462
-4
lines changed

packages/deflex/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,28 @@ console.log(`Confirmed in round ${result.confirmedRound}`)
120120
console.log('Transaction IDs:', result.txIds)
121121
```
122122

123+
### Transaction Tracking
124+
125+
Add a custom note to the input transaction for tracking purposes, and retrieve its transaction ID after execution:
126+
127+
```typescript
128+
const swap = await deflex.newSwap({
129+
quote,
130+
address: activeAddress,
131+
signer: transactionSigner,
132+
slippage: 1,
133+
note: new TextEncoder().encode('tracking-id-123'), // Custom note for tracking
134+
})
135+
136+
const result = await swap.execute()
137+
138+
// Get the transaction ID of the user-signed input transaction
139+
const inputTxId = swap.getInputTransactionId()
140+
console.log('Input transaction ID:', inputTxId)
141+
```
142+
143+
The `note` is applied only to the user-signed payment or asset transfer transaction (not pre-signed or middleware transactions). The transaction ID is available after calling `buildGroup()`, `sign()`, or `execute()`.
144+
123145
### Transaction Signing
124146

125147
The SDK supports both standard `algosdk.TransactionSigner` and ARC-1 compliant signer functions.
@@ -395,6 +417,7 @@ async newSwap(config: SwapComposerConfig): Promise<SwapComposer>
395417
| `address` | Signer address | `string` |
396418
| `slippage` | Slippage tolerance as percentage (e.g., 1 for 1%) | `number` |
397419
| `signer` | Transaction signer function | `algosdk.TransactionSigner \| ((txnGroup: Transaction[], indexesToSign: number[]) => Promise<(Uint8Array \| null)[]>)` |
420+
| `note` | Optional note for the user-signed input transaction (for tracking purposes) | `Uint8Array` |
398421

399422
#### DeflexClient.needsAssetOptIn()
400423

@@ -458,6 +481,7 @@ Builder for constructing and executing atomic swap transaction groups, returned
458481
| `execute(waitRounds?)` | Sign, submit, and wait for confirmation | `waitRounds?: number` (default: 4) | `Promise<{ confirmedRound: bigint, txIds: string[], methodResults: ABIResult[] }>` |
459482
| `getStatus()` | Get current status: `BUILDING`, `BUILT`, `SIGNED`, `SUBMITTED`, or `COMMITTED` | None | `SwapComposerStatus` |
460483
| `count()` | Get the number of transactions in the group | None | `number` |
484+
| `getInputTransactionId()` | Get the transaction ID of the user-signed input transaction (available after `buildGroup()`, `sign()`, or `execute()`) | None | `string \| undefined` |
461485

462486
## Documentation
463487

packages/deflex/src/client.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,10 @@ export class DeflexClient {
409409
address: string
410410
slippage: number
411411
signer: TransactionSigner | SignerFunction
412+
/** Optional note field for the user-signed input transaction (payment or asset transfer) */
413+
note?: Uint8Array
412414
}): Promise<SwapComposer> {
413-
const { quote, address, slippage, signer } = config
415+
const { quote, address, slippage, signer, note } = config
414416

415417
const swapResponse = await this.fetchSwapTransactions({
416418
quote,
@@ -426,6 +428,7 @@ export class DeflexClient {
426428
address,
427429
signer,
428430
middleware: this.middleware,
431+
note,
429432
})
430433

431434
return composer

packages/deflex/src/composer.ts

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ export interface SwapComposerConfig {
7373
readonly signer: TransactionSigner | SignerFunction
7474
/** Middleware to apply during swap composition */
7575
readonly middleware?: SwapMiddleware[]
76+
/** Optional note field for the user-signed input transaction (payment or asset transfer) */
77+
readonly note?: Uint8Array
7678
}
7779

7880
/**
@@ -110,6 +112,8 @@ export class SwapComposer {
110112
private readonly address: string
111113
private readonly signer: TransactionSigner | SignerFunction
112114
private readonly middleware: SwapMiddleware[]
115+
private readonly note?: Uint8Array
116+
private inputTransactionIndex?: number
113117

114118
/**
115119
* Create a new SwapComposer instance
@@ -150,6 +154,7 @@ export class SwapComposer {
150154
this.address = this.validateAddress(config.address)
151155
this.signer = config.signer
152156
this.middleware = config.middleware ?? []
157+
this.note = config.note
153158
}
154159

155160
/**
@@ -282,7 +287,8 @@ export class SwapComposer {
282287
}
283288

284289
// Process swap transactions and execute afterSwap hooks
285-
const processedTxns = await this.processSwapTransactions()
290+
const { txns: processedTxns, userSignedTxnRelativeIndex } =
291+
await this.processSwapTransactions()
286292
const afterTxns = await this.executeMiddlewareHooks('afterSwap')
287293

288294
// Check total length before adding swap and afterSwap transactions
@@ -295,6 +301,12 @@ export class SwapComposer {
295301
)
296302
}
297303

304+
// Calculate the absolute index of the user-signed input transaction
305+
// This is: current ATC count (user txns + beforeSwap) + relative index within processed txns
306+
if (userSignedTxnRelativeIndex !== undefined) {
307+
this.inputTransactionIndex = this.atc.count() + userSignedTxnRelativeIndex
308+
}
309+
298310
// Add swap transactions
299311
for (const txnWithSigner of processedTxns) {
300312
this.atc.addTransaction(txnWithSigner)
@@ -425,6 +437,46 @@ export class SwapComposer {
425437
}
426438
}
427439

440+
/**
441+
* Get the transaction ID of the user-signed input transaction
442+
*
443+
* Returns the transaction ID of the payment or asset transfer transaction
444+
* that sends the input asset. This is the transaction whose note field can
445+
* be customized via the `note` config option.
446+
*
447+
* The transaction ID is only available after the group has been built
448+
* (after calling buildGroup(), sign(), submit(), or execute()).
449+
*
450+
* @returns The transaction ID, or undefined if the group hasn't been built yet
451+
* or if the input transaction index couldn't be determined
452+
*
453+
* @example
454+
* ```typescript
455+
* const swap = await deflex.newSwap({
456+
* quote,
457+
* address,
458+
* slippage,
459+
* signer,
460+
* note: new TextEncoder().encode('tracking-123')
461+
* })
462+
*
463+
* await swap.execute()
464+
* const inputTxId = swap.getInputTransactionId()
465+
* console.log('Input transaction ID:', inputTxId)
466+
* ```
467+
*/
468+
getInputTransactionId(): string | undefined {
469+
if (this.getStatus() < SwapComposerStatus.BUILT) {
470+
return undefined
471+
}
472+
if (this.inputTransactionIndex === undefined) {
473+
return undefined
474+
}
475+
const txns = this.atc.buildGroup()
476+
const txn = txns[this.inputTransactionIndex]?.txn
477+
return txn?.txID()
478+
}
479+
428480
/**
429481
* Validates an Algorand address
430482
*/
@@ -438,10 +490,15 @@ export class SwapComposer {
438490
/**
439491
* Processes app opt-ins and decodes swap transactions from API response
440492
*/
441-
private async processSwapTransactions(): Promise<TransactionWithSigner[]> {
493+
private async processSwapTransactions(): Promise<{
494+
txns: TransactionWithSigner[]
495+
userSignedTxnRelativeIndex?: number
496+
}> {
442497
const appOptIns = await this.processRequiredAppOptIns()
443498

444499
const swapTxns: TransactionWithSigner[] = []
500+
let userSignedTxnRelativeIndex: number | undefined
501+
445502
for (let i = 0; i < this.deflexTxns.length; i++) {
446503
const deflexTxn = this.deflexTxns[i]
447504
if (!deflexTxn) continue
@@ -459,6 +516,13 @@ export class SwapComposer {
459516
})
460517
} else {
461518
// User transaction - use configured signer
519+
// Set the note if provided (using type assertion since note is readonly but safe to modify before signing)
520+
if (this.note !== undefined) {
521+
;(txn as { note: Uint8Array }).note = this.note
522+
}
523+
// Track the relative index within processed transactions (after app opt-ins)
524+
userSignedTxnRelativeIndex = appOptIns.length + swapTxns.length
525+
462526
swapTxns.push({
463527
txn,
464528
signer: this.defaultSigner,
@@ -471,7 +535,10 @@ export class SwapComposer {
471535
}
472536
}
473537

474-
return [...appOptIns, ...swapTxns]
538+
return {
539+
txns: [...appOptIns, ...swapTxns],
540+
userSignedTxnRelativeIndex,
541+
}
475542
}
476543

477544
/**

0 commit comments

Comments
 (0)