Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b7bf599
feat: add prepare send rpc
stanleyyconsensys May 11, 2026
e8764c8
fix: test and comment
stanleyyconsensys May 11, 2026
a2563b5
Merge branch 'main' into feat/amout-address-input-validate
stanleyyconsensys May 12, 2026
0e3cbb8
Merge branch 'main' into feat/amout-address-input-validate
stanleyyconsensys May 13, 2026
ca411e7
Merge branch 'main' into feat/amout-address-input-validate
stanleyyconsensys May 14, 2026
d6d297c
chore: update on amount input
stanleyyconsensys May 15, 2026
4883e4e
Merge branch 'chore/network-service-with-cache' into feat/amout-addre…
stanleyyconsensys May 15, 2026
55f9de6
Merge branch 'fix/simulation-self-transfer' into feat/amout-address-i…
stanleyyconsensys May 15, 2026
2acac99
chore: add account not activate handle
stanleyyconsensys May 15, 2026
f8daed5
Merge branch 'fix/asset-sync-visibiltity' into feat/amout-address-inp…
stanleyyconsensys May 18, 2026
fdccd21
chore: fix test
stanleyyconsensys May 18, 2026
8bed842
feat: add confirm send rpc
stanleyyconsensys May 20, 2026
d29e64b
Merge branch 'main' into feat/amout-address-input-validate
stanleyyconsensys May 20, 2026
af06ad9
fix: code comment
stanleyyconsensys May 20, 2026
9996e1f
Merge branch 'main' into feat/confirm-send
stanleyyconsensys May 21, 2026
4894294
Merge branch 'feat/amout-address-input-validate' into feat/confirm-send
stanleyyconsensys May 21, 2026
951e036
Merge branch 'main' into feat/confirm-send
stanleyyconsensys May 21, 2026
2157c7f
fix: lint and test
stanleyyconsensys May 22, 2026
5ea10af
chore: fix review comment
stanleyyconsensys May 22, 2026
b8aaf79
fix: align with onAmountInput
stanleyyconsensys May 22, 2026
e6e83b0
fix: comment
stanleyyconsensys May 22, 2026
8afeefd
Merge branch 'main' into feat/confirm-send
stanleyyconsensys May 22, 2026
b86ddd4
chore: rebuild txn
stanleyyconsensys May 28, 2026
c903291
Revert "chore: rebuild txn"
stanleyyconsensys May 28, 2026
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
185 changes: 185 additions & 0 deletions packages/site/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ const Index = () => {
const [trustlineOutput, setTrustlineOutput] = useState<string | null>(null);
const [trustlineLimit, setTrustlineLimit] = useState('');

const [sendAssetId, setSendAssetId] = useState('stellar:testnet/slip44:148');
const [sendToAddress, setSendToAddress] = useState('');
const [sendAmount, setSendAmount] = useState('1');
const [sendOutput, setSendOutput] = useState<string | null>(null);

const isMetaMaskReady = isLocalSnap(defaultSnapOrigin)
? isFlask
: snapsDetected;
Expand Down Expand Up @@ -549,6 +554,128 @@ const Index = () => {
}
};

const invokeClientRequest = async (
method: 'confirmSend' | 'onAmountInput' | 'onAddressInput',
params: Record<string, unknown>,
): Promise<unknown> =>
invokeSnap({
method: `stellar_${method}`,
params: {
jsonrpc: '2.0',
id: crypto.randomUUID(),
method,
params,
},
});

const handleValidateSendAddress = async () => {
setSendOutput(null);
const trimmedTo = sendToAddress.trim();
if (!trimmedTo) {
setSendOutput('Enter a destination Stellar address.');
return;
}

try {
const result = await invokeClientRequest('onAddressInput', {
value: trimmedTo,
});
setSendOutput(JSON.stringify(result, null, 2));
} catch (addressError: unknown) {
const message =
addressError instanceof Error
? addressError.message
: String(addressError);
setSendOutput(message);
}
};

const handleValidateSendAmount = async () => {
setSendOutput(null);
const trimmedAsset = sendAssetId.trim();
const trimmedAmount = sendAmount.trim();
const trimmedTo = sendToAddress.trim();

if (!trimmedAsset) {
setSendOutput('Enter a CAIP-19 asset id.');
return;
}
if (!trimmedAmount) {
setSendOutput('Enter an amount.');
return;
}

const account = resolveSelectedAccount();
if (!account) {
setSendOutput(
'No keyring accounts found. Add a Stellar account in MetaMask first.',
);
return;
}

const params: Record<string, unknown> = {
accountId: account.id,
assetId: trimmedAsset,
value: trimmedAmount,
};
if (trimmedTo) {
params.to = trimmedTo;
}

try {
const result = await invokeClientRequest('onAmountInput', params);
setSendOutput(JSON.stringify(result, null, 2));
} catch (amountError: unknown) {
const message =
amountError instanceof Error
? amountError.message
: String(amountError);
setSendOutput(message);
}
};

const handleConfirmSend = async () => {
setSendOutput(null);
const trimmedAsset = sendAssetId.trim();
const trimmedAmount = sendAmount.trim();
const trimmedTo = sendToAddress.trim();

if (!trimmedAsset) {
setSendOutput('Enter a CAIP-19 asset id.');
return;
}
if (!trimmedAmount) {
setSendOutput('Enter an amount.');
return;
}
if (!trimmedTo) {
setSendOutput('Enter a destination Stellar address.');
return;
}

const account = resolveSelectedAccount();
if (!account) {
setSendOutput(
'No keyring accounts found. Add a Stellar account in MetaMask first.',
);
return;
}

try {
const result = await invokeClientRequest('confirmSend', {
fromAccountId: account.id,
toAddress: trimmedTo,
assetId: trimmedAsset,
amount: trimmedAmount,
});
setSendOutput(JSON.stringify(result, null, 2));
} catch (sendError: unknown) {
const message =
sendError instanceof Error ? sendError.message : String(sendError);
setSendOutput(message);
}
};

return (
<Container>
<Heading>
Expand Down Expand Up @@ -832,6 +959,64 @@ const Index = () => {
fullWidth
/>

<Card
content={{
title: 'Send transaction (Unified Non-EVM Send)',
description:
'Exercises stellar_confirmSend, stellar_onAmountInput, and stellar_onAddressInput. confirmSend builds the payment, shows the Snap confirmation UI, signs, and submits. Uses the active keyring account above.',
button: (
<>
<MessageField
aria-label="CAIP-19 asset id to send"
value={sendAssetId}
onChange={({ target }) => setSendAssetId(target.value)}
disabled={!installedSnap}
/>
<MessageField
aria-label="Destination Stellar address"
value={sendToAddress}
onChange={({ target }) => setSendToAddress(target.value)}
disabled={!installedSnap}
/>
<MessageField
aria-label="Amount to send"
value={sendAmount}
onChange={({ target }) => setSendAmount(target.value)}
disabled={!installedSnap}
/>
{sendOutput !== null && (
<SignatureOutput>{sendOutput}</SignatureOutput>
)}
<TrustlineButtonRow>
<SignOpsButton
type="button"
onClick={handleValidateSendAddress}
disabled={!installedSnap}
>
Validate address
</SignOpsButton>
<SignOpsButton
type="button"
onClick={handleValidateSendAmount}
disabled={!installedSnap}
>
Validate amount
</SignOpsButton>
<SignOpsButton
type="button"
onClick={handleConfirmSend}
disabled={!installedSnap}
>
Confirm send
</SignOpsButton>
</TrustlineButtonRow>
</>
),
}}
disabled={!installedSnap}
fullWidth
/>

<Notice>
<p>
Please note that the <b>snap.manifest.json</b> and{' '}
Expand Down
10 changes: 10 additions & 0 deletions packages/snap/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ClientRequestMethod,
} from './handlers/clientRequest';
import { ComputeFeeHandler } from './handlers/clientRequest/computeFee';
import { ConfirmSendHandler } from './handlers/clientRequest/confirmSend';
import { OnAddressInputHandler } from './handlers/clientRequest/onAddressInput';
import { OnAmountInputHandler } from './handlers/clientRequest/onAmountInput';
import { SignAndSendTransactionHandler } from './handlers/clientRequest/signAndSendTransaction';
Expand Down Expand Up @@ -229,6 +230,14 @@ const signAndSendTransactionHandler = new SignAndSendTransactionHandler({
transactionService,
});

const confirmSendHandler = new ConfirmSendHandler({
logger,
accountResolver,
transactionService,
assetMetadataService,
confirmationUIController,
});

const computeFeeHandler = new ComputeFeeHandler({
logger,
accountResolver,
Expand All @@ -242,6 +251,7 @@ const clientRequestMethodHandlers: Record<
[ClientRequestMethod.ChangeTrustOpt]: changeTrustOptHandler,
[ClientRequestMethod.OnAddressInput]: onAddressInputHandler,
[ClientRequestMethod.OnAmountInput]: onAmountInputHandler,
[ClientRequestMethod.ConfirmSend]: confirmSendHandler,
[ClientRequestMethod.SignAndSendTransaction]: signAndSendTransactionHandler,
[ClientRequestMethod.ComputeFee]: computeFeeHandler,
};
Expand Down
Loading
Loading