Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
18780f3
feat: add vault activity timeline
Seranged Jul 13, 2026
d66c4c8
fix: make vault activity coverage honest
Seranged Jul 13, 2026
d640f92
fix: filter activity display events
Seranged Jul 17, 2026
9832063
fix: improve activity event presentation
Seranged Jul 17, 2026
d99dc0f
fix: simplify vault activity presentation
Seranged Jul 17, 2026
27c2735
fix: make activity rows container responsive
Seranged Jul 17, 2026
26b4581
fix: tighten compact activity rows
Seranged Jul 17, 2026
e48a8f2
fix: refine compact activity hierarchy
Seranged Jul 17, 2026
4ebddba
feat: add portfolio activity timeline
Seranged Jul 13, 2026
d4a1ee9
fix: identify markets in account activity
Seranged Jul 13, 2026
52dfcc7
fix: align activity filters with shared select
Seranged Jul 17, 2026
658550b
fix: improve activity event presentation
Seranged Jul 17, 2026
ef3b2f9
fix: clarify partial activity coverage
Seranged Jul 17, 2026
909e7cc
fix: simplify activity presentation
Seranged Jul 17, 2026
930442d
fix: use position terminology in activity
Seranged Jul 17, 2026
52b34db
fix: present activity events semantically
Seranged Jul 20, 2026
6b86ec8
Merge pull request #737 from euler-xyz/feature/lite-220-add-activity-…
Seranged Jul 20, 2026
67adb12
fix: ignore stale spy owner resolutions
Seranged Jul 20, 2026
8bbb152
fix: align activity UX
Seranged Jul 20, 2026
0a87f39
fix: refine activity controls
Seranged Jul 20, 2026
c7cf082
chore: bump Euler SDK to 1.2.0
Seranged Jul 20, 2026
0d2ba01
fix: use direct activity filters
Seranged Jul 20, 2026
2d1312f
fix: center activity event icons
Seranged Jul 20, 2026
02591b6
fix: declutter activity rows and sign amounts
Seranged Jul 20, 2026
a904c29
fix: present governance activity semantically
Seranged Jul 20, 2026
166b268
fix: balance activity row collapsing and alignment
Seranged Jul 20, 2026
c5ded73
fix: embed activity expand toggle in the details column
Seranged Jul 20, 2026
c262a37
fix: center activity row summary and transaction link
Seranged Jul 20, 2026
38119cb
fix: align activity rows on a single centerline
Seranged Jul 20, 2026
055bd64
fix: center activity row icon, details, and link on card height
Seranged Jul 20, 2026
3865f28
fix: reduce activity row noise
Seranged Jul 20, 2026
9f2a609
fix: inline activity participants in the meta line at mid width
Seranged Jul 20, 2026
09bfae8
fix: drop separator dot from inline activity participants
Seranged Jul 20, 2026
a3f60be
fix: drop the mostly-empty activity participants column
Seranged Jul 20, 2026
860515c
fix: clarify the empty activity state
Seranged Jul 20, 2026
6b3a11a
fix: inset activity row hover surface
Seranged Jul 20, 2026
d037214
fix: drop the always-empty Position activity filter
Seranged Jul 20, 2026
28f7f1f
fix: keep activity timestamps current
Seranged Jul 21, 2026
0c9ab9c
fix: reduce Earn activity noise
Seranged Jul 21, 2026
8442a11
fix: refine portfolio activity presentation
Seranged Jul 21, 2026
0bab1c3
fix: refine portfolio activity hierarchy
Seranged Jul 21, 2026
57844bf
fix: lead portfolio activity with vault context
Seranged Jul 21, 2026
8b927e9
fix: align portfolio activity with vault identity
Seranged Jul 21, 2026
dc71453
fix: never display sub-account addresses in activity participants
Seranged Jul 21, 2026
78ea060
fix: reduce portfolio activity filters to a counted liquidations chip
Seranged Jul 21, 2026
ec72cdc
fix: rebalance portfolio activity row hierarchy
Seranged Jul 21, 2026
c747dfe
fix: outline unselected activity filter chips
Seranged Jul 21, 2026
456e425
fix: match activity filter chips to the app-wide filter pills
Seranged Jul 21, 2026
e707898
fix: extract the shared filter chip into UiFilterChip
Seranged Jul 21, 2026
d2b9690
fix: separate transaction bundles from flat activity rows
Seranged Jul 21, 2026
0772c45
fix: anchor bundle headers and tighten event verbs
Seranged Jul 21, 2026
f4272d6
fix: render every portfolio activity entry as a card
Seranged Jul 21, 2026
f7eca07
fix: only link liquidated positions that still exist
Seranged Jul 21, 2026
8e0fd1b
fix: decode and declutter the vault activity feed
Seranged Jul 21, 2026
fd2cb9d
fix: keep activity status copy user-facing
Seranged Jul 21, 2026
de26576
fix: reduce activity syncing noise
Seranged Jul 21, 2026
194eb9a
fix: surface Earn activity reliably and trim reallocation noise
Seranged Jul 21, 2026
6b57f58
fix: stop activity meta lines wrapping on glyph-width variance
Seranged Jul 21, 2026
abab8ee
fix: visible separators between flat activity rows
Seranged Jul 21, 2026
11da6a4
fix: actually separate flat activity rows
Seranged Jul 21, 2026
84e024b
fix: match flat activity separators to bundle row rules
Seranged Jul 21, 2026
4fa1f64
fix: address activity review blockers
Seranged Jul 21, 2026
cab5730
fix: fail closed on spy owner lookup errors and unstrand filtered pages
Seranged Jul 21, 2026
cafcef9
fix: never consume an unverified spy address
Seranged Jul 21, 2026
354c7a8
fix: drop vault feed participant addresses entirely
Seranged Jul 21, 2026
6e28089
test: cover spy owner resolution edge cases
Seranged Jul 21, 2026
dc27445
feat: enrich liquidation activity rows with historical valuations
Seranged Jul 23, 2026
28a2611
fix: tolerate activity services without liquidations support
Seranged Jul 23, 2026
1cac48f
fix: keep the in-flight owner resolution when a pending spy address i…
Seranged Jul 23, 2026
dd31366
test: cover the liquidation-details composable contract
Seranged Jul 23, 2026
10d34d3
style: tighten liquidation detail rows
Seranged Jul 23, 2026
2e5bbe4
fix: never fall back to the connected wallet while spy verification i…
Seranged Jul 23, 2026
a9b245f
fix: drop wallet fallbacks from spy-safe owner-address derivations
Seranged Jul 24, 2026
e5bd7a8
Merge development
Seranged Jul 24, 2026
93f36e0
fix: avoid linking liquidations to current positions
Seranged Jul 27, 2026
614ee33
feat: default vault activity to lending filters
Seranged Jul 27, 2026
abcc732
fix: preserve liquidator repayments in activity
Seranged Jul 27, 2026
a234e1f
feat: label grouped liquidation activity
Seranged Jul 27, 2026
82b3cd4
style: simplify activity detail toggle
Seranged Jul 27, 2026
f140e6a
style: align activity detail toggle labels
Seranged Jul 27, 2026
b814e15
feat: refine activity detail presentation
Seranged Jul 28, 2026
386f217
fix: use published liquidation activity endpoint
Seranged Jul 29, 2026
380b111
fix: prevent activity detail overflow
Seranged Jul 29, 2026
43626b4
Merge remote-tracking branch 'origin/development' into HEAD
Seranged Jul 29, 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
174 changes: 174 additions & 0 deletions components/entities/activity/ActivityAddress.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<script setup lang="ts">
import type { ActivityVaultType } from '@eulerxyz/euler-v2-sdk'
import { getExplorerLink } from '~/utils/block-explorer'
import type { ActivityAddressLinkKind } from '~/utils/activity-display'
import { shortenAddress } from '~/utils/string-utils'

const props = withDefaults(defineProps<{
address: string
chainId: number
label?: string
linkKind?: ActivityAddressLinkKind
vaultType?: ActivityVaultType
compactVault?: boolean
}>(), {
linkKind: 'explorer',
compactVault: false,
})

const route = useRoute()
const { activateSpyMode } = useSpyMode()
const {
getVault,
getType: getVaultType,
getOrFetch: getOrFetchVault,
getVaultCategory,
isVerifiedVault,
registryVersion,
} = useVaultRegistry()
const addressRef = toRef(props, 'address')
const product = useEulerProductOfVault(addressRef)
const copyKey = computed(() => `activity-address-${props.address.toLowerCase()}`)
const explorerLink = computed(() => getExplorerLink(props.address, props.chainId, true))
const resolvedVault = computed(() => {
void registryVersion.value
return props.linkKind === 'vault' ? getVault(props.address) : undefined
})
const resolvedVaultType = computed(() =>
getVaultType(props.address) ?? props.vaultType)
const compactVaultName = computed(() => {
if (!resolvedVault.value) return props.label || shortenAddress(props.address)
if (getVaultCategory(resolvedVault.value.address) === 'escrow') {
return 'Escrowed collateral'
}
return product.name || resolvedVault.value.shares.name
})
const internalLink = computed(() => {
const query = typeof route.query.network === 'string'
? { network: route.query.network }
: undefined
if (props.linkKind === 'spy') {
return { path: '/portfolio/activity', query: { ...query, spy: props.address } }
}
if (props.linkKind === 'vault') {
return {
path: resolvedVaultType.value === 'earn' ? `/earn/${props.address}` : `/lend/${props.address}`,
query,
}
}
return null
})

watch(
() => [props.address, props.linkKind] as const,
([address, linkKind]) => {
if (linkKind === 'vault') void getOrFetchVault(address)
},
{ immediate: true },
)

const fallbackCopy = (text: string) => {
const textarea = document.createElement('textarea')
textarea.value = text
textarea.setAttribute('readonly', '')
textarea.style.position = 'fixed'
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.select()
const copied = document.execCommand('copy')
document.body.removeChild(textarea)
return copied
}

const writeAddressToClipboard = async (address: string) => {
if (fallbackCopy(address)) return
if (navigator.clipboard?.writeText) await navigator.clipboard.writeText(address)
else throw new Error('Unable to copy address')
}

const { isCopied, copyToClipboard } = useClipboardCopy({ timeout: 5000, write: writeAddressToClipboard })

const copyAddress = () => {
copyToClipboard(props.address, copyKey.value).catch(() => {})
}

const handleInternalClick = (event: MouseEvent) => {
if (
props.linkKind !== 'spy'
|| event.button !== 0
|| event.metaKey
|| event.ctrlKey
|| event.shiftKey
|| event.altKey
) return
activateSpyMode(props.address)
}
</script>

<template>
<span class="inline-flex min-w-0 max-w-full items-center gap-2">
<NuxtLink
v-if="internalLink && resolvedVault && compactVault"
:to="internalLink"
class="inline-flex min-w-0 max-w-full items-center gap-6 rounded-8 transition-opacity hover:opacity-80"
:title="address"
>
<AssetAvatar
:asset="[resolvedVault.asset]"
size="20"
/>
<span
class="min-w-0 truncate text-p4 text-content-secondary"
:title="compactVaultName"
>
<VaultDisplayName
:name="compactVaultName"
:is-unverified="!isVerifiedVault(resolvedVault.address)"
/>
</span>
<span class="shrink-0 text-p4 font-medium text-content-primary">
{{ resolvedVault.asset.symbol }}
</span>
</NuxtLink>
<NuxtLink
v-else-if="internalLink && resolvedVault"
:to="internalLink"
class="inline-flex min-w-0 max-w-full rounded-8 transition-opacity hover:opacity-80"
:title="address"
>
<VaultLabelsAndAssets
:vault="resolvedVault"
:assets="[resolvedVault.asset]"
size="small"
/>
</NuxtLink>
<NuxtLink
v-else-if="internalLink"
:to="internalLink"
class="min-w-0 truncate text-content-secondary transition-colors hover:text-accent-500 hover:underline"
:title="address"
@click="handleInternalClick"
>{{ label || shortenAddress(address) }}</NuxtLink>
<a
v-else
:href="explorerLink"
target="_blank"
rel="noopener noreferrer"
class="min-w-0 truncate text-content-secondary transition-colors hover:text-accent-500 hover:underline"
:title="address"
>{{ label || shortenAddress(address) }}</a>
<button
type="button"
class="inline-flex h-28 w-28 shrink-0 items-center justify-center rounded-6 text-content-muted transition-colors hover:bg-surface hover:text-content-primary"
:aria-label="isCopied(copyKey) ? `Copied address ${address}` : `Copy address ${address}`"
:title="isCopied(copyKey) ? 'Copied' : 'Copy address'"
@click.stop="copyAddress"
>
<SvgIcon
:name="isCopied(copyKey) ? 'check' : 'copy'"
class="!h-16 !w-16"
aria-hidden="true"
/>
</button>
</span>
</template>
45 changes: 45 additions & 0 deletions components/entities/activity/ActivityCategoryFilters.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script setup lang="ts">
import type { ActivityFilterOption } from '~/utils/activity-display'

const selected = defineModel<string[]>({ required: true })
defineProps<{
options: readonly ActivityFilterOption[]
}>()

const selectAll = () => {
selected.value = []
}

const toggle = (filter: string) => {
selected.value = selected.value.includes(filter)
? selected.value.filter(value => value !== filter)
: [...selected.value, filter]
}
</script>

<template>
<div
class="flex max-w-full flex-wrap gap-8"
role="group"
aria-label="Activity categories"
>
<UiFilterChip
class="shrink-0"
:active="selected.length === 0"
:aria-pressed="selected.length === 0"
@click="selectAll"
>
All
</UiFilterChip>
<UiFilterChip
v-for="option in options"
:key="option.value"
class="shrink-0"
:active="selected.includes(option.value)"
:aria-pressed="selected.includes(option.value)"
@click="toggle(option.value)"
>
{{ option.count !== undefined ? `${option.label} (${option.count})` : option.label }}
</UiFilterChip>
</div>
</template>
Loading
Loading