Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c92354e
wip - making sender optional
PatrickDinh Jul 28, 2025
80ec848
Merge branch 'main' of https://github.com/algorandfoundation/algokit-…
p2arthur Aug 30, 2025
2c8d8fb
feat(optional_sender): implement basic working version of optional se…
p2arthur Aug 31, 2025
9130179
add(optional_sender) implement the use of dispenser address for empty…
p2arthur Aug 31, 2025
5b5ad14
refactor(optional_sender) refactor optional sender to working version…
p2arthur Sep 1, 2025
13011ab
feat(optional_sender): implement optional sender to 8 different trans…
p2arthur Sep 3, 2025
d4c3f71
fix(lint): fix lint errors and warning
p2arthur Sep 4, 2025
681f3bd
feat(optional_sender): implement optional sender in 4 more transactio…
p2arthur Sep 5, 2025
2eb565d
feat(optional): add optional sender support to asset reconfigure tran…
p2arthur Sep 5, 2025
35f8ec5
feat(optional_sender) finish implement optional sender to all transac…
p2arthur Sep 11, 2025
78fb4a5
test(optionalSender) add test to confirm that optional sender works
p2arthur Sep 12, 2025
6f0be92
test(optionalSender) remove expect cases where sender was required
p2arthur Sep 12, 2025
7a29bab
fix(optionalSender): fix tooltip not showing when hovering optional s…
p2arthur Sep 12, 2025
60930a8
fix(testnet_address): add testnet fee sink address to be used as defa…
p2arthur Sep 15, 2025
39ed51a
refactor(sender_address): refactor defineSender to be network aware
p2arthur Sep 15, 2025
6b170e0
fix(lint) fix linting errors
p2arthur Sep 15, 2025
3a0c3b7
refactor(reviews) updat code to follow PR reviews for better implemen…
p2arthur Sep 17, 2025
09acee5
fix(build): fix build errors
p2arthur Sep 18, 2025
c715c55
fix(type): implement transaction sender type
p2arthur Sep 22, 2025
829589b
fix(update_sender) update auto populated on updating transaction
p2arthur Sep 25, 2025
1c9d7e5
fix(update_op_sender) fix optional sender for editing transactions
p2arthur Sep 26, 2025
0c29eaf
refactor(helpers) update helper text to mention optional sender for s…
p2arthur Sep 26, 2025
bbee3ec
fix(build) fix build errors
p2arthur Sep 26, 2025
920f098
fix(conflict) fix conflict with latest from main
p2arthur Sep 26, 2025
80de8e7
fix(lint) fix lint errors
p2arthur Sep 26, 2025
0d069a1
fix(update-transaction): fix auto populate editing transactions in th…
p2arthur Sep 28, 2025
1d255a3
fix(optional-sender): fix optional address or nfd mapper to work with…
p2arthur Sep 29, 2025
d69ea30
refactor(resolve_sender): refactor resolve sender address to follow b…
p2arthur Oct 1, 2025
877dcd3
test(optional_sender): add more tests to check optional sender in the…
p2arthur Oct 2, 2025
f8f5b31
refactor(asTransactionSender): refactor asTransactionSenderHelper and…
p2arthur Oct 3, 2025
206971b
Update src/features/transaction-wizard/transaction-wizard-page.test.tsx
p2arthur Oct 3, 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
9 changes: 8 additions & 1 deletion src/features/accounts/components/address-or-nfd-link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ export type AddressOrNfdLinkProps = PropsWithChildren<{
showCopyButton?: boolean
showQRButton?: boolean
nfd?: Nfd
autoPopulated?: boolean
}>

export const AddressOrNfdLink = fixedForwardRef(
(
{ address: _address, nfd, short, className, children, showCopyButton, showQRButton, ...rest }: AddressOrNfdLinkProps,
{ address: _address, nfd, short, className, children, showCopyButton, showQRButton, autoPopulated, ...rest }: AddressOrNfdLinkProps,
ref?: React.LegacyRef<HTMLAnchorElement>
) => {
const [selectedNetwork] = useSelectedNetwork()
Expand Down Expand Up @@ -54,6 +55,12 @@ export const AddressOrNfdLink = fixedForwardRef(
) : (
<div className="flex items-center overflow-hidden">
{link}
{autoPopulated && (
<span className="group ml-1 cursor-help text-yellow-500">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: there is double spaces here

<span>?</span>
<div className="absolute z-10 hidden rounded-sm border-2 border-gray-300/20 p-1 group-hover:block">auto populated</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL group-hover

</span>
)}
{showCopyButton && <CopyButton value={address} />}
{showQRButton && <OpenAddressQRDialogButton address={address} />}
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/features/common/components/description-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { cn } from '../utils'
export type DescriptionListItems = {
dt: string
dd: string | number | bigint | boolean | React.JSX.Element[] | React.JSX.Element | undefined
highlightedDD?: boolean
}[]

type Props = {
Expand All @@ -16,7 +17,7 @@ export function DescriptionList({ items, dtClassName }: Props) {
{items.map((item, index) => (
<dl key={index} className={cn('grid grid-cols-subgrid col-span-2')}>
<dt className={cn('font-medium', dtClassName)}>{item.dt}</dt>
<dd className={cn('overflow-ellipsis whitespace-normal overflow-hidden')}>
<dd className={cn('overflow-ellipsis whitespace-normal overflow-hidden', item.highlightedDD && 'font-medium text-green-500')}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be implemented as a ddClassNames props, i.e.

<dd className={cn('overflow-ellipsis whitespace-normal overflow-hidden', item.ddClassNames)}

the consumer if this component can decide how they want to style it. This way it's more flexible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, will try that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest we don't need that change. I think it slipped while I was trying to figure out the inner workings of the transaction wizard in order to add highlighting if auto populated.

Reverting this change

{typeof item.dd === 'bigint' ? item.dd.toString() : item.dd}
</dd>
</dl>
Expand Down
3 changes: 2 additions & 1 deletion src/features/forms/components/address-form-item.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FieldPath, useFormContext } from 'react-hook-form'
import { FormItemProps } from '@/features/forms/components/form-item'
import { TextFormItem } from './text-form-item'
import { addressFieldSchema, optionalAddressFieldSchema } from '@/features/transaction-wizard/data/common'
import { addressFieldSchema, optionalAddressFieldSchema, optionalSenderFieldShape } from '@/features/transaction-wizard/data/common'
import { useDebounce } from 'use-debounce'
import { isNfd, useLoadableForwardLookupNfdResult } from '@/features/nfd/data'
import { useCallback, useEffect } from 'react'
Expand All @@ -11,6 +11,7 @@ import { z } from 'zod'

export type AddressOrNfdFieldSchema = z.infer<typeof addressFieldSchema>
export type OptionalAddressOrNfdFieldSchema = z.infer<typeof optionalAddressFieldSchema>
export type OptionalSenderFieldSchema = z.infer<typeof optionalSenderFieldShape.sender>

export interface AddressFieldProps<TSchema extends Record<string, unknown> = Record<string, unknown>>
extends Omit<FormItemProps<TSchema>, 'children'> {
Expand Down
3 changes: 2 additions & 1 deletion src/features/network/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ export const allWalletProviderNames: Record<WalletId, string> = {
pera: 'Pera',
exodus: 'Exodus',
lute: 'Lute',
// The below providers aren't used
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should still keep this comment

custom: 'Custom',
kibisis: 'Kibisis',
walletconnect: 'Wallet Connect',
magic: 'Magic',
biatec: 'Biatec',
}

export const FEE_SINK_ADDRESS = 'Y76M3MSY6DKBRHBL7C3NNDXGS5IIMQVQVUAB6MP4XEMMGVF2QWNPL226CA'

export const defaultNetworkConfigs: Record<NetworkId, NetworkConfig> = {
[localnetId]: {
name: 'LocalNet',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { numberSchema } from '@/features/forms/data/common'
import { addressFieldSchema, commonSchema, optionalAddressFieldSchema, senderFieldSchema } from '../data/common'
import {
addressFieldSchema,
commonSchema,
optionalAddressFieldSchema,
optionalSenderFieldShape,
} from '@/features/transaction-wizard/data/common'
import { z } from 'zod'
import { useCallback, useMemo } from 'react'
import { zfd } from 'zod-form-data'
Expand All @@ -18,14 +23,17 @@ 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 defineSenderAddress from '../utils/defineSenderAddress'
import { useNetworkConfig } from '@/features/network/data'

const senderLabel = 'Sender'
const receiverLabel = 'Receiver'
const closeRemainderToLabel = 'Close remainder to'

const formSchema = z
.object({
...commonSchema,
...senderFieldSchema,
...optionalSenderFieldShape,
closeRemainderTo: addressFieldSchema,
receiver: optionalAddressFieldSchema,
amount: numberSchema(z.number({ required_error: 'Required', invalid_type_error: 'Required' }).min(0).optional()),
Expand Down Expand Up @@ -58,12 +66,14 @@ type Props = {
}

export function AccountCloseTransactionBuilder({ mode, transaction, activeAccount, onSubmit, onCancel }: Props) {
const { id: networkId } = useNetworkConfig()

const submit = useCallback(
async (data: z.infer<typeof formData>) => {
onSubmit({
id: transaction?.id ?? randomGuid(),
type: BuildableTransactionType.AccountClose,
sender: data.sender,
sender: await defineSenderAddress(data.sender!, networkId),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need ! in data.sender!

closeRemainderTo: data.closeRemainderTo,
receiver: asOptionalAddressOrNfd(data.receiver),
amount: data.amount,
Expand All @@ -72,7 +82,7 @@ export function AccountCloseTransactionBuilder({ mode, transaction, activeAccoun
note: data.note,
})
},
[onSubmit, transaction?.id]
[onSubmit, transaction?.id, networkId]
)
const defaultValues = useMemo<Partial<z.infer<typeof formData>>>(() => {
if (mode === TransactionBuilderMode.Edit && transaction) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
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, optionalSenderFieldShape } 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 +16,12 @@ 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 defineSenderAddress from '../utils/defineSenderAddress'
import { useNetworkConfig } from '@/features/network/data'

const formData = zfd.formData({
...commonSchema,
...senderFieldSchema,
...optionalSenderFieldShape,
...onCompleteFieldSchema,
applicationId: bigIntSchema(z.bigint({ required_error: 'Required', invalid_type_error: 'Required' })),
extraProgramPages: numberSchema(z.number().min(0).max(3).optional()),
Expand All @@ -41,13 +43,14 @@ type Props = {
}

export function AppCallTransactionBuilder({ mode, transaction, activeAccount, defaultValues: _defaultValues, onSubmit, onCancel }: Props) {
const { id: networkId } = useNetworkConfig()
const submit = useCallback(
async (values: z.infer<typeof formData>) => {
onSubmit({
id: transaction?.id ?? randomGuid(),
type: BuildableTransactionType.AppCall,
applicationId: BigInt(values.applicationId),
sender: values.sender,
sender: await defineSenderAddress(values.sender!, networkId),
onComplete: Number(values.onComplete),
extraProgramPages: values.extraProgramPages,
fee: values.fee,
Expand All @@ -56,7 +59,7 @@ export function AppCallTransactionBuilder({ mode, transaction, activeAccount, de
note: values.note,
})
},
[onSubmit, transaction?.id]
[onSubmit, transaction?.id, networkId]
)

const defaultValues = useMemo<Partial<z.infer<typeof formData>>>(() => {
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,
optionalSenderFieldShape,
} from '@/features/transaction-wizard/data/common'
import { z } from 'zod'
import { zfd } from 'zod-form-data'
Expand All @@ -22,9 +22,12 @@ import { TransactionBuilderNoteField } from './transaction-builder-note-field'
import { asAddressOrNfd } from '../mappers/as-address-or-nfd'
import { ActiveWalletAccount } from '@/features/wallet/types/active-wallet'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove empty line here

import defineSenderAddress from '../utils/defineSenderAddress'
import { useNetworkConfig } from '@/features/network/data'

const formData = zfd.formData({
...commonSchema,
...senderFieldSchema,
...optionalSenderFieldShape,
...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 All @@ -50,14 +53,16 @@ type Props = {
}

export function ApplicationCreateTransactionBuilder({ mode, transaction, activeAccount, onSubmit, onCancel }: Props) {
const { id: networkId } = useNetworkConfig()

const submit = useCallback(
async (values: z.infer<typeof formData>) => {
onSubmit({
id: transaction?.id ?? randomGuid(),
type: BuildableTransactionType.ApplicationCreate,
approvalProgram: values.approvalProgram,
clearStateProgram: values.clearStateProgram,
sender: values.sender,
sender: await defineSenderAddress(values.sender!, networkId),
onComplete: Number(values.onComplete),
extraProgramPages: values.extraProgramPages,
globalInts: values.globalInts,
Expand All @@ -70,7 +75,7 @@ export function ApplicationCreateTransactionBuilder({ mode, transaction, activeA
note: values.note,
})
},
[onSubmit, transaction?.id]
[onSubmit, transaction?.id, networkId]
)

const defaultValues = useMemo<Partial<z.infer<typeof formData>>>(() => {
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, optionalSenderFieldShape } 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,12 @@ 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 defineSenderAddress from '../utils/defineSenderAddress'
import { useNetworkConfig } from '@/features/network/data'

const formData = zfd.formData({
...commonSchema,
...senderFieldSchema,
...optionalSenderFieldShape,
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 All @@ -39,6 +41,8 @@ type Props = {
}

export function ApplicationUpdateTransactionBuilder({ mode, transaction, activeAccount, onSubmit, onCancel }: Props) {
const { id: networkId } = useNetworkConfig()

const submit = useCallback(
async (values: z.infer<typeof formData>) => {
onSubmit({
Expand All @@ -47,14 +51,14 @@ export function ApplicationUpdateTransactionBuilder({ mode, transaction, activeA
applicationId: BigInt(values.applicationId),
approvalProgram: values.approvalProgram,
clearStateProgram: values.clearStateProgram,
sender: values.sender,
sender: await defineSenderAddress(values.sender, networkId),
fee: values.fee,
validRounds: values.validRounds,
args: values.args.map((arg) => arg.value),
note: values.note,
})
},
[onSubmit, transaction?.id]
[onSubmit, transaction?.id, networkId]
)

const defaultValues = useMemo<Partial<z.infer<typeof formData>>>(() => {
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, optionalSenderFieldShape, 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,15 @@ 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 defineSenderAddress from '../utils/defineSenderAddress'
import { useNetworkConfig } from '@/features/network/data'

const clawbackTargetLabel = 'Clawback target'

export const assetClawbackFormSchema = z
.object({
...commonSchema,
...senderFieldSchema,
...optionalSenderFieldShape,
...receiverFieldSchema,
clawbackTarget: addressFieldSchema,
asset: z
Expand Down Expand Up @@ -180,13 +182,15 @@ type Props = {
}

export function AssetClawbackTransactionBuilder({ mode, transaction, onSubmit, onCancel }: Props) {
const { id: networkId } = useNetworkConfig()

const submit = useCallback(
async (data: z.infer<typeof formData>) => {
onSubmit({
id: transaction?.id ?? randomGuid(),
type: BuildableTransactionType.AssetClawback,
asset: data.asset,
sender: data.sender,
sender: await defineSenderAddress(data.sender, networkId),
receiver: data.receiver,
clawbackTarget: data.clawbackTarget,
amount: data.amount!,
Expand All @@ -195,7 +199,7 @@ export function AssetClawbackTransactionBuilder({ mode, transaction, onSubmit, o
note: data.note,
})
},
[onSubmit, transaction?.id]
[onSubmit, transaction?.id, networkId]
)
const defaultValues = useMemo<Partial<z.infer<typeof formData>>>(() => {
if (mode === TransactionBuilderMode.Edit && transaction) {
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, optionalSenderFieldShape } from '../data/common'
import { z } from 'zod'
import { useCallback, useMemo } from 'react'
import { zfd } from 'zod-form-data'
Expand All @@ -17,10 +17,12 @@ 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 defineSenderAddress from '../utils/defineSenderAddress'
import { useNetworkConfig } from '@/features/network/data'

export const assetCreateFormSchema = z.object({
...commonSchema,
...senderFieldSchema,
...optionalSenderFieldShape,
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 @@ -124,6 +126,8 @@ type Props = {
}

export function AssetCreateTransactionBuilder({ mode, transaction, activeAccount, onSubmit, onCancel }: Props) {
const { id: networkId } = useNetworkConfig()

const submit = useCallback(
async (data: z.infer<typeof formData>) => {
onSubmit({
Expand All @@ -133,7 +137,7 @@ export function AssetCreateTransactionBuilder({ mode, transaction, activeAccount
unitName: data.unitName,
total: data.total,
decimals: data.decimals,
sender: data.sender,
sender: await defineSenderAddress(data.sender, networkId),
manager: asOptionalAddressOrNfd(data.manager),
reserve: asOptionalAddressOrNfd(data.reserve),
freeze: asOptionalAddressOrNfd(data.freeze),
Expand All @@ -146,7 +150,7 @@ export function AssetCreateTransactionBuilder({ mode, transaction, activeAccount
note: data.note,
})
},
[onSubmit, transaction?.id]
[onSubmit, transaction?.id, networkId]
)
const defaultValues = useMemo<Partial<z.infer<typeof formData>>>(() => {
if (mode === TransactionBuilderMode.Edit && transaction) {
Expand Down
Loading