diff --git a/src/features/transaction-wizard/data/common.ts b/src/features/transaction-wizard/data/common.ts
index dab3f6e63..afd5f7390 100644
--- a/src/features/transaction-wizard/data/common.ts
+++ b/src/features/transaction-wizard/data/common.ts
@@ -36,6 +36,7 @@ export const optionalAddressFieldSchema = z
message: invalidAddressOrNfdMessage,
}),
resolvedAddress: z.string().optional(),
+ autoPopulated: z.boolean().optional(),
})
.superRefine((field, ctx) => {
if (field.value && (!field.resolvedAddress || !isAddress(field.resolvedAddress))) {
diff --git a/src/features/transaction-wizard/mappers/as-address-or-nfd.ts b/src/features/transaction-wizard/mappers/as-address-or-nfd.ts
index 148e978b0..01f11b213 100644
--- a/src/features/transaction-wizard/mappers/as-address-or-nfd.ts
+++ b/src/features/transaction-wizard/mappers/as-address-or-nfd.ts
@@ -26,5 +26,6 @@ export const asOptionalAddressOrNfdSchema = (address?: Address) => {
return {
value: address,
resolvedAddress: address,
+ autoPopulated: false,
}
}
diff --git a/src/features/transaction-wizard/mappers/as-algosdk-transactions.ts b/src/features/transaction-wizard/mappers/as-algosdk-transactions.ts
index 791d0dd89..43f711a63 100644
--- a/src/features/transaction-wizard/mappers/as-algosdk-transactions.ts
+++ b/src/features/transaction-wizard/mappers/as-algosdk-transactions.ts
@@ -87,7 +87,7 @@ export const asPaymentTransactionParams = (
): PaymentParams => {
return {
sender: transaction.sender.resolvedAddress,
- receiver: transaction.receiver ? transaction.receiver.resolvedAddress : transaction.sender.resolvedAddress,
+ receiver: transaction.receiver ? transaction.receiver.resolvedAddress : transaction.sender?.resolvedAddress,
closeRemainderTo: 'closeRemainderTo' in transaction ? transaction.closeRemainderTo.resolvedAddress : undefined,
amount: algos(transaction.amount ?? 0),
note: transaction.note,
diff --git a/src/features/transaction-wizard/mappers/as-description-list-items.tsx b/src/features/transaction-wizard/mappers/as-description-list-items.tsx
index 2cfcb5ed2..427d58556 100644
--- a/src/features/transaction-wizard/mappers/as-description-list-items.tsx
+++ b/src/features/transaction-wizard/mappers/as-description-list-items.tsx
@@ -49,6 +49,7 @@ import { asAssetDisplayAmount } from '@/features/common/components/display-asset
import { AddressOrNfdLink } from '@/features/accounts/components/address-or-nfd-link'
import { DecodedAbiStruct } from '@/features/abi-methods/components/decoded-abi-struct'
import { ArgumentDefinition } from '@/features/applications/models'
+import TransactionSenderLink from '@/features/accounts/components/transaction-sender-link'
export const asDescriptionListItems = (
transaction: BuildTransactionResult,
@@ -101,7 +102,7 @@ const asPaymentTransaction = (txn: BuildPaymentTransactionResult | BuildAccountC
return [
{
dt: 'Sender',
- dd:
,
+ dd:
,
},
...('closeRemainderTo' in params && params.closeRemainderTo
? [
@@ -141,7 +142,7 @@ const asAssetTransferTransaction = (
},
{
dt: 'Sender',
- dd:
,
+ dd:
,
},
{
dt: 'Receiver',
@@ -193,7 +194,7 @@ const asAssetConfigTransaction = (
...('decimals' in params && params.decimals !== undefined ? [{ dt: 'Decimals', dd: params.decimals }] : []),
{
dt: transaction.type === BuildableTransactionType.AssetCreate ? 'Creator' : 'Sender',
- dd:
,
+ dd:
,
},
...('manager' in params && params.manager
? [
@@ -248,7 +249,7 @@ const asAssetFreezeTransaction = (transaction: BuildAssetFreezeTransactionResult
},
{
dt: 'Sender',
- dd:
,
+ dd:
,
},
...('account' in params && params.account
? [
@@ -274,7 +275,7 @@ const asKeyRegistrationTransaction = (transaction: BuildKeyRegistrationTransacti
return [
{
dt: 'Sender',
- dd:
,
+ dd:
,
},
{
dt: 'Registration',
@@ -386,7 +387,7 @@ const asAppCallTransaction = (transaction: BuildAppCallTransactionResult): Descr
},
{
dt: 'Sender',
- dd:
,
+ dd:
,
},
...(transaction.extraProgramPages !== undefined
? [
@@ -439,7 +440,7 @@ const asMethodCallTransaction = (
},
{
dt: 'Sender',
- dd:
,
+ dd:
,
},
...(transaction.extraProgramPages !== undefined
? [
@@ -699,7 +700,7 @@ const asApplicationCreateTransaction = (transaction: BuildApplicationCreateTrans
},
{
dt: 'Sender',
- dd:
,
+ dd:
,
},
{
dt: 'Approval program',
@@ -762,7 +763,7 @@ const asApplicationUpdateTransaction = (transaction: BuildApplicationUpdateTrans
},
{
dt: 'Sender',
- dd:
,
+ dd:
,
},
{
dt: 'Approval program',
diff --git a/src/features/transaction-wizard/models/index.ts b/src/features/transaction-wizard/models/index.ts
index 969ee2caa..b4eb5adfd 100644
--- a/src/features/transaction-wizard/models/index.ts
+++ b/src/features/transaction-wizard/models/index.ts
@@ -65,9 +65,13 @@ export type AddressOrNfd = {
resolvedAddress: Address
}
+export type TransactionSender = AddressOrNfd & {
+ autoPopulated?: boolean
+}
+
type CommonBuildTransactionResult = {
id: string
- sender: AddressOrNfd
+ sender: TransactionSender
fee: {
setAutomatically: boolean
value?: number
diff --git a/src/features/transaction-wizard/transaction-wizard-page.test.tsx b/src/features/transaction-wizard/transaction-wizard-page.test.tsx
index 841056ac3..b670a2be4 100644
--- a/src/features/transaction-wizard/transaction-wizard-page.test.tsx
+++ b/src/features/transaction-wizard/transaction-wizard-page.test.tsx
@@ -4,11 +4,11 @@ import { executeComponentTest } from '@/tests/test-component'
import { fireEvent, render, waitFor, within } from '@/tests/testing-library'
import { useWallet } from '@txnlab/use-wallet-react'
import { algo } from '@algorandfoundation/algokit-utils'
-import { sendButtonLabel, transactionTypeLabel, TransactionWizardPage } from './transaction-wizard-page'
+import { sendButtonLabel, simulateButtonLabel, transactionTypeLabel, TransactionWizardPage } from './transaction-wizard-page'
import { selectOption } from '@/tests/utils/select-option'
import { setWalletAddressAndSigner } from '@/tests/utils/set-wallet-address-and-signer'
import { addTransactionLabel } from './components/transactions-builder'
-import { groupSendResultsLabel } from './components/group-send-results'
+import { groupSendResultsLabel, groupSimulateResultsLabel } from './components/group-send-results'
import { base64ToBytes } from '@/utils/base64-to-bytes'
describe('transaction-wizard-page', () => {
@@ -43,6 +43,59 @@ describe('transaction-wizard-page', () => {
}
)
})
+
+ it('Can add a payment transaction without defining a sender and simulate transaction', async () => {
+ const kmdAccount2 = await localnet.algorand.account.kmd.getOrCreateWalletAccount('test-wallet-2')
+
+ await executeComponentTest(
+ () => {
+ return render(
)
+ },
+ async (component, user) => {
+ const addTransactionButton = await waitFor(() => {
+ const addTransactionButton = component.getByRole('button', { name: addTransactionLabel })
+ expect(addTransactionButton).not.toBeDisabled()
+ return addTransactionButton!
+ })
+ await user.click(addTransactionButton)
+
+ const receiverInput = await component.findByLabelText(/Receiver/)
+ fireEvent.input(receiverInput, {
+ target: { value: kmdAccount2.addr },
+ })
+
+ const amountInput = await component.findByLabelText(/Amount/)
+ fireEvent.input(amountInput, {
+ target: { value: '0.5' },
+ })
+
+ const addButton = await waitFor(() => {
+ const addButton = component.getByRole('button', { name: 'Add' })
+ expect(addButton).not.toBeDisabled()
+ return addButton!
+ })
+ await user.click(addButton)
+
+ const simulateButton = await waitFor(() => {
+ const simulateButton = component.getByRole('button', { name: simulateButtonLabel })
+ expect(simulateButton).not.toBeDisabled()
+ return simulateButton!
+ })
+ await user.click(simulateButton)
+
+ const resultsDiv = await waitFor(
+ () => {
+ expect(component.queryByText('Required')).not.toBeInTheDocument()
+ return component.getByText(groupSimulateResultsLabel).parentElement!
+ },
+ { timeout: 10_000 }
+ )
+
+ // Basic assertion that something was rendered as a result
+ expect(resultsDiv).toBeInTheDocument()
+ }
+ )
+ })
})
describe('when a wallet is connected', () => {
diff --git a/src/features/transaction-wizard/transaction-wizard-page.tsx b/src/features/transaction-wizard/transaction-wizard-page.tsx
index 80cac7f0d..f790a82c1 100644
--- a/src/features/transaction-wizard/transaction-wizard-page.tsx
+++ b/src/features/transaction-wizard/transaction-wizard-page.tsx
@@ -10,15 +10,18 @@ import { AppCallTransaction, TransactionType } from '../transactions/models'
import { GroupSendResults, SendResults } from './components/group-send-results'
import algosdk from 'algosdk'
import { useTitle } from '@/utils/use-title'
-import { useTransactionSearchParamsBuilder } from './utils/use-transaction-search-params-builder'
+import { PageLoader } from '../common/components/page-loader'
+import { useLoadableSearchParamsTransactions } from '../transactions/data/use-loadable-search-params-transaction'
+import { RenderLoadable } from '../common/components/render-loadable'
export const transactionWizardPageTitle = 'Transaction Wizard'
export const transactionTypeLabel = 'Transaction type'
export const sendButtonLabel = 'Send'
+export const simulateButtonLabel = 'Simulate'
export function TransactionWizardPage() {
const [sendResults, setSendResults] = useState
(undefined)
- const searchParamsTransactions = useTransactionSearchParamsBuilder()
+ const loadableSearchParamsTransactions = useLoadableSearchParamsTransactions()
useTitle('Transaction Wizard')
const renderTransactionResults = useCallback((result: SendTransactionResults, simulateResponse?: algosdk.modelsv2.SimulateResponse) => {
@@ -57,13 +60,18 @@ export function TransactionWizardPage() {
Create and send transactions to the selected network using a connected wallet.
-
{transactionGroupLabel}}
- onSendTransactions={sendTransactions}
- onSimulated={renderSimulateResult}
- onReset={reset}
- />
+
+ }>
+ {(searchParamsTransactions) => (
+ {transactionGroupLabel}}
+ onSendTransactions={sendTransactions}
+ onSimulated={renderSimulateResult}
+ onReset={reset}
+ />
+ )}
+
{sendResults && }
diff --git a/src/features/transaction-wizard/utils/resolve-sender-address.ts b/src/features/transaction-wizard/utils/resolve-sender-address.ts
new file mode 100644
index 000000000..ae18496ce
--- /dev/null
+++ b/src/features/transaction-wizard/utils/resolve-sender-address.ts
@@ -0,0 +1,47 @@
+import {
+ TESTNET_FEE_SINK_ADDRESS,
+ MAINNET_FEE_SINK_ADDRESS,
+ networkConfigAtom,
+ BETANET_FEE_SINK_ADDRESS,
+ FNET_FEE_SINK_ADDRESS,
+} from '@/features/network/data'
+import { TransactionSender } from '../models'
+import { settingsStore } from '@/features/settings/data'
+import { betanetId, mainnetId, testnetId, fnetId, localnetId } from '@/features/network/data'
+import { algorandClient } from '@/features/common/data/algo-client'
+
+export async function resolveTransactionSender(data: { value?: string; resolvedAddress?: string }): Promise