Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update types #2

Merged
merged 2 commits into from
Aug 22, 2023
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
37 changes: 12 additions & 25 deletions packages/core/src/common/utils/getFeeData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Client } from "viem";
import { PublicClient, type BlockTag } from "viem";

export type FeeData = {
lastBaseFeePerGas: null | bigint;
Expand All @@ -7,35 +7,22 @@ export type FeeData = {
gasPrice: null | bigint;
};

const getFeeHistory = (
provider: Client,
blockCount: number,
latestBlock: string,
percentile?: number[]
): Promise<{
baseFeePerGas: string[];
gasUsedRatio: number[];
oldestBlock: string;
reward: string[][];
}> => {
return provider.request({
method: "eth_feeHistory",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
params: [`0x${blockCount.toString(16)}`, latestBlock, percentile],
});
};

export const getFeeData = async (provider: Client): Promise<FeeData> => {
export const getFeeData = async (provider: PublicClient): Promise<FeeData> => {
// we look back 5 blocks at fees of botton 25% txs
// if you want to increase maxPriorityFee output increase percentile
const feeHistory = await getFeeHistory(provider, 5, "pending", [25]);
const feeHistory = await provider.getFeeHistory({
blockCount: 5,
blockTag: "pending",
rewardPercentiles: [25],
});

// get average priority fee
const maxPriorityFeePerGas =
feeHistory.reward
.map((fees) => (fees[0] ? BigInt(fees[0]) : 0n))
.reduce((sum, fee) => sum + fee) / BigInt(feeHistory.reward.length);
feeHistory.reward && feeHistory.reward.length > 0
? feeHistory.reward
.map((fees) => (fees[0] ? BigInt(fees[0]) : 0n))
.reduce((sum, fee) => sum + fee) / BigInt(feeHistory.reward.length)
: 0n;

const lastBaseFeePerGas = feeHistory.baseFeePerGas[0]
? BigInt(feeHistory.baseFeePerGas[0])
Expand Down
38 changes: 20 additions & 18 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,48 @@ import { SUPPORTED_CHAINS } from "./contants";
import { LidoSDKCoreProps } from "./types";

export default class LidoSDKCore {
readonly chainId: (typeof SUPPORTED_CHAINS)[number] | undefined;
readonly rpcUrls: string[] = [];
readonly rpcProvider: PublicClient | undefined;
readonly web3Provider: WalletClient | undefined;
readonly chain: Chain | undefined;
readonly chainId: (typeof SUPPORTED_CHAINS)[number];
readonly rpcUrls: string[] | undefined;
readonly rpcProvider: PublicClient;
readonly web3Provider: WalletClient;
readonly chain: Chain;

constructor(props: LidoSDKCoreProps, version?: string) {
const { chain, chainId, rpcUrls, rpcProvider, web3Provider } = this.init(
props,
version
);
const { chain, rpcProvider, web3Provider } = this.init(props, version);

this.chainId = chainId;
this.chainId = props.chainId;
this.chain = chain;
this.rpcUrls = rpcUrls;
this.rpcUrls = props.rpcUrls;
this.rpcProvider = rpcProvider;
this.web3Provider = web3Provider;
}

@Initialize("Init:")
@Logger("LOG:")
private init(props: LidoSDKCoreProps, _version?: string) {
const { chainId, rpcUrls, web3Provider } = props;
const { chainId, rpcUrls, web3Provider, rpcProvider } = props;

if (!SUPPORTED_CHAINS.includes(chainId)) {
throw new Error(`Unsupported chain: ${chainId}`);
}

if (rpcUrls.length === 0) {
if (!rpcProvider && rpcUrls.length === 0) {
throw new Error("rpcUrls is required");
}

if (!rpcUrls && !rpcProvider) {
throw new Error("rpcUrls or rpcProvider is required");
}

const chain = chainId === 1 ? mainnet : goerli;
const currentRpcProvider =
rpcProvider ?? this.createRpcProvider(chain, rpcUrls);
const currentWeb3Provider = web3Provider ?? this.createWeb3Provider(chain);

return {
chainId,
chain,
rpcUrls,
rpcProvider: this.createRpcProvider(chain, rpcUrls),
web3Provider: web3Provider ?? this.createWeb3Provider(chain),
rpcProvider: currentRpcProvider,
web3Provider: currentWeb3Provider,
};
}

Expand Down Expand Up @@ -141,7 +143,7 @@ export default class LidoSDKCore {

@ErrorHandler("Utils:")
@Logger("Utils:")
@Cache(30 * 1000)
@Cache(60 * 60 * 1000)
public async isContract(address: Address): Promise<boolean> {
invariant(this.rpcProvider, "RPC provider is not defined");
const { isContract } = await checkIsContract(this.rpcProvider, address);
Expand Down
15 changes: 13 additions & 2 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { WalletClient } from "viem";
import { WalletClient, PublicClient } from "viem";

import { SUPPORTED_CHAINS } from "./contants";

export type LidoSDKCoreProps = {
type LidoSDKCorePropsRpcUrls = {
chainId: (typeof SUPPORTED_CHAINS)[number];
rpcUrls: string[];
web3Provider?: WalletClient;
rpcProvider?: undefined;
};
type LidoSDKCorePropsRpcProvider = {
chainId: (typeof SUPPORTED_CHAINS)[number];
rpcUrls: undefined;
web3Provider?: WalletClient;
rpcProvider: PublicClient;
};

export type LidoSDKCoreProps =
| LidoSDKCorePropsRpcUrls
| LidoSDKCorePropsRpcProvider;
53 changes: 35 additions & 18 deletions packages/stake/src/stake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ import {} from "@lido-sdk/contracts/dist/cjs";
import { SUBMIT_EXTRA_GAS_TRANSACTION_RATIO } from "./common/constants";
import { version } from "./version";
import { abi } from "./abi/abi";
import { LidoSDKStakeProps, StakeCallbackStage, StakeProps } from "./types";
import {
LidoSDKStakeProps,
StakeCallbackStage,
StakeProps,
StakeResult,
} from "./types";

// TODO: move to constants

Expand Down Expand Up @@ -76,31 +81,31 @@ export class LidoSDKStake {

@ErrorHandler("Call:")
@Logger("Call:")
public async stake(props: StakeProps): Promise<void> {
public async stake(props: StakeProps): Promise<StakeResult> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StakeResult is okay, but I bet for external user the most convenient would be having transaction result as same generic type, because txs results share most properties but in the generic part we can include more method specific payload, like how much was staked, how much steth recieved and so on.

const address = await this.core.getWeb3Address();
const isContract = await this.core.isContract(address);

if (isContract) await this.stakeMultisig(props);
else await this.stakeEOA(props);
if (isContract) return await this.stakeMultisig(props);
else return await this.stakeEOA(props);
}

@ErrorHandler("Call:")
@Logger("LOG:")
private async stakeEOA(props: StakeProps): Promise<void> {
private async stakeEOA(props: StakeProps): Promise<StakeResult> {
const { value, callback, referralAddress = zeroAddress } = props;

invariant(this.core.rpcProvider, "RPC provider is not defined");
invariant(this.core.web3Provider, "Web3 provider is not defined");
// Checking the daily protocol staking limit
await this.checkStakeLimit(value);
await this.validateStakeLimit(value);

const { gasLimit, overrides } = await this.submitGasLimit(
value,
referralAddress
);
const address = await this.core.getWeb3Address();

callback(StakeCallbackStage.SIGN);
callback({ stage: StakeCallbackStage.SIGN });

const transaction = await this.getContractStETH().write.submit(
[referralAddress],
Expand All @@ -113,37 +118,49 @@ export class LidoSDKStake {
}
);

callback(StakeCallbackStage.RECEIPT, transaction);
callback({ stage: StakeCallbackStage.RECEIPT, payload: transaction });

const transactionReceipt =
await this.core.rpcProvider.waitForTransactionReceipt({
hash: transaction,
});

callback(StakeCallbackStage.CONFIRMATION, transactionReceipt);
callback({
stage: StakeCallbackStage.CONFIRMATION,
payload: transactionReceipt,
});

const confirmations =
await this.core.rpcProvider.getTransactionConfirmations({
hash: transactionReceipt.transactionHash,
});

callback(StakeCallbackStage.DONE, confirmations);
callback({ stage: StakeCallbackStage.DONE, payload: confirmations });

return { hash: transaction, receipt: transactionReceipt, confirmations };
}

@ErrorHandler("Call:")
@Logger("LOG:")
private async stakeMultisig(props: StakeProps): Promise<void> {
private async stakeMultisig(props: StakeProps): Promise<StakeResult> {
const { value, callback, referralAddress = zeroAddress } = props;

const address = await this.core.getWeb3Address();

await this.getContractStETH().write.submit([referralAddress], {
value: parseEther(value),
chain: this.core.chain,
account: address,
});
callback({ stage: StakeCallbackStage.SIGN });

const transaction = await this.getContractStETH().write.submit(
[referralAddress],
{
value: parseEther(value),
chain: this.core.chain,
account: address,
}
);

callback({ stage: StakeCallbackStage.MULTISIG_DONE });

callback(StakeCallbackStage.MULTISIG_DONE);
return { hash: transaction };
}

// Views
Expand Down Expand Up @@ -201,7 +218,7 @@ export class LidoSDKStake {

@ErrorHandler("Utils:")
@Logger("Utils:")
private async checkStakeLimit(value: string): Promise<void> {
private async validateStakeLimit(value: string): Promise<void> {
const currentStakeLimit = (await this.getStakeLimitInfo())[3];
const parsedValue = parseEther(value);

Expand Down
21 changes: 16 additions & 5 deletions packages/stake/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Address } from "viem";
import { type Address, type Hash, type TransactionReceipt } from "viem";
import {
type LidoSDKCoreProps,
type LidoSDKCore,
Expand All @@ -16,12 +16,23 @@ export enum StakeCallbackStage {
"MULTISIG_DONE" = "multisig_done",
}

export type StageCallback = (
stage: StakeCallbackStage,
payload?: unknown
) => void;
export type StakeCallbackProps =
| { stage: StakeCallbackStage.SIGN; payload?: undefined }
| { stage: StakeCallbackStage.RECEIPT; payload: Hash }
| { stage: StakeCallbackStage.CONFIRMATION; payload: TransactionReceipt }
| { stage: StakeCallbackStage.DONE; payload: bigint }
| { stage: StakeCallbackStage.MULTISIG_DONE; payload?: undefined };

export type StageCallback = (props: StakeCallbackProps) => void;

export type StakeProps = {
value: string;
callback: StageCallback;
referralAddress?: Address;
};

export type StakeResult = {
hash: Hash;
receipt?: TransactionReceipt;
confirmations?: bigint;
};