Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9f35acb
chore(merge-main): merge latest from main with tailwind v4 to optiona…
p2arthur Nov 4, 2025
0e78c0f
feat(optional_sender): Add optional sender at latest squashed to solv…
p2arthur Nov 7, 2025
78166ad
chore(package-lock): update package lock to reflect dependencies from…
p2arthur Nov 7, 2025
6c2ad60
feat(search_params) add optional sender with auto populate to search …
p2arthur Nov 10, 2025
406f776
test(optional_sender): add optional sender to url params test
p2arthur Nov 11, 2025
a22d374
chore: add DataProvider to txn wizard test
PatrickDinh Nov 13, 2025
bf2ae35
chore: lint-fix
PatrickDinh Nov 13, 2025
fac30c1
feat(transaction-wizard): auto-populate sender from localnet dispense…
p2arthur Nov 13, 2025
669d16f
fix(search_params): fix two tests that were failing only on CI/CD
p2arthur Nov 13, 2025
799967c
fix(test): add racing condition to wait asset-opt out render before t…
p2arthur Nov 13, 2025
02b70e6
fix(test): add racing condition to wait render before testing
p2arthur Nov 13, 2025
a874ce9
fix(test): add racing condition to wait render before testing
p2arthur Nov 13, 2025
d65e4b4
reafactor(non-null): remove non-null assertion from the transaction m…
p2arthur Nov 14, 2025
d7215ae
test(optional_sender): updated tests to use kmd instead of assuming t…
p2arthur Nov 16, 2025
cfe5917
chore(optional_sender): cleanup to remove repeated tests and optional…
p2arthur Nov 16, 2025
a7a8d85
fix(conflicts) fix merge conflicts from feature branch
p2arthur Nov 19, 2025
cc92c31
fix(conflicts) fix merge conflicts from feature branch
p2arthur Nov 19, 2025
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
21 changes: 21 additions & 0 deletions src/features/accounts/components/transaction-sender-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { AddressOrNfdLink, AddressOrNfdLinkProps } from './address-or-nfd-link'
import { cn } from '@/features/common/utils'

export type Props = AddressOrNfdLinkProps & { autoPopulated?: boolean }

export default function TransactionSenderLink(props: Props) {
const { autoPopulated, className, ...rest } = props

return (
<div className="flex items-center">
<AddressOrNfdLink className={cn(className, autoPopulated && 'text-yellow-500')} {...rest} />

{autoPopulated && (
<span className="group ml-1 cursor-help text-yellow-500">
<span>?</span>
<div className="absolute z-10 hidden rounded-sm border-2 border-gray-300/20 p-1 group-hover:block">auto populated</div>
</span>
)}
</div>
)
}
4 changes: 2 additions & 2 deletions src/features/forms/components/address-form-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ function ResolveNfdAddress({ nfd, onNfdResolved }: ResolveNfdAddressProps) {

export function AddressFormItem({ field, resolvedAddressField, label, ...props }: AddressFormItemProps) {
const { watch, setValue } = useFormContext<AddressOrNfdFieldSchema | OptionalAddressOrNfdFieldSchema>()
const value = watch(field)
const resolvedAddress = watch(resolvedAddressField)
const value = watch(field) as string
const resolvedAddress = watch(resolvedAddressField) as string

const setAddress = useCallback((address: string) => setValue(resolvedAddressField, address), [resolvedAddressField, setValue])
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { numberSchema } from '@/features/forms/data/common'
import { addressFieldSchema, commonSchema, optionalAddressFieldSchema, senderFieldSchema } from '../data/common'
import { addressFieldSchema, commonSchema, optionalAddressFieldSchema } from '@/features/transaction-wizard/data/common'
import { z } from 'zod'
import { useCallback, useMemo } from 'react'
import { zfd } from 'zod-form-data'
Expand All @@ -17,6 +17,7 @@ import SvgAlgorand from '@/features/common/components/icons/algorand'
import { TransactionBuilderNoteField } from './transaction-builder-note-field'
import { asAddressOrNfd, asOptionalAddressOrNfd } from '../mappers/as-address-or-nfd'
import { ActiveWalletAccount } from '@/features/wallet/types/active-wallet'
import resolveSenderAddress from '../utils/resolve-sender-address'

const senderLabel = 'Sender'
const receiverLabel = 'Receiver'
Expand All @@ -25,7 +26,7 @@ const closeRemainderToLabel = 'Close remainder to'
const formSchema = z
.object({
...commonSchema,
...senderFieldSchema,
sender: optionalAddressFieldSchema,
closeRemainderTo: addressFieldSchema,
receiver: optionalAddressFieldSchema,
amount: numberSchema(z.number({ required_error: 'Required', invalid_type_error: 'Required' }).min(0).optional()),
Expand Down Expand Up @@ -63,7 +64,7 @@ export function AccountCloseTransactionBuilder({ mode, transaction, activeAccoun
onSubmit({
id: transaction?.id ?? randomGuid(),
type: BuildableTransactionType.AccountClose,
sender: data.sender,
sender: await resolveSenderAddress(data.sender),
closeRemainderTo: data.closeRemainderTo,
receiver: asOptionalAddressOrNfd(data.receiver),
amount: data.amount,
Expand All @@ -77,7 +78,7 @@ export function AccountCloseTransactionBuilder({ mode, transaction, activeAccoun
const defaultValues = useMemo<Partial<z.infer<typeof formData>>>(() => {
if (mode === TransactionBuilderMode.Edit && transaction) {
return {
sender: transaction.sender,
sender: transaction.sender?.autoPopulated ? undefined : transaction.sender,
closeRemainderTo: transaction.closeRemainderTo,
receiver: transaction.receiver,
amount: transaction.amount,
Expand Down Expand Up @@ -114,7 +115,7 @@ export function AccountCloseTransactionBuilder({ mode, transaction, activeAccoun
{helper.addressField({
field: 'sender',
label: senderLabel,
helpText: 'Account to be closed. Sends the transaction and pays the fee',
helpText: 'Account to be closed. Sends the transaction and pays the fee - optional for simulating ',
placeholder: ZERO_ADDRESS,
})}
{helper.addressField({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import algosdk from 'algosdk'
import { bigIntSchema, numberSchema } from '@/features/forms/data/common'
import { senderFieldSchema, commonSchema, onCompleteFieldSchema, onCompleteOptions } from '@/features/transaction-wizard/data/common'
import {
commonSchema,
onCompleteFieldSchema,
onCompleteOptions,
optionalAddressFieldSchema,
} from '@/features/transaction-wizard/data/common'
import { z } from 'zod'
import { zfd } from 'zod-form-data'
import { Form } from '@/features/forms/components/form'
Expand All @@ -16,10 +21,11 @@ import { TransactionBuilderMode } from '../data'
import { TransactionBuilderNoteField } from './transaction-builder-note-field'
import { asAddressOrNfd } from '../mappers/as-address-or-nfd'
import { ActiveWalletAccount } from '@/features/wallet/types/active-wallet'
import resolveSenderAddress from '../utils/resolve-sender-address'

const formData = zfd.formData({
...commonSchema,
...senderFieldSchema,
sender: optionalAddressFieldSchema,
...onCompleteFieldSchema,
applicationId: bigIntSchema(z.bigint({ required_error: 'Required', invalid_type_error: 'Required' })),
extraProgramPages: numberSchema(z.number().min(0).max(3).optional()),
Expand Down Expand Up @@ -47,7 +53,7 @@ export function AppCallTransactionBuilder({ mode, transaction, activeAccount, de
id: transaction?.id ?? randomGuid(),
type: BuildableTransactionType.AppCall,
applicationId: BigInt(values.applicationId),
sender: values.sender,
sender: await resolveSenderAddress(values.sender),
onComplete: Number(values.onComplete),
extraProgramPages: values.extraProgramPages,
fee: values.fee,
Expand All @@ -63,7 +69,7 @@ export function AppCallTransactionBuilder({ mode, transaction, activeAccount, de
if (mode === TransactionBuilderMode.Edit && transaction) {
return {
applicationId: transaction.applicationId !== undefined ? BigInt(transaction.applicationId) : undefined,
sender: transaction.sender,
sender: transaction.sender?.autoPopulated ? undefined : transaction.sender,
onComplete: transaction.onComplete.toString(),
extraProgramPages: transaction.extraProgramPages,
fee: transaction.fee,
Expand Down Expand Up @@ -117,7 +123,7 @@ export function AppCallTransactionBuilder({ mode, transaction, activeAccount, de
{helper.addressField({
field: 'sender',
label: 'Sender',
helpText: 'Account to call from. Sends the transaction and pays the fee',
helpText: 'Account to call from. Sends the transaction and pays the fee - optional for simulating',
})}
{defaultValues.applicationId === 0n &&
helper.numberField({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import algosdk from 'algosdk'
import { numberSchema } from '@/features/forms/data/common'
import {
senderFieldSchema,
commonSchema,
onCompleteOptionsForAppCreate,
onCompleteForAppCreateFieldSchema,
optionalAddressFieldSchema,
} from '@/features/transaction-wizard/data/common'
import { z } from 'zod'
import { zfd } from 'zod-form-data'
Expand All @@ -21,10 +21,11 @@ import { TransactionBuilderMode } from '../data'
import { TransactionBuilderNoteField } from './transaction-builder-note-field'
import { asAddressOrNfd } from '../mappers/as-address-or-nfd'
import { ActiveWalletAccount } from '@/features/wallet/types/active-wallet'
import resolveSenderAddress from '../utils/resolve-sender-address'

const formData = zfd.formData({
...commonSchema,
...senderFieldSchema,
sender: optionalAddressFieldSchema,
...onCompleteForAppCreateFieldSchema,
approvalProgram: zfd.text(z.string({ required_error: 'Required', invalid_type_error: 'Required' })),
clearStateProgram: zfd.text(z.string({ required_error: 'Required', invalid_type_error: 'Required' })),
Expand Down Expand Up @@ -57,7 +58,7 @@ export function ApplicationCreateTransactionBuilder({ mode, transaction, activeA
type: BuildableTransactionType.ApplicationCreate,
approvalProgram: values.approvalProgram,
clearStateProgram: values.clearStateProgram,
sender: values.sender,
sender: await resolveSenderAddress(values.sender),
onComplete: Number(values.onComplete),
extraProgramPages: values.extraProgramPages,
globalInts: values.globalInts,
Expand All @@ -78,7 +79,7 @@ export function ApplicationCreateTransactionBuilder({ mode, transaction, activeA
return {
approvalProgram: transaction.approvalProgram,
clearStateProgram: transaction.clearStateProgram,
sender: transaction.sender,
sender: transaction.sender?.autoPopulated ? undefined : transaction.sender,
onComplete: transaction.onComplete.toString(),
extraProgramPages: transaction.extraProgramPages,
globalInts: transaction.globalInts,
Expand Down Expand Up @@ -140,7 +141,7 @@ export function ApplicationCreateTransactionBuilder({ mode, transaction, activeA
{helper.addressField({
field: 'sender',
label: 'Sender',
helpText: 'Account to create from. Sends the transaction and pays the fee',
helpText: 'Account to create from. Sends the transaction and pays the fee - optional for simulating',
})}
{helper.numberField({
field: 'globalInts',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { bigIntSchema } from '@/features/forms/data/common'
import { senderFieldSchema, commonSchema } from '@/features/transaction-wizard/data/common'
import { commonSchema, optionalAddressFieldSchema } from '@/features/transaction-wizard/data/common'
import { z } from 'zod'
import { zfd } from 'zod-form-data'
import { Form } from '@/features/forms/components/form'
Expand All @@ -15,10 +15,11 @@ import { TransactionBuilderMode } from '../data'
import { TransactionBuilderNoteField } from './transaction-builder-note-field'
import { asAddressOrNfd } from '../mappers/as-address-or-nfd'
import { ActiveWalletAccount } from '@/features/wallet/types/active-wallet'
import resolveSenderAddress from '../utils/resolve-sender-address'

const formData = zfd.formData({
...commonSchema,
...senderFieldSchema,
sender: optionalAddressFieldSchema,
applicationId: bigIntSchema(z.bigint({ required_error: 'Required', invalid_type_error: 'Required' })),
approvalProgram: zfd.text(z.string({ required_error: 'Required', invalid_type_error: 'Required' })),
clearStateProgram: zfd.text(z.string({ required_error: 'Required', invalid_type_error: 'Required' })),
Expand Down Expand Up @@ -47,7 +48,7 @@ export function ApplicationUpdateTransactionBuilder({ mode, transaction, activeA
applicationId: BigInt(values.applicationId),
approvalProgram: values.approvalProgram,
clearStateProgram: values.clearStateProgram,
sender: values.sender,
sender: await resolveSenderAddress(values.sender),
fee: values.fee,
validRounds: values.validRounds,
args: values.args.map((arg) => arg.value),
Expand All @@ -63,7 +64,7 @@ export function ApplicationUpdateTransactionBuilder({ mode, transaction, activeA
applicationId: transaction.applicationId !== undefined ? BigInt(transaction.applicationId) : undefined,
approvalProgram: transaction.approvalProgram,
clearStateProgram: transaction.clearStateProgram,
sender: transaction.sender,
sender: transaction.sender?.autoPopulated ? undefined : transaction.sender,
fee: transaction.fee,
validRounds: transaction.validRounds,
note: transaction.note,
Expand Down Expand Up @@ -116,7 +117,7 @@ export function ApplicationUpdateTransactionBuilder({ mode, transaction, activeA
{helper.addressField({
field: 'sender',
label: 'Sender',
helpText: 'Account to update from. Sends the transaction and pays the fee',
helpText: 'Account to update from. Sends the transaction and pays the fee - optional for simulating',
})}
{helper.arrayField({
field: 'args',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { bigIntSchema, decimalSchema } from '@/features/forms/data/common'
import { addressFieldSchema, commonSchema, receiverFieldSchema, senderFieldSchema } from '../data/common'
import { addressFieldSchema, commonSchema, optionalAddressFieldSchema, receiverFieldSchema } from '../data/common'
import { z } from 'zod'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { zfd } from 'zod-form-data'
Expand All @@ -22,13 +22,14 @@ import { useDebounce } from 'use-debounce'
import { TransactionBuilderMode } from '../data'
import { TransactionBuilderNoteField } from './transaction-builder-note-field'
import { asAddressOrNfd } from '../mappers/as-address-or-nfd'
import resolveSenderAddress from '../utils/resolve-sender-address'

const clawbackTargetLabel = 'Clawback target'

export const assetClawbackFormSchema = z
.object({
...commonSchema,
...senderFieldSchema,
sender: optionalAddressFieldSchema,
...receiverFieldSchema,
clawbackTarget: addressFieldSchema,
asset: z
Expand Down Expand Up @@ -83,7 +84,7 @@ function FormFields({ helper, asset }: FormFieldsProps) {
{helper.addressField({
field: 'sender',
label: 'Sender',
helpText: 'The clawback account of the asset. Sends the transaction and pays the fee',
helpText: 'The clawback account of the asset. Sends the transaction and pays the fee - optional for simulating',
placeholder: ZERO_ADDRESS,
})}
{helper.addressField({
Expand Down Expand Up @@ -186,7 +187,7 @@ export function AssetClawbackTransactionBuilder({ mode, transaction, onSubmit, o
id: transaction?.id ?? randomGuid(),
type: BuildableTransactionType.AssetClawback,
asset: data.asset,
sender: data.sender,
sender: await resolveSenderAddress(data.sender),
receiver: data.receiver,
clawbackTarget: data.clawbackTarget,
amount: data.amount!,
Expand All @@ -201,7 +202,7 @@ export function AssetClawbackTransactionBuilder({ mode, transaction, onSubmit, o
if (mode === TransactionBuilderMode.Edit && transaction) {
return {
asset: transaction.asset,
sender: transaction.sender,
sender: transaction.sender?.autoPopulated ? undefined : transaction.sender,
receiver: transaction.receiver,
clawbackTarget: transaction.clawbackTarget,
amount: transaction.amount,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { bigIntSchema, numberSchema } from '@/features/forms/data/common'
import { commonSchema, optionalAddressFieldSchema, senderFieldSchema } from '../data/common'
import { commonSchema, optionalAddressFieldSchema } from '../data/common'
import { z } from 'zod'
import { useCallback, useMemo } from 'react'
import { zfd } from 'zod-form-data'
Expand All @@ -17,10 +17,11 @@ import { TransactionBuilderMode } from '../data'
import { TransactionBuilderNoteField } from './transaction-builder-note-field'
import { asAddressOrNfd, asOptionalAddressOrNfd } from '../mappers/as-address-or-nfd'
import { ActiveWalletAccount } from '@/features/wallet/types/active-wallet'
import resolveSenderAddress from '../utils/resolve-sender-address'

export const assetCreateFormSchema = z.object({
...commonSchema,
...senderFieldSchema,
sender: optionalAddressFieldSchema,
total: bigIntSchema(z.bigint({ required_error: 'Required', invalid_type_error: 'Required' }).gt(BigInt(0), 'Must be greater than 0')),
decimals: numberSchema(z.number({ required_error: 'Required', invalid_type_error: 'Required' }).min(0).max(19)),
assetName: zfd.text(z.string().optional()),
Expand Down Expand Up @@ -66,7 +67,7 @@ function FormFields({ helper }: FormFieldsProps) {
{helper.addressField({
field: 'sender',
label: 'Creator',
helpText: 'Account that creates the asset. Sends the transaction and pays the fee',
helpText: 'Account that creates the asset. Sends the transaction and pays the fee - optional for simulating',
placeholder: ZERO_ADDRESS,
})}
{helper.addressField({
Expand Down Expand Up @@ -133,7 +134,7 @@ export function AssetCreateTransactionBuilder({ mode, transaction, activeAccount
unitName: data.unitName,
total: data.total,
decimals: data.decimals,
sender: data.sender,
sender: await resolveSenderAddress(data.sender),
manager: asOptionalAddressOrNfd(data.manager),
reserve: asOptionalAddressOrNfd(data.reserve),
freeze: asOptionalAddressOrNfd(data.freeze),
Expand All @@ -155,7 +156,7 @@ export function AssetCreateTransactionBuilder({ mode, transaction, activeAccount
unitName: transaction.unitName,
total: transaction.total,
decimals: transaction.decimals,
sender: transaction.sender,
sender: transaction.sender?.autoPopulated ? undefined : transaction.sender,
manager: transaction.manager,
reserve: transaction.reserve,
freeze: transaction.freeze,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { bigIntSchema } from '@/features/forms/data/common'
import { commonSchema, senderFieldSchema } from '../data/common'
import { commonSchema, optionalAddressFieldSchema } from '../data/common'
import { z } from 'zod'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { zfd } from 'zod-form-data'
Expand All @@ -25,10 +25,11 @@ import { cn } from '@/features/common/utils'
import { TransactionBuilderMode } from '../data'
import { TransactionBuilderNoteField } from './transaction-builder-note-field'
import { asAddressOrNfd } from '../mappers/as-address-or-nfd'
import resolveSenderAddress from '../utils/resolve-sender-address'

export const assetDestroyFormSchema = z.object({
...commonSchema,
...senderFieldSchema,
sender: optionalAddressFieldSchema,
asset: z
.object({
id: bigIntSchema(z.bigint({ required_error: 'Required', invalid_type_error: 'Required' }).min(1n)),
Expand Down Expand Up @@ -81,7 +82,7 @@ function FormFields({ helper, asset }: FormFieldsProps) {
{helper.addressField({
field: 'sender',
label: 'Sender',
helpText: 'The current asset manager address. Sends the transaction and pays the fee',
helpText: 'The current asset manager address. Sends the transaction and pays the fee - optional for simulating',
placeholder: ZERO_ADDRESS,
})}
<TransactionBuilderFeeField />
Expand Down Expand Up @@ -165,7 +166,7 @@ export function AssetDestroyTransactionBuilder({ mode, transaction, onSubmit, on
id: transaction?.id ?? randomGuid(),
type: BuildableTransactionType.AssetDestroy,
asset: data.asset,
sender: data.sender,
sender: await resolveSenderAddress(data.sender),
fee: data.fee,
validRounds: data.validRounds,
note: data.note,
Expand All @@ -177,7 +178,7 @@ export function AssetDestroyTransactionBuilder({ mode, transaction, onSubmit, on
if (mode === TransactionBuilderMode.Edit && transaction) {
return {
asset: transaction.asset,
sender: transaction.sender,
sender: transaction.sender?.autoPopulated ? undefined : transaction.sender,
fee: transaction.fee,
validRounds: transaction.validRounds,
note: transaction.note,
Expand Down
Loading