Skip to content

Commit d7ce09c

Browse files
committed
wip override estimateFeesPerGas
1 parent a8c404b commit d7ce09c

4 files changed

Lines changed: 196 additions & 1 deletion

File tree

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import {
2+
Chain,
3+
FeeValuesType,
4+
GetChainParameter,
5+
FeeValuesLegacy,
6+
FeeValuesEIP1559,
7+
Client,
8+
Transport,
9+
Block,
10+
PrepareTransactionRequestParameters,
11+
Account,
12+
ChainFeesFnParameters,
13+
BaseFeeScalarError,
14+
ChainEstimateFeesPerGasFnParameters,
15+
Eip1559FeesNotSupportedError,
16+
hexToBigInt,
17+
EstimateMaxPriorityFeePerGasReturnType,
18+
EstimateMaxPriorityFeePerGasParameters,
19+
PublicActions,
20+
} from "viem";
21+
import { getBlock, getGasPrice } from "viem/actions";
22+
import { getAction } from "viem/utils";
23+
24+
type EstimateFeesPerGasParameters<
25+
chain extends Chain | undefined = Chain | undefined,
26+
chainOverride extends Chain | undefined = Chain | undefined,
27+
type extends FeeValuesType = FeeValuesType,
28+
> = {
29+
/**
30+
* The type of fee values to return.
31+
*
32+
* - `legacy`: Returns the legacy gas price.
33+
* - `eip1559`: Returns the max fee per gas and max priority fee per gas.
34+
*
35+
* @default 'eip1559'
36+
*/
37+
type?: type | FeeValuesType | undefined;
38+
} & GetChainParameter<chain, chainOverride>;
39+
40+
type EstimateFeesPerGasReturnType<type extends FeeValuesType = FeeValuesType> =
41+
| (type extends "legacy" ? FeeValuesLegacy : never)
42+
| (type extends "eip1559" ? FeeValuesEIP1559 : never);
43+
44+
/**
45+
* Returns an estimate for the fees per gas (in wei) for a
46+
* transaction to be likely included in the next block.
47+
* Defaults to [`chain.fees.estimateFeesPerGas`](/docs/clients/chains#fees-estimatefeespergas) if set.
48+
*
49+
* - Docs: https://viem.sh/docs/actions/public/estimateFeesPerGas
50+
*
51+
* @param client - Client to use
52+
* @param parameters - {@link EstimateFeesPerGasParameters}
53+
* @returns An estimate (in wei) for the fees per gas. {@link EstimateFeesPerGasReturnType}
54+
*
55+
* @example
56+
* import { createPublicClient, http } from 'viem'
57+
* import { mainnet } from 'viem/chains'
58+
* import { estimateFeesPerGas } from 'viem/actions'
59+
*
60+
* const client = createPublicClient({
61+
* chain: mainnet,
62+
* transport: http(),
63+
* })
64+
* const maxPriorityFeePerGas = await estimateFeesPerGas(client)
65+
* // { maxFeePerGas: ..., maxPriorityFeePerGas: ... }
66+
*/
67+
export function estimateFeesPerGas<chain extends Chain, account extends Account | undefined>(
68+
client: Client<Transport, chain, account>,
69+
): Pick<PublicActions<Transport, chain, account>, "estimateFeesPerGas"> {
70+
console.log("extending with estimateFeesPerGas");
71+
return { estimateFeesPerGas: (args) => internal_estimateFeesPerGas(client, args as never) };
72+
}
73+
74+
async function internal_estimateFeesPerGas<
75+
chain extends Chain | undefined,
76+
chainOverride extends Chain | undefined,
77+
type extends FeeValuesType = "eip1559",
78+
>(
79+
client: Client<Transport, chain>,
80+
args: EstimateFeesPerGasParameters<chain, chainOverride, type> & {
81+
block?: Block | undefined;
82+
request?: PrepareTransactionRequestParameters<Chain, Account> | undefined;
83+
},
84+
): Promise<EstimateFeesPerGasReturnType<type>> {
85+
console.log("estimateFeesPerGas", client, args);
86+
const { block: block_, chain = client.chain, request, type = "eip1559" } = args || {};
87+
88+
const baseFeeMultiplier = await (async () => {
89+
if (typeof chain?.fees?.baseFeeMultiplier === "function")
90+
return chain.fees.baseFeeMultiplier({
91+
block: block_ as Block,
92+
client,
93+
request,
94+
} as ChainFeesFnParameters);
95+
return chain?.fees?.baseFeeMultiplier ?? 1.2;
96+
})();
97+
if (baseFeeMultiplier < 1) throw new BaseFeeScalarError();
98+
99+
const decimals = baseFeeMultiplier.toString().split(".")[1]?.length ?? 0;
100+
const denominator = 10 ** decimals;
101+
const multiply = (base: bigint) => (base * BigInt(Math.ceil(baseFeeMultiplier * denominator))) / BigInt(denominator);
102+
103+
const block = block_ ? block_ : await getAction(client, getBlock, "getBlock")({});
104+
105+
if (typeof chain?.fees?.estimateFeesPerGas === "function") {
106+
const fees = (await chain.fees.estimateFeesPerGas({
107+
block: block_ as Block,
108+
client,
109+
multiply,
110+
request,
111+
type,
112+
} as ChainEstimateFeesPerGasFnParameters)) as unknown as EstimateFeesPerGasReturnType<type>;
113+
114+
if (fees !== null) return fees;
115+
}
116+
117+
if (type === "eip1559") {
118+
if (typeof block.baseFeePerGas !== "bigint") throw new Eip1559FeesNotSupportedError();
119+
120+
const maxPriorityFeePerGas =
121+
typeof request?.maxPriorityFeePerGas === "bigint"
122+
? request.maxPriorityFeePerGas
123+
: await internal_estimateMaxPriorityFeePerGas(client as Client<Transport, Chain>, {
124+
block: block as Block,
125+
chain,
126+
request,
127+
});
128+
129+
const baseFeePerGas = multiply(block.baseFeePerGas);
130+
const maxFeePerGas = request?.maxFeePerGas ?? baseFeePerGas + maxPriorityFeePerGas;
131+
132+
return {
133+
maxFeePerGas,
134+
maxPriorityFeePerGas,
135+
} as EstimateFeesPerGasReturnType<type>;
136+
}
137+
138+
const gasPrice = request?.gasPrice ?? multiply(await getAction(client, getGasPrice, "getGasPrice")({}));
139+
return {
140+
gasPrice,
141+
} as EstimateFeesPerGasReturnType<type>;
142+
}
143+
144+
async function internal_estimateMaxPriorityFeePerGas<
145+
chain extends Chain | undefined,
146+
chainOverride extends Chain | undefined,
147+
>(
148+
client: Client<Transport, chain>,
149+
args: EstimateMaxPriorityFeePerGasParameters<chain, chainOverride> & {
150+
block?: Block | undefined;
151+
request?: PrepareTransactionRequestParameters<chain, Account | undefined, chainOverride> | undefined;
152+
},
153+
): Promise<EstimateMaxPriorityFeePerGasReturnType> {
154+
const { block: block_, chain = client.chain, request } = args || {};
155+
156+
try {
157+
const maxPriorityFeePerGas = chain?.fees?.maxPriorityFeePerGas ?? chain?.fees?.defaultPriorityFee;
158+
159+
if (typeof maxPriorityFeePerGas === "function") {
160+
const block = block_ || (await getAction(client, getBlock, "getBlock")({}));
161+
const maxPriorityFeePerGas_ = await maxPriorityFeePerGas({
162+
block,
163+
client,
164+
request,
165+
} as ChainFeesFnParameters);
166+
if (maxPriorityFeePerGas_ === null) throw new Error();
167+
return maxPriorityFeePerGas_;
168+
}
169+
170+
if (typeof maxPriorityFeePerGas !== "undefined") return maxPriorityFeePerGas;
171+
172+
const maxPriorityFeePerGasHex = await client.request({
173+
method: "eth_maxPriorityFeePerGas",
174+
});
175+
return hexToBigInt(maxPriorityFeePerGasHex);
176+
} catch {
177+
// If the RPC Provider does not support `eth_maxPriorityFeePerGas`
178+
// fall back to calculating it manually via `gasPrice - baseFeePerGas`.
179+
// See: https://github.com/ethereum/pm/issues/328#:~:text=eth_maxPriorityFeePerGas%20after%20London%20will%20effectively%20return%20eth_gasPrice%20%2D%20baseFee
180+
const [block, gasPrice] = await Promise.all([
181+
block_ ? Promise.resolve(block_) : getAction(client, getBlock, "getBlock")({}),
182+
getAction(client, getGasPrice, "getGasPrice")({}),
183+
]);
184+
185+
if (typeof block.baseFeePerGas !== "bigint") throw new Eip1559FeesNotSupportedError();
186+
187+
const maxPriorityFeePerGas = gasPrice - block.baseFeePerGas;
188+
189+
if (maxPriorityFeePerGas < 0n) return 0n;
190+
return maxPriorityFeePerGas;
191+
}
192+
}

packages/entrykit/src/getSessionClient.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { createBundlerClient } from "./createBundlerClient";
55
import { SessionClient } from "./common";
66
import { SmartAccount } from "viem/account-abstraction";
77
import { getBundlerTransport } from "./getBundlerTransport";
8+
import { estimateFeesPerGas } from "./actions/estimateFeesPerGas";
89

910
export async function getSessionClient({
1011
userAddress,
@@ -30,6 +31,7 @@ export async function getSessionClient({
3031

3132
const sessionClient = bundlerClient
3233
.extend(smartAccountActions)
34+
.extend(estimateFeesPerGas)
3335
.extend(
3436
callFrom({
3537
worldAddress,
@@ -44,7 +46,6 @@ export async function getSessionClient({
4446
publicClient: client,
4547
}),
4648
)
47-
4849
// TODO: add observer once we conditionally fetch receipts while bridge is open
4950
.extend(() => ({ userAddress, worldAddress, internal_signer: sessionSigner }));
5051

packages/world/ts/actions/callFrom.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export function callFrom(
3939
) => Pick<WalletActions<chain, account>, "writeContract"> {
4040
return (client) => ({
4141
async writeContract(writeArgs) {
42+
console.log("call from", client, writeArgs);
4243
const _writeContract = getAction(client, viem_writeContract, "writeContract");
4344

4445
// Skip if the contract isn't the World or the function called should not be redirected through `callFrom`.

packages/world/ts/actions/sendUserOperationFrom.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export function sendUserOperationFrom(
4040
) => Pick<BundlerActions<account>, "sendUserOperation"> {
4141
return (client) => ({
4242
async sendUserOperation(args) {
43+
console.log("sendUserOperationFrom", client, args);
4344
const _sendUserOperation = getAction(client, viem_sendUserOperation, "sendUserOperation");
4445

4546
if (args.callData) {

0 commit comments

Comments
 (0)