Add oracle rates

This commit is contained in:
2026-04-27 08:42:51 +00:00
parent dbfb2c68d2
commit 7ad17a7c0e
17 changed files with 884 additions and 25 deletions

View File

@@ -17,6 +17,7 @@ import { useNavigation } from '../../hooks/useNavigation.js';
import { useAppContext, useStatus } from '../../hooks/useAppContext.js';
import { useBlockableInput, useIsInputCaptured } from '../../hooks/useInputLayer.js';
import { useInvitations } from '../../hooks/useInvitations.js';
import { useSatoshisConversion } from '../../hooks/useSatoshisConversion.js';
import { colors, logoSmall, formatSatoshis } from '../../theme.js';
import { copyToClipboard } from '../../utils/clipboard.js';
import type { Invitation } from '../../../services/invitation.js';
@@ -88,6 +89,8 @@ export function InvitationScreen(): React.ReactElement {
const { setStatus } = useStatus();
const invitations = useInvitations();
const { currencyCode, formattedFiatPerBchRate, formatSatoshisToFiat } =
useSatoshisConversion('USD');
// ── UI state ─────────────────────────────────────────────────────────────
const [selectedIndex, setSelectedIndex] = useState(0);
@@ -494,6 +497,44 @@ export function InvitationScreen(): React.ReactElement {
const roleInfoRaw = userRole && selectedTemplate?.roles?.[userRole];
const roleInfo = roleInfoRaw && typeof roleInfoRaw === 'object' ? roleInfoRaw : null;
const getFiatSuffix = (satoshis: bigint): string => {
const fiatValue = formatSatoshisToFiat(satoshis);
return fiatValue ? ` (~${fiatValue})` : '';
};
const parseNumberishToBigInt = (value: unknown): bigint | null => {
if (typeof value === 'bigint') {
return value;
}
const asString = String(value).trim();
if (!/^[-]?\d+$/.test(asString)) {
return null;
}
try {
return BigInt(asString);
} catch {
return null;
}
};
const isSatoshisVariable = (variableIdentifier: string): boolean => {
const templateVariable = selectedTemplate?.variables?.[variableIdentifier];
const templateType = templateVariable?.type?.toLowerCase();
const templateHint = templateVariable?.hint?.toLowerCase();
const identifier = variableIdentifier.toLowerCase();
if (templateHint?.includes('satoshi')) {
return true;
}
return (
templateType === 'integer' &&
(identifier.includes('satoshi') || identifier.includes('amount'))
);
};
return (
<Box flexDirection="column">
{/* Type & Status */}
@@ -514,6 +555,11 @@ export function InvitationScreen(): React.ReactElement {
<Text color={colors.textMuted}>
Action: {action?.name ?? selectedInvitation.data.actionIdentifier}
</Text>
{formattedFiatPerBchRate && (
<Text color={colors.textMuted}>
1 BCH = {formattedFiatPerBchRate}
</Text>
)}
{action?.description && (
<Text color={colors.textMuted} dimColor>{action.description}</Text>
)}
@@ -542,6 +588,11 @@ export function InvitationScreen(): React.ReactElement {
inputs.map((input, idx) => {
const isUserInput = input.entityIdentifier === userEntityId;
const inputTemplate = selectedTemplate?.inputs?.[input.inputIdentifier ?? ''];
const inputSatoshis = (
'valueSatoshis' in input && input.valueSatoshis !== undefined
)
? parseNumberishToBigInt(input.valueSatoshis)
: null;
return (
<Text
key={`input-${idx}`}
@@ -550,6 +601,7 @@ export function InvitationScreen(): React.ReactElement {
{' '}{isUserInput ? '• ' : '○ '}
{inputTemplate?.name ?? input.inputIdentifier ?? `Input ${idx}`}
{input.roleIdentifier && ` (${input.roleIdentifier})`}
{inputSatoshis !== null && ` ${formatSatoshis(inputSatoshis)}${getFiatSuffix(inputSatoshis)}`}
</Text>
);
})
@@ -564,6 +616,9 @@ export function InvitationScreen(): React.ReactElement {
outputs.map((output, idx) => {
const isUserOutput = output.entityIdentifier === userEntityId;
const outputTemplate = selectedTemplate?.outputs?.[output.outputIdentifier ?? ''];
const outputSatoshis = output.valueSatoshis !== undefined
? parseNumberishToBigInt(output.valueSatoshis)
: null;
return (
<Text
key={`output-${idx}`}
@@ -571,7 +626,7 @@ export function InvitationScreen(): React.ReactElement {
>
{' '}{isUserOutput ? '• ' : '○ '}
{outputTemplate?.name ?? output.outputIdentifier ?? `Output ${idx}`}
{output.valueSatoshis !== undefined && ` (${formatSatoshis(output.valueSatoshis)})`}
{outputSatoshis !== null && ` (${formatSatoshis(outputSatoshis)}${getFiatSuffix(outputSatoshis)})`}
</Text>
);
})
@@ -591,6 +646,9 @@ export function InvitationScreen(): React.ReactElement {
const displayValue = typeof variable.value === 'bigint'
? variable.value.toString()
: String(variable.value);
const parsedVariableSatoshis = isSatoshisVariable(variable.variableIdentifier)
? parseNumberishToBigInt(variable.value)
: null;
return (
<Text
key={`var-${idx}`}
@@ -598,6 +656,8 @@ export function InvitationScreen(): React.ReactElement {
>
{' '}{isUserVariable ? '• ' : '○ '}
{varTemplate?.name ?? variable.variableIdentifier}: {displayValue}
{parsedVariableSatoshis !== null &&
` (${formatSatoshis(parsedVariableSatoshis)}${getFiatSuffix(parsedVariableSatoshis)})`}
{varTemplate?.description && (
<Text color={colors.textMuted} dimColor> - {varTemplate.description}</Text>
)}