Skip to content

Commit 0c95997

Browse files
committed
feat(app2): consider L1 data fee for BOB
Signed-off-by: Eric Hegnes <[email protected]>
1 parent 14cb9d4 commit 0c95997

File tree

6 files changed

+123
-18
lines changed

6 files changed

+123
-18
lines changed

app2/src/lib/gasprice/index.ts

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { chainInfoMap } from "$lib/services/cosmos/chain-info/config"
22
import { createViemPublicClient } from "@unionlabs/sdk/evm"
3-
import type { Chain } from "@unionlabs/sdk/schema"
3+
import { type Chain, NumberFromHexString, UniversalChainId } from "@unionlabs/sdk/schema"
44
import {
55
Array as A,
66
BigDecimal,
@@ -11,9 +11,12 @@ import {
1111
Option as O,
1212
pipe,
1313
Record as R,
14+
Schema as S,
1415
unsafeCoerce,
1516
} from "effect"
1617
import { type GetGasPriceErrorType, http } from "viem"
18+
import type * as V from "viem"
19+
import { publicActionsL2 } from "viem/op-stack"
1720
import { GasPriceError } from "./error"
1821
import * as GasPrice from "./service"
1922

@@ -29,14 +32,22 @@ export class GasPriceMap extends LayerMap.Service<GasPriceMap>()("GasPriceByChai
2932
Layer.effect(
3033
GasPrice.GasPrice,
3134
Effect.gen(function*() {
32-
const client = yield* pipe(
33-
chain.toViemChain(),
34-
Effect.flatMap((chain) =>
35-
createViemPublicClient({
36-
chain,
37-
transport: http(),
35+
const viemChain = yield* chain.toViemChain().pipe(
36+
Effect.mapError((cause) =>
37+
new GasPriceError({
38+
module: "Evm",
39+
method: "chain",
40+
description: "could not convert internal chain to viem chain",
41+
cause,
3842
})
3943
),
44+
)
45+
46+
const client = yield* pipe(
47+
createViemPublicClient({
48+
chain: viemChain,
49+
transport: http(),
50+
}),
4051
Effect.mapError((cause) =>
4152
new GasPriceError({
4253
module: "Evm",
@@ -47,6 +58,38 @@ export class GasPriceMap extends LayerMap.Service<GasPriceMap>()("GasPriceByChai
4758
),
4859
)
4960

61+
const additiveFee = yield* pipe(
62+
Match.value(chain.universal_chain_id),
63+
Match.whenOr(
64+
Match.is(UniversalChainId.make("bob.60808")),
65+
Match.is(UniversalChainId.make("bob.808813")),
66+
(id) =>
67+
pipe(
68+
client.extend(publicActionsL2()),
69+
(client) =>
70+
Effect.tryPromise({
71+
try: () =>
72+
client.estimateL1Fee({
73+
// TODO: re-evaluate correctness
74+
account: "0x0000000000000000000000000000000000000000",
75+
chain: undefined,
76+
}),
77+
catch: (cause) =>
78+
new GasPriceError({
79+
module: "Evm",
80+
method: "additiveFee",
81+
description: `Could not calculate L1 fee for ${id}`,
82+
cause,
83+
}),
84+
}),
85+
Effect.map((atomic) => BigDecimal.make(atomic, 0)),
86+
Effect.map(GasPrice.AtomicGasPrice),
87+
),
88+
),
89+
Match.option,
90+
Effect.transposeOption,
91+
)
92+
5093
const of = pipe(
5194
Effect.tryPromise({
5295
try: () => client.getGasPrice(),
@@ -60,10 +103,13 @@ export class GasPriceMap extends LayerMap.Service<GasPriceMap>()("GasPriceByChai
60103
}),
61104
// XXX: take from constants file
62105
Effect.tap((x) =>
63-
Effect.logDebug(`${chain.display_name} gas price (atomic): ${JSON.stringify(x)}`)
106+
Effect.logDebug(
107+
`${chain.display_name} gas price (atomic): ${JSON.stringify(x)}`,
108+
)
64109
),
65110
Effect.map((a) => ({
66111
value: GasPrice.BaseGasPrice(BigDecimal.make(a, 18)),
112+
additiveFee,
67113
decimals: 18,
68114
})),
69115
Effect.tap((x) =>
@@ -115,6 +161,7 @@ export class GasPriceMap extends LayerMap.Service<GasPriceMap>()("GasPriceByChai
115161
GasPrice.BaseGasPrice,
116162
(value) => ({
117163
value,
164+
additiveFee: O.none<GasPrice.AtomicGasPrice>(),
118165
decimals,
119166
}),
120167
)

app2/src/lib/gasprice/service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BigDecimal, Brand, Context, Effect } from "effect"
1+
import { BigDecimal, Brand, Context, Effect, Option as O } from "effect"
22
import type { GasPriceError } from "./error"
33

44
export type AtomicGasPrice = BigDecimal.BigDecimal & Brand.Brand<"AtomicGasPrice">
@@ -30,6 +30,10 @@ export declare namespace GasPrice {
3030
export interface Service {
3131
readonly of: Effect.Effect<{
3232
value: BaseGasPrice
33+
/**
34+
* e.g. L1 fee on BOB
35+
*/
36+
additiveFee: O.Option<AtomicGasPrice>
3337
decimals: number
3438
}, GasPriceError>
3539
}

app2/src/lib/stores/fee.svelte.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,13 @@ const createFeeStore = () => {
7878
source: {
7979
gas: BaseGasPrice
8080
gasDecimals: number
81+
additiveFee: O.Option<AtomicGasPrice>
8182
usd: PriceResult
8283
}
8384
destination: {
8485
gas: BaseGasPrice
8586
gasDecimals: number
87+
additiveFee: O.Option<AtomicGasPrice>
8688
usd: PriceResult
8789
}
8890
},
@@ -130,6 +132,7 @@ const createFeeStore = () => {
130132
return {
131133
gas: gas.value,
132134
gasDecimals: gas.decimals,
135+
additiveFee: gas.additiveFee,
133136
usd,
134137
}
135138
})
@@ -183,7 +186,8 @@ const createFeeStore = () => {
183186

184187
const decorate = (
185188
self: {
186-
gasPrice: { value: BaseGasPrice; decimals: number }
189+
// TODO: consolidate type
190+
gasPrice: { value: BaseGasPrice; additiveFee: O.Option<AtomicGasPrice>; decimals: number }
187191
ratio: BigDecimal.BigDecimal
188192
} & typeof config,
189193
) => {
@@ -244,6 +248,21 @@ const createFeeStore = () => {
244248
return f(self.gasPrice.value)
245249
}
246250

251+
const applyAdditiveFeeK = (
252+
amount: AtomicGasPrice,
253+
): Writer.Writer<readonly string[], AtomicGasPrice> => {
254+
const result = self.gasPrice.additiveFee.pipe(
255+
O.map(BigDecimal.sum(amount)),
256+
O.getOrElse(() => amount),
257+
AtomicGasPrice,
258+
)
259+
260+
return [
261+
result,
262+
[`additive (L1) fee for BOB is ${self.gasPrice.additiveFee.toString()}`],
263+
]
264+
}
265+
247266
const applyRatioK = (a: BaseGasPrice): Writer.Writer<readonly string[], BaseGasPrice> => {
248267
const ratio = BigDecimal.round(self.ratio, { scale: gasDecimals, mode: "from-zero" })
249268
const result = pipe(
@@ -336,6 +355,7 @@ const createFeeStore = () => {
336355
applyGasPriceK,
337356
asBaseUnitK,
338357
asAtomicUnitK,
358+
applyAdditiveFeeK,
339359
applyFeeMultiplierK,
340360
applyRatioK,
341361
applyBatchDivisionK,
@@ -377,12 +397,6 @@ const createFeeStore = () => {
377397
O.map(x => x.source.gas),
378398
))
379399

380-
const destGasUnitPrice = $derived(pipe(
381-
data,
382-
O.flatMap(O.getRight),
383-
O.map(x => x.destination.gas),
384-
))
385-
386400
const decoratedConfig = $derived(pipe(
387401
O.all({
388402
// XXX: source / dest here should be determined on per-transaction basis
@@ -392,6 +406,7 @@ const createFeeStore = () => {
392406
O.map(x => ({
393407
value: x.destination.gas,
394408
decimals: x.destination.gasDecimals,
409+
additiveFee: x.destination.additiveFee,
395410
})),
396411
),
397412
ratio: maybeRatio,
@@ -407,6 +422,7 @@ const createFeeStore = () => {
407422
PACKET_SEND_LC_UPDATE_L1: flow(
408423
pipe( // TODO: extract
409424
config.applyGasPriceK,
425+
composeK(config.applyAdditiveFeeK),
410426
composeK(config.asBaseUnitK),
411427
composeK(config.applyFeeMultiplierK),
412428
composeK(config.applyBatchDivisionK),
@@ -416,6 +432,7 @@ const createFeeStore = () => {
416432
PACKET_SEND_LC_UPDATE_L0: flow(
417433
pipe( // TODO: extract
418434
config.applyGasPriceK,
435+
composeK(config.applyAdditiveFeeK),
419436
composeK(config.asBaseUnitK),
420437
composeK(config.applyFeeMultiplierK),
421438
composeK(config.applyBatchDivisionK),
@@ -425,6 +442,7 @@ const createFeeStore = () => {
425442
PACKET_RECV: flow(
426443
pipe( // TODO: extract
427444
config.applyGasPriceK,
445+
composeK(config.applyAdditiveFeeK),
428446
composeK(config.asBaseUnitK),
429447
composeK(config.applyFeeMultiplierK),
430448
composeK(config.applyRatioK),

app2/src/lib/transfer/shared/components/FeeDetails.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ const calculating = false
156156
<div class="text-sm">
157157
<div class="font-semibold text-white mb-2">{item.label}</div>
158158
<div class="text-zinc-300 mb-4">{item.description}</div>
159+
<!-- <div>{@html item.steps.calc}</div> -->
159160
</div>
160161
{/snippet}
161162
</Tooltip>

ts-sdk/src/evm/client.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Context, Data, Effect } from "effect"
1+
import { Array as A, Context, Data, Effect, pipe, Predicate } from "effect"
22
import {
33
type Account,
44
type Chain,
@@ -62,11 +62,23 @@ export class CreateViemWalletClientError extends Data.TaggedError("CreateViemWal
6262
cause: CreateWalletClientErrorType
6363
}> {}
6464

65+
type Extension = Parameters<ReturnType<typeof createPublicClient>["extend"]>[0]
66+
6567
export const createViemPublicClient = (
6668
parameters: PublicClientConfig,
69+
extensions?: ReadonlyArray<Extension> | Extension | undefined,
6770
): Effect.Effect<PublicClient, CreateViemPublicClientError> =>
6871
Effect.try({
69-
try: () => createPublicClient(parameters),
72+
try: () =>
73+
pipe(
74+
A.ensure(extensions),
75+
A.filter(Predicate.isNotUndefined),
76+
(xs) =>
77+
xs.reduce(
78+
(client, extension) => client.extend(extension as unknown as any),
79+
createPublicClient(parameters),
80+
),
81+
),
7082
catch: err =>
7183
new CreateViemPublicClientError({
7284
cause: extractErrorDetails(err as CreatePublicClientErrorType),

ts-sdk/src/schema/hex.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Effect, ParseResult } from "effect"
2+
import { pipe } from "effect/Function"
13
import * as S from "effect/Schema"
24
import { fromHex, toHex } from "viem"
35

@@ -26,3 +28,24 @@ export const HexFromString = S.transform(S.String, Hex, {
2628
encode: hex => fromHex(hex, "string"),
2729
})
2830
export type HexFromString = typeof HexFromString.Type
31+
32+
/**
33+
* TODO: handle signed hex?
34+
*/
35+
export const NumberFromHexString = S.transformOrFail(
36+
Hex,
37+
S.Positive,
38+
{
39+
decode: (fromA, _, ast) =>
40+
pipe(
41+
Effect.try({
42+
try: () => Number.parseInt(fromA, 16),
43+
catch: (e) => new Error(String(e)),
44+
}),
45+
Effect.mapError((e) => new ParseResult.Type(ast, fromA, e.message)),
46+
),
47+
encode: (toI) => Effect.succeed(`0x${toI.toString(16)}` as const),
48+
strict: true,
49+
},
50+
)
51+
export type NumberFromHexString = typeof NumberFromHexString.Type

0 commit comments

Comments
 (0)