Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 8 additions & 2 deletions packages/euler-v2-sdk/docs/config-through-env.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,21 @@ endpoints without changing the V3 configuration used by other SDK services.

`EULER_SDK_VAULT_TYPE_V3_TYPE_MAP_JSON` is a JSON object with string values, for example `{"custom":"EVault"}`.

## Pricing, Swaps, And Deployments
## Pricing, Swaps, And Euler Interfaces

| Config field | Environment variable | Default |
|---|---|---|
| `pricingApiUrl` | `EULER_SDK_PRICING_API_URL` | `v3ApiUrl` |
| `pricingApiKey` | `EULER_SDK_PRICING_API_KEY` | `v3ApiKey` |
| `swapApiUrl` | `EULER_SDK_SWAP_API_URL` | `https://swap.euler.finance` |
| `swapDefaultDeadline` | `EULER_SDK_SWAP_DEFAULT_DEADLINE` | `1800` |
| `deploymentsUrl` | `EULER_SDK_DEPLOYMENTS_URL` | Euler interfaces `EulerChains.json` |
| `eulerInterfacesBranch` | `EULER_SDK_EULER_INTERFACES_BRANCH` | `master` |
| `deploymentsUrl` | `EULER_SDK_DEPLOYMENTS_URL` | `EulerChains.json` from `eulerInterfacesBranch` |

`eulerInterfacesBranch` keeps the runtime ABI service and the default
deployments document on the same `euler-interfaces` branch. A direct
`deploymentsUrl` remains available for custom mirrors and takes precedence for
the deployment service without changing the ABI branch.

## Rewards

Expand Down
29 changes: 26 additions & 3 deletions packages/euler-v2-sdk/src/sdk/buildSDK.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { Address } from "viem";
import { EulerSDK } from "./sdk.js";
import { ABIService, type IABIService } from "../services/abiService/index.js";
import {
ABIService,
DEFAULT_EULER_INTERFACES_BRANCH,
type IABIService,
} from "../services/abiService/index.js";
import {
DeploymentService,
type IDeploymentService,
Expand Down Expand Up @@ -106,6 +110,7 @@ import {
defaultSwapServiceConfig,
defaultTokenlistServiceConfig,
defaultVaultTypeAdapterConfig,
getEulerInterfacesDeploymentsUrl,
} from "./defaultConfig.js";
import { defaultEVaultV3AdapterConfig } from "./defaultConfig.js";
import {
Expand Down Expand Up @@ -568,15 +573,27 @@ export async function buildEulerSDK<
};
const resolvedBuildQuery =
buildQuery ?? createQueryCacheBuildQuery(resolvedQueryCacheConfig);
const resolvedEulerInterfacesBranch =
pickConfigValue(
config?.eulerInterfacesBranch,
undefined,
envConfig.eulerInterfacesBranch,
) ?? DEFAULT_EULER_INTERFACES_BRANCH;
const resolvedDeploymentServiceConfig = {
...defaultDeploymentServiceConfig,
deploymentsUrl: getEulerInterfacesDeploymentsUrl(
resolvedEulerInterfacesBranch,
),
...maybeField("deploymentsUrl", envConfig.deploymentsUrl),
...maybeField("deploymentsUrl", config?.deploymentsUrl),
};

// Build core services (these may be needed for adapters even if overridden)
const abiService =
servicesOverrides?.abiService ?? new ABIService(resolvedBuildQuery);
servicesOverrides?.abiService ??
new ABIService(resolvedBuildQuery, {
eulerInterfacesBranch: resolvedEulerInterfacesBranch,
});
const deploymentService =
servicesOverrides?.deploymentService ??
(await DeploymentService.build(
Expand Down Expand Up @@ -651,6 +668,7 @@ export async function buildEulerSDK<
deploymentService as DeploymentService,
accountVaultsAdapter,
resolvedBuildQuery,
abiService,
);
return accountOnchainAdapter;
};
Expand Down Expand Up @@ -1419,7 +1437,10 @@ export async function buildEulerSDK<
fuulManagerAddress: directAdapter.getFuulManagerAddress(),
fuulFactoryAddress: directAdapter.getFuulFactoryAddress(),
},
{ isActiveForViewer: rewardsServiceConfig?.isActiveForViewer },
{
isActiveForViewer: rewardsServiceConfig?.isActiveForViewer,
abiService,
},
);
})();

Expand Down Expand Up @@ -1571,6 +1592,7 @@ export async function buildEulerSDK<
);

if (executionService instanceof ExecutionService) {
executionService.setABIService(abiService);
executionService.setProviderService(providerService as ProviderService);
executionService.setVaultMetaService(
vaultMetaService as IVaultMetaService<TVaultEntity>,
Expand Down Expand Up @@ -1611,6 +1633,7 @@ export async function buildEulerSDK<
}

if (rewardsService instanceof RewardsService) {
rewardsService.setABIService(abiService);
rewardsService.setProviderService(providerService as ProviderService);
rewardsService.setDeploymentService(deploymentService as DeploymentService);
}
Expand Down
5 changes: 5 additions & 0 deletions packages/euler-v2-sdk/src/sdk/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export interface EulerSDKConfig {
swapApiUrl?: string;
swapDefaultDeadline?: number;

eulerInterfacesBranch?: string;
deploymentsUrl?: string;

eulerLabelsBaseUrl?: string;
Expand Down Expand Up @@ -497,6 +498,10 @@ export function readEulerSDKEnvConfig(
swapApiUrl: readString(env, "EULER_SDK_SWAP_API_URL"),
swapDefaultDeadline: readNumber(env, "EULER_SDK_SWAP_DEFAULT_DEADLINE"),

eulerInterfacesBranch: readString(
env,
"EULER_SDK_EULER_INTERFACES_BRANCH",
),
deploymentsUrl: readString(env, "EULER_SDK_DEPLOYMENTS_URL"),

eulerLabelsBaseUrl: readString(env, "EULER_SDK_EULER_LABELS_BASE_URL"),
Expand Down
9 changes: 7 additions & 2 deletions packages/euler-v2-sdk/src/sdk/defaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { PricingServiceConfig } from "../services/priceService/index.js";
import type { IntrinsicApyV3AdapterConfig } from "../services/intrinsicApyService/index.js";
import type { RewardsV3AdapterConfig } from "../services/rewardsService/index.js";
import type { ActivityServiceConfig } from "../services/activityService/index.js";
import { DEFAULT_EULER_INTERFACES_BRANCH } from "../services/abiService/index.js";

const SUBGRAPH_BASE_URL =
"https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs";
Expand Down Expand Up @@ -106,9 +107,13 @@ export const defaultSwapServiceConfig: SwapServiceConfig = {
defaultDeadline: 1800, // 30 minutes
};

export const getEulerInterfacesDeploymentsUrl = (
branch = DEFAULT_EULER_INTERFACES_BRANCH,
): string =>
`https://raw.githubusercontent.com/euler-xyz/euler-interfaces/refs/heads/${branch}/EulerChains.json`;
Comment thread
Seranged marked this conversation as resolved.

export const defaultDeploymentServiceConfig: DeploymentServiceConfig = {
deploymentsUrl:
"https://raw.githubusercontent.com/euler-xyz/euler-interfaces/refs/heads/master/EulerChains.json",
deploymentsUrl: getEulerInterfacesDeploymentsUrl(),
};

export const DEFAULT_TOKENLIST_API_BASE_URL = DEFAULT_V3_API_URL;
Expand Down
54 changes: 45 additions & 9 deletions packages/euler-v2-sdk/src/services/abiService/abiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,67 @@ export interface IABIService {
fetchABI(chainId: number, contract: string): Promise<Abi>;
}

export interface ABIServiceConfig {
eulerInterfacesBranch?: string;
}

export const DEFAULT_EULER_INTERFACES_BRANCH = "master";

export class ABIService implements IABIService {
private readonly abis: Record<string, Abi> = {};
private readonly abiRequests: Record<string, Promise<Abi>> = {};

constructor(buildQuery?: BuildQueryFn) {
constructor(
buildQuery?: BuildQueryFn,
private readonly config: ABIServiceConfig = {},
) {
if (buildQuery) applyBuildQuery(this, buildQuery);
}

private getABIURL(_: number, contract: string): string {
return `https://raw.githubusercontent.com/euler-xyz/euler-interfaces/refs/heads/master/abis/${contract}.json`;
const branch =
this.config.eulerInterfacesBranch?.trim() ||
DEFAULT_EULER_INTERFACES_BRANCH;
return `https://raw.githubusercontent.com/euler-xyz/euler-interfaces/refs/heads/${branch}/abis/${contract}.json`;
}

queryABI = async (url: string): Promise<Abi> => {
const response = await fetch(url);
return response.json() as Promise<Abi>;
if (!response.ok) {
throw new Error(
`Failed to fetch ABI (${response.status} ${response.statusText})`,
);
}

const abi: unknown = await response.json();
if (!Array.isArray(abi)) {
throw new Error("Invalid ABI response");
}

return abi as Abi;
};

setQueryABI(fn: typeof this.queryABI): void {
this.queryABI = fn;
}

async fetchABI(_: number, contract: string): Promise<Abi> {
if (!this.abis[contract]) {
this.abis[contract] = await this.queryABI(this.getABIURL(_, contract));
}
async fetchABI(chainId: number, contract: string): Promise<Abi> {
// Keyed by resolved URL rather than contract name, so the cache follows
// whatever `getABIURL` keys on (today the URL is chain-agnostic, so all
// chains share one request).
const url = this.getABIURL(chainId, contract);
const pending = this.abiRequests[url];
if (pending) return pending;

// Evict failed requests so a later call retries instead of replaying the
// rejection for the lifetime of the service.
const request = this.queryABI(url).catch((error) => {
if (this.abiRequests[url] === request) {
delete this.abiRequests[url];
}
throw error;
});
this.abiRequests[url] = request;

return this.abis[contract];
return request;
}
}
7 changes: 5 additions & 2 deletions packages/euler-v2-sdk/src/services/abiService/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export { ABIService } from "./abiService.js";
export type { IABIService } from "./abiService.js";
export {
ABIService,
DEFAULT_EULER_INTERFACES_BRANCH,
} from "./abiService.js";
export type { ABIServiceConfig, IABIService } from "./abiService.js";
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ export const accountLensAbi = [
type: "tuple[]",
internalType: "struct VaultAccountInfo[]",
components: [
{
name: "queryFailure",
type: "bool",
internalType: "bool",
},
{
name: "queryFailureReason",
type: "bytes",
internalType: "bytes",
},
{
name: "timestamp",
type: "uint256",
Expand Down Expand Up @@ -442,6 +452,16 @@ export const accountLensAbi = [
type: "tuple",
internalType: "struct VaultAccountInfo",
components: [
{
name: "queryFailure",
type: "bool",
internalType: "bool",
},
{
name: "queryFailureReason",
type: "bytes",
internalType: "bytes",
},
{
name: "timestamp",
type: "uint256",
Expand Down Expand Up @@ -1066,6 +1086,16 @@ export const accountLensAbi = [
type: "tuple",
internalType: "struct VaultAccountInfo",
components: [
{
name: "queryFailure",
type: "bool",
internalType: "bool",
},
{
name: "queryFailureReason",
type: "bytes",
internalType: "bytes",
},
{
name: "timestamp",
type: "uint256",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,24 @@ export interface AccountLiquidityInfo {
collateralValuesRaw: bigint[];
}

export interface AccountRewardInfo {
timestamp: bigint;
account: Address;
vault: Address;
balanceTracker: Address;
balanceForwarderEnabled: boolean;
balance: bigint;
enabledRewardsInfo: {
reward: Address;
earnedReward: bigint;
earnedRewardRecentIgnored: bigint;
}[];
}

export interface VaultAccountInfo {
/** Present on Account Lens deployments that report whole-vault query failures. */
queryFailure?: boolean;
queryFailureReason?: Hex;
timestamp: bigint;
account: Address;
vault: Address;
Expand Down
Loading