diff --git a/.env.example b/.env.example index 6cb736d11..c2d9baae9 100644 --- a/.env.example +++ b/.env.example @@ -75,6 +75,9 @@ RPC_URL_1= V3_API_URL="https://v3.euler.finance" # Optional server-side key forwarded by the /api/internal/v3 proxy as X-API-Key. EULER_SDK_V3_API_KEY= +# Euler interfaces branch used by the SDK ABI service and +# /api/internal/euler-chains. When set, it takes precedence over the direct URL below. +EULER_SDK_EULER_INTERFACES_BRANCH="master" # Adapter chain used by the server snapshot builder (server-side only). # - fallback (default): V3 primary, on-chain secondary @@ -123,7 +126,8 @@ NUXT_PUBLIC_CONFIG_LABELS_BASE_URL= # Base URL for oracle checks data. When set, ORACLE_CHECKS_REPO is ignored. # Must serve files at {baseUrl}/{chainId}/adapters/{address}.json. NUXT_PUBLIC_CONFIG_ORACLE_CHECKS_BASE_URL= -# Full URL for EulerChains.json (contract addresses). Defaults to GitHub raw if empty. +# Full URL for EulerChains.json (contract addresses), used only when +# EULER_SDK_EULER_INTERFACES_BRANCH and its public aliases are empty. NUXT_PUBLIC_CONFIG_EULER_CHAINS_URL= # Feature flags — all enabled by default diff --git a/README.md b/README.md index c6ecea4b2..d55c3760b 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Euler Lite uses the [Euler V2 SDK](https://github.com/euler-xyz/euler-sdks) for | `DEPRECATED_CHAINS` | — | Comma-separated chain IDs shown collapsed in the chain selector and skipped by startup warm-cache cycles. | | `ONCHAIN_SDK_CHAINS` | — | Comma-separated chain IDs pinned to the onchain SDK adapter config for chain-aware browser reads and the server vault snapshot, bypassing V3. Independent of `DEPRECATED_CHAINS`; list a chain in both to deprecate it and route it onchain. | | `EVAULT_FETCH_CHUNK_CHAINS` | — | Comma-separated chain IDs whose EVault list reads are split into small sequential SDK calls. Use for RPC/lens endpoints that fail under larger concurrent onchain EVault fetches. | +| `EULER_SDK_EULER_INTERFACES_BRANCH` | `master` | Euler interfaces branch used for runtime ABIs and `EulerChains.json`. When set, it takes precedence over `NUXT_PUBLIC_CONFIG_EULER_CHAINS_URL`. | `fallback` uses V3 first and on-chain reads second. If no V3 URL is configured, Lite passes `disableV3: true` to the SDK so fallback reads go straight on-chain. @@ -98,7 +99,7 @@ These use Nuxt's `runtimeConfig` and are set via `NUXT_PUBLIC_CONFIG_*` env vars | `NUXT_PUBLIC_CONFIG_LABELS_BASE_URL` | — | S3/CDN base URL for labels (overrides repo/branch) | | `NUXT_PUBLIC_CONFIG_ORACLE_CHECKS_REPO` | `euler-xyz/oracle-checks` | GitHub repo for oracle check results | | `NUXT_PUBLIC_CONFIG_ORACLE_CHECKS_BASE_URL` | — | S3/CDN base URL for oracle checks (overrides repo) | -| `NUXT_PUBLIC_CONFIG_EULER_CHAINS_URL` | — | URL for EulerChains.json (overrides default upstream) | +| `NUXT_PUBLIC_CONFIG_EULER_CHAINS_URL` | — | URL for EulerChains.json, used when no Euler interfaces branch is configured | | `NUXT_PUBLIC_CONFIG_DOCS_URL` | — | Documentation link | | `NUXT_PUBLIC_CONFIG_STARGATE_URL` | — | Stargate link | | `NUXT_PUBLIC_CONFIG_TOS_URL` | — | Terms of Service link | diff --git a/abis/brevis.ts b/abis/brevis.ts deleted file mode 100644 index fb09c65a1..000000000 --- a/abis/brevis.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const brevisClaimABI = [ - { - type: 'function', - name: 'claim', - inputs: [ - { - name: 'earner', - type: 'address', - internalType: 'address', - }, - { - name: 'cumulativeAmounts', - type: 'uint256[]', - internalType: 'uint256[]', - }, - { - name: 'epoch', - type: 'uint64', - internalType: 'uint64', - }, - { - name: 'proof', - type: 'bytes32[]', - internalType: 'bytes32[]', - }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, -] as const diff --git a/abis/erc20.ts b/abis/erc20.ts index 0b3a17d1a..58a4f28b4 100644 --- a/abis/erc20.ts +++ b/abis/erc20.ts @@ -1,39 +1,3 @@ -export const erc20ApproveAbi = [ - { - type: 'function', - name: 'approve', - inputs: [ - { name: 'spender', type: 'address' }, - { name: 'amount', type: 'uint256' }, - ], - outputs: [{ name: '', type: 'bool' }], - stateMutability: 'nonpayable', - }, -] as const - -export const erc20TransferAbi = [ - { - type: 'function', - name: 'transfer', - inputs: [ - { name: 'to', type: 'address' }, - { name: 'amount', type: 'uint256' }, - ], - outputs: [{ name: '', type: 'bool' }], - stateMutability: 'nonpayable', - }, -] as const - -export const erc20BalanceOfAbi = [ - { - type: 'function', - name: 'balanceOf', - inputs: [{ name: 'account', type: 'address' }], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'view', - }, -] as const - export const erc20SymbolAbi = [ { type: 'function', diff --git a/abis/evc.ts b/abis/evc.ts index 4aa4afb3d..84865d454 100644 --- a/abis/evc.ts +++ b/abis/evc.ts @@ -1,11 +1,11 @@ -export const BATCH_ITEM_COMPONENTS = [ +const BATCH_ITEM_COMPONENTS = [ { name: 'targetContract', type: 'address' }, { name: 'onBehalfOfAccount', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'data', type: 'bytes' }, ] as const -export const BATCH_ITEM_RESULT_COMPONENTS = [ +const BATCH_ITEM_RESULT_COMPONENTS = [ { name: 'success', type: 'bool' }, { name: 'result', type: 'bytes' }, ] as const @@ -83,55 +83,6 @@ export type BatchItemResult = { result: string } -export const evcEnableControllerAbi = [ - { - type: 'function', - name: 'enableController', - inputs: [ - { name: 'account', type: 'address' }, - { name: 'vault', type: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, -] as const - -export const evcDisableControllerAbi = [ - { - type: 'function', - name: 'disableController', - inputs: [], - outputs: [], - stateMutability: 'nonpayable', - }, -] as const - -export const evcEnableCollateralAbi = [ - { - type: 'function', - name: 'enableCollateral', - inputs: [ - { name: 'account', type: 'address' }, - { name: 'vault', type: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, -] as const - -export const evcDisableCollateralAbi = [ - { - type: 'function', - name: 'disableCollateral', - inputs: [ - { name: 'account', type: 'address' }, - { name: 'vault', type: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, -] as const - export const evcGetAccountOwnerAbi = [ { type: 'function', @@ -141,13 +92,3 @@ export const evcGetAccountOwnerAbi = [ stateMutability: 'view', }, ] as const - -export const evcGetControllersAbi = [ - { - type: 'function', - name: 'getControllers', - inputs: [{ name: 'account', type: 'address' }], - outputs: [{ name: '', type: 'address[]' }], - stateMutability: 'view', - }, -] as const diff --git a/abis/fuul.ts b/abis/fuul.ts deleted file mode 100644 index c82b96a26..000000000 --- a/abis/fuul.ts +++ /dev/null @@ -1,50 +0,0 @@ -export const fuulManagerABI = [ - { - type: 'function', - name: 'claim', - inputs: [ - { - name: 'claimChecks', - type: 'tuple[]', - internalType: 'struct ClaimCheck[]', - components: [ - { name: 'projectAddress', type: 'address', internalType: 'address' }, - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'currency', type: 'address', internalType: 'address' }, - { name: 'currencyType', type: 'uint8', internalType: 'enum IFuulProject.TokenType' }, - { name: 'amount', type: 'uint256', internalType: 'uint256' }, - { name: 'reason', type: 'uint8', internalType: 'enum ClaimReason' }, - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - { name: 'deadline', type: 'uint256', internalType: 'uint256' }, - { name: 'proof', type: 'bytes32', internalType: 'bytes32' }, - { name: 'signatures', type: 'bytes[]', internalType: 'bytes[]' }, - ], - }, - ], - outputs: [], - stateMutability: 'payable', - }, -] as const - -export const fuulFactoryABI = [ - { - type: 'function', - name: 'getFeesInformation', - inputs: [ - { name: 'projectAddress', type: 'address', internalType: 'address' }, - ], - outputs: [ - { - name: '', - type: 'tuple', - internalType: 'struct FeesInformation', - components: [ - { name: 'projectOwnerClaimFee', type: 'uint256', internalType: 'uint256' }, - { name: 'nativeUserClaimFee', type: 'uint256', internalType: 'uint256' }, - { name: 'tokenUserClaimFee', type: 'uint256', internalType: 'uint256' }, - ], - }, - ], - stateMutability: 'view', - }, -] as const diff --git a/abis/merkl.ts b/abis/merkl.ts deleted file mode 100644 index b945acf17..000000000 --- a/abis/merkl.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const merklDistributorABI = [ - { - type: 'function', - name: 'claim', - inputs: [ - { - name: 'users', - type: 'address[]', - internalType: 'address[]', - }, - { - name: 'tokens', - type: 'address[]', - internalType: 'address[]', - }, - { - name: 'amounts', - type: 'uint256[]', - internalType: 'uint256[]', - }, - { - name: 'proofs', - type: 'bytes32[][]', - internalType: 'bytes32[][]', - }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, -] as const diff --git a/abis/vault.ts b/abis/vault.ts index 9813ea8ab..4967e729b 100644 --- a/abis/vault.ts +++ b/abis/vault.ts @@ -1,80 +1,3 @@ -export const vaultBorrowAbi = [ - { - type: 'function', - name: 'borrow', - inputs: [ - { name: 'amount', type: 'uint256' }, - { name: 'receiver', type: 'address' }, - ], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'nonpayable', - }, -] as const - -export const vaultRepayAbi = [ - { - type: 'function', - name: 'repay', - inputs: [ - { name: 'amount', type: 'uint256' }, - { name: 'receiver', type: 'address' }, - ], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'nonpayable', - }, -] as const - -export const vaultDepositAbi = [ - { - type: 'function', - name: 'deposit', - inputs: [ - { name: 'amount', type: 'uint256' }, - { name: 'receiver', type: 'address' }, - ], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'nonpayable', - }, -] as const - -export const vaultWithdrawAbi = [ - { - type: 'function', - name: 'withdraw', - inputs: [ - { name: 'amount', type: 'uint256' }, - { name: 'receiver', type: 'address' }, - { name: 'owner', type: 'address' }, - ], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'nonpayable', - }, -] as const - -export const vaultRedeemAbi = [ - { - type: 'function', - name: 'redeem', - inputs: [ - { name: 'shares', type: 'uint256' }, - { name: 'receiver', type: 'address' }, - { name: 'owner', type: 'address' }, - ], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'nonpayable', - }, -] as const - -export const vaultPreviewWithdrawAbi = [ - { - type: 'function', - name: 'previewWithdraw', - inputs: [{ name: 'assets', type: 'uint256' }], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'view', - }, -] as const - export const vaultConvertToAssetsAbi = [ { type: 'function', @@ -84,65 +7,3 @@ export const vaultConvertToAssetsAbi = [ stateMutability: 'view', }, ] as const - -export const vaultConvertToSharesAbi = [ - { - type: 'function', - name: 'convertToShares', - inputs: [{ name: 'assets', type: 'uint256' }], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'view', - }, -] as const - -export const vaultMaxWithdrawAbi = [ - { - type: 'function', - name: 'maxWithdraw', - inputs: [{ name: 'owner', type: 'address' }], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'view', - }, -] as const - -export const vaultSkimAbi = [ - { - type: 'function', - name: 'skim', - inputs: [ - { name: 'amount', type: 'uint256' }, - { name: 'receiver', type: 'address' }, - ], - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'nonpayable', - }, -] as const - -export const vaultRepayWithSharesAbi = [ - { - type: 'function', - name: 'repayWithShares', - inputs: [ - { name: 'amount', type: 'uint256' }, - { name: 'receiver', type: 'address' }, - ], - outputs: [ - { name: 'shares', type: 'uint256' }, - { name: 'debt', type: 'uint256' }, - ], - stateMutability: 'nonpayable', - }, -] as const - -export const vaultTransferFromMaxAbi = [ - { - type: 'function', - name: 'transferFromMax', - inputs: [ - { name: 'from', type: 'address' }, - { name: 'to', type: 'address' }, - ], - outputs: [{ name: '', type: 'bool' }], - stateMutability: 'nonpayable', - }, -] as const diff --git a/abis/weth.ts b/abis/weth.ts deleted file mode 100644 index 8b9090dad..000000000 --- a/abis/weth.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const wethDepositAbi = [ - { - type: 'function', - name: 'deposit', - inputs: [], - outputs: [], - stateMutability: 'payable', - }, -] as const diff --git a/components/entities/activity/ActivityAddress.vue b/components/entities/activity/ActivityAddress.vue new file mode 100644 index 000000000..9232b8825 --- /dev/null +++ b/components/entities/activity/ActivityAddress.vue @@ -0,0 +1,186 @@ + + + diff --git a/components/entities/activity/ActivityCategoryFilters.vue b/components/entities/activity/ActivityCategoryFilters.vue new file mode 100644 index 000000000..73c0c486d --- /dev/null +++ b/components/entities/activity/ActivityCategoryFilters.vue @@ -0,0 +1,45 @@ + + + diff --git a/components/entities/activity/ActivityEventRow.vue b/components/entities/activity/ActivityEventRow.vue new file mode 100644 index 000000000..adb8d3eab --- /dev/null +++ b/components/entities/activity/ActivityEventRow.vue @@ -0,0 +1,678 @@ + + + + + diff --git a/components/entities/activity/ActivityFeed.vue b/components/entities/activity/ActivityFeed.vue new file mode 100644 index 000000000..73bdc3751 --- /dev/null +++ b/components/entities/activity/ActivityFeed.vue @@ -0,0 +1,464 @@ + + + + + diff --git a/components/entities/activity/activityEventRowDetails.ts b/components/entities/activity/activityEventRowDetails.ts new file mode 100644 index 000000000..14a3a2e71 --- /dev/null +++ b/components/entities/activity/activityEventRowDetails.ts @@ -0,0 +1,36 @@ +import type { ActivityEvent } from '@eulerxyz/euler-v2-sdk' +import type { ActivityLiquidationDisplayDetails } from '~/utils/activity-display' + +const QUEUE_EVENT_TYPES: readonly ActivityEvent['type'][] = [ + 'set_supply_queue', + 'set_withdraw_queue', +] + +export const getActivityAddressCollectionSummary = ( + eventType: ActivityEvent['type'], + count: number, +): string | null => { + if (count <= 1) return null + return QUEUE_EVENT_TYPES.includes(eventType) + ? `${count} strategies` + : `${count} addresses` +} + +export const getActivityLiquidationBonusEntry = ( + display: ActivityLiquidationDisplayDetails | null, +) => { + if (!display?.bonus) return null + return { + kind: 'valuation' as const, + key: 'liquidation-bonus', + label: 'Liquidator bonus', + value: display.bonus, + valueClass: display.bonusTone === 'positive' + ? 'text-accent-600' + : display.bonusTone === 'negative' + ? 'text-error-500' + : undefined, + valueTitle: display.bonusTitle, + addresses: undefined, + } +} diff --git a/components/entities/asset/AssetAvatar.vue b/components/entities/asset/AssetAvatar.vue index 78e83ef79..f69041c34 100644 --- a/components/entities/asset/AssetAvatar.vue +++ b/components/entities/asset/AssetAvatar.vue @@ -3,7 +3,7 @@ import { getAssetLogoUrl } from '~/composables/useTokenList' const { asset, size, iconUrl } = defineProps<{ asset: { address: string, symbol: string } | { address: string, symbol: string }[] - size?: '16' | '20' | '28' | '36' | '38' | '40' | '46' + size?: '16' | '20' | '28' | '32' | '36' | '38' | '40' | '46' iconUrl?: string }>() diff --git a/components/entities/portfolio/PortfolioBorrowItem.vue b/components/entities/portfolio/PortfolioBorrowItem.vue index 98be8484c..8a8bf4820 100644 --- a/components/entities/portfolio/PortfolioBorrowItem.vue +++ b/components/entities/portfolio/PortfolioBorrowItem.vue @@ -31,9 +31,11 @@ import { resolveComponent, type Component } from 'vue' const { position, clickable = true } = defineProps<{ position: PortfolioBorrowPosition, clickable?: boolean }>() const { getVaultCategory, isVerifiedVault } = useVaultRegistry() -const { address } = useWagmi() const { portfolioAddress } = useEulerAccount() -const ownerAddress = computed(() => portfolioAddress.value || address.value || '') +// portfolioAddress is already the spy-safe acting address; a wallet +// fallback here would compute wrong position indices while a spy +// candidate is still verifying. +const ownerAddress = computed(() => portfolioAddress.value) const subAccountIndex = computed(() => { if (!ownerAddress.value || !position.subAccount) return 0 return getSubAccountIndex(getAddress(ownerAddress.value), getAddress(position.subAccount)) diff --git a/components/entities/portfolio/PortfolioEarnItem.vue b/components/entities/portfolio/PortfolioEarnItem.vue index cf508ddb6..c471173cd 100644 --- a/components/entities/portfolio/PortfolioEarnItem.vue +++ b/components/entities/portfolio/PortfolioEarnItem.vue @@ -15,9 +15,11 @@ import { getVaultIntrinsicApyInfo } from '~/utils/vault-intrinsic-apy' const { position } = defineProps<{ position: PortfolioSavingsPosition }>() const modal = useModal() -const { address } = useWagmi() const { portfolioAddress } = useEulerAccount() -const ownerAddress = computed(() => portfolioAddress.value || address.value || '') +// portfolioAddress is already the spy-safe acting address; a wallet +// fallback here would compute wrong position indices while a spy +// candidate is still verifying. +const ownerAddress = computed(() => portfolioAddress.value) const subAccountIndex = computed(() => { if (!ownerAddress.value || !position.subAccount) return 0 return getSubAccountIndex(getAddress(ownerAddress.value), getAddress(position.subAccount)) diff --git a/components/entities/portfolio/PortfolioSavingItem.vue b/components/entities/portfolio/PortfolioSavingItem.vue index adeed29e2..31ac2ff24 100644 --- a/components/entities/portfolio/PortfolioSavingItem.vue +++ b/components/entities/portfolio/PortfolioSavingItem.vue @@ -15,9 +15,11 @@ import { getVaultIntrinsicApyInfo } from '~/utils/vault-intrinsic-apy' const { position } = defineProps<{ position: PortfolioSavingsPosition }>() const modal = useModal() -const { address } = useWagmi() const { portfolioAddress } = useEulerAccount() -const ownerAddress = computed(() => portfolioAddress.value || address.value || '') +// portfolioAddress is already the spy-safe acting address; a wallet +// fallback here would compute wrong position indices while a spy +// candidate is still verifying. +const ownerAddress = computed(() => portfolioAddress.value) const subAccountIndex = computed(() => { if (!ownerAddress.value || !position.subAccount) return 0 return getSubAccountIndex(getAddress(ownerAddress.value), getAddress(position.subAccount)) diff --git a/components/entities/portfolio/PortfolioUnsupportedBorrowItem.vue b/components/entities/portfolio/PortfolioUnsupportedBorrowItem.vue index 4c13f2c31..0f86c0ae5 100644 --- a/components/entities/portfolio/PortfolioUnsupportedBorrowItem.vue +++ b/components/entities/portfolio/PortfolioUnsupportedBorrowItem.vue @@ -15,9 +15,11 @@ import { useEulerProductOfVault } from '~/composables/useEulerLabels' const { position } = defineProps<{ position: PortfolioBorrowPosition }>() -const { address } = useWagmi() const { portfolio, portfolioAddress } = useEulerAccount() -const ownerAddress = computed(() => portfolioAddress.value || address.value || '') +// portfolioAddress is already the spy-safe acting address; a wallet +// fallback here would compute wrong position indices while a spy +// candidate is still verifying. +const ownerAddress = computed(() => portfolioAddress.value) const subAccountIndex = computed(() => { if (!ownerAddress.value || !position.subAccount) return 0 return getSubAccountIndex(getAddress(ownerAddress.value), getAddress(position.subAccount)) diff --git a/components/entities/vault/ChooseCollateralModal.vue b/components/entities/vault/ChooseCollateralModal.vue index b2dc719dd..4eb83661e 100644 --- a/components/entities/vault/ChooseCollateralModal.vue +++ b/components/entities/vault/ChooseCollateralModal.vue @@ -34,12 +34,14 @@ const { }>() const { isEscrowVault } = useVaultRegistry() -const { address } = useWagmi() const { portfolioAddress } = useEulerAccount() const searchQuery = ref('') const selectedIdx = ref(selected) -const ownerAddress = computed(() => portfolioAddress.value || address.value || '') +// portfolioAddress is already the spy-safe acting address; a wallet +// fallback here would compute wrong position indices while a spy +// candidate is still verifying. +const ownerAddress = computed(() => portfolioAddress.value) const getOptionLabel = (option: CollateralOption) => { if (option.vaultAddress && isEscrowVault(option.vaultAddress)) return 'Escrowed collateral' if (option.label) return option.label diff --git a/components/entities/vault/VaultLabelsAndAssets.vue b/components/entities/vault/VaultLabelsAndAssets.vue index 3e2656d83..1f95e6088 100644 --- a/components/entities/vault/VaultLabelsAndAssets.vue +++ b/components/entities/vault/VaultLabelsAndAssets.vue @@ -8,7 +8,8 @@ import { getAddress } from 'viem' const { vault, assets, size, assetsLabel, pairVault, back, backFallback } = defineProps<{ vault?: EVault | EulerEarn | SecuritizeCollateralVault assets: VaultAsset[] - size?: 'large' + /** `small` renders a compact identity for list contexts (e.g. activity rows) where surrounding content carries the emphasis. */ + size?: 'large' | 'small' assetsLabel?: string pairVault?: EVault back?: boolean @@ -91,7 +92,7 @@ const displayAssetsLabel = computed(() => assetsLabel || assets.map(asset => ass