@guru-fund/sdk 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { ContractTransactionReceipt, Interface } from 'ethers';
2
2
  import { TypedContractEvent } from '../typechain/common';
3
- type TypechainOutputObject<T> = T extends TypedContractEvent<infer _U, infer _W, infer V> ? V : never;
3
+ type TypechainOutputObject<T> = T extends TypedContractEvent<infer _U, infer _W, infer V> ? keyof V extends never ? any : V : any;
4
4
  export default class ReceiptParser {
5
5
  private receipt;
6
6
  constructor(receipt: ContractTransactionReceipt);
@@ -37,5 +37,12 @@ export interface QuoteDepositResult {
37
37
  txData: TransactionRequest;
38
38
  decodeLogs: (logs: SimulationLog[]) => QuoteDepositLogs | null;
39
39
  }
40
+ type DepositAllocationQuote = {
41
+ asset: Fund.Asset;
42
+ amountIn: bigint;
43
+ amountOut: bigint;
44
+ };
40
45
  export declare function allocateDepositInputAmounts(assets: Fund.Asset[], totalAmount: bigint, totalValueLocked: bigint): Map<string, bigint>;
46
+ export declare function rebalanceDepositInputAmounts(quotes: DepositAllocationQuote[], totalAmount: bigint): Map<string, bigint>;
41
47
  export default function quoteDeposit(params: QuoteDepositParams, ctx: QuoteDepositContext): Promise<QuoteDepositResult>;
48
+ export {};
@@ -8,28 +8,30 @@ import { quoteDepositSchema } from '../schemas/quoteDeposit.js';
8
8
  import buildDepositTx from '../txBuilders/buildDepositTx.js';
9
9
  import { FundLedger__factory } from '../typechain/index.js';
10
10
  const MAX_UINT256 = (1n << 256n) - 1n;
11
+ const MIN_ROUTE_INPUT_AMOUNT = 2n;
12
+ const ROUTE_AWARE_ALLOCATION_PASSES = 1;
11
13
  const minBigint = (a, b) => (a < b ? a : b);
12
14
  const slippageE2For = (settings, token) => {
13
15
  const slippage = settings?.[token.toLowerCase()];
14
16
  return slippage == null ? undefined : Number(BigInt(slippage) / 10n);
15
17
  };
16
18
  const assetUsd1e18Value = (asset) => (asset.usd1e18Price * asset.balance) / Token.unitFor(asset.token.decimals);
17
- export function allocateDepositInputAmounts(assets, totalAmount, totalValueLocked) {
19
+ function allocateWeightedInputAmounts(items, totalAmount) {
18
20
  const allocations = new Map();
19
- if (totalAmount === 0n || totalValueLocked === 0n) {
20
- for (const asset of assets) {
21
- allocations.set(asset.token.address.toLowerCase(), 0n);
22
- }
21
+ if (totalAmount === 0n) {
22
+ for (const item of items)
23
+ allocations.set(item.key, 0n);
23
24
  return allocations;
24
25
  }
25
- const weighted = assets.map((asset) => {
26
- const usdValue = assetUsd1e18Value(asset);
27
- const weightedAmount = totalAmount * usdValue;
28
- const amount = weightedAmount / totalValueLocked;
26
+ const weighted = items.map((item) => {
27
+ const weightedAmount = totalAmount * item.weight;
28
+ const amount = item.denominator === 0n ? 0n : weightedAmount / item.denominator;
29
29
  return {
30
- asset,
30
+ ...item,
31
31
  amount,
32
- remainder: weightedAmount % totalValueLocked,
32
+ remainder: item.denominator === 0n
33
+ ? 0n
34
+ : weightedAmount % item.denominator,
33
35
  };
34
36
  });
35
37
  const allocated = weighted.reduce((sum, item) => sum + item.amount, 0n);
@@ -38,11 +40,116 @@ export function allocateDepositInputAmounts(assets, totalAmount, totalValueLocke
38
40
  .sort((a, b) => a.remainder === b.remainder ? 0 : a.remainder > b.remainder ? -1 : 1)
39
41
  .forEach((item) => {
40
42
  const extra = remainder > 0n ? 1n : 0n;
41
- allocations.set(item.asset.token.address.toLowerCase(), item.amount + extra);
43
+ allocations.set(item.key, item.amount + extra);
42
44
  remainder -= extra;
43
45
  });
46
+ const zeroRoundedAssets = weighted
47
+ .filter((item) => item.balance > 0n && (allocations.get(item.key) ?? 0n) === 0n)
48
+ .sort((a, b) => a.remainder === b.remainder ? 0 : a.remainder > b.remainder ? -1 : 1);
49
+ for (const item of zeroRoundedAssets) {
50
+ const donor = weighted
51
+ .filter((candidate) => candidate.key !== item.key)
52
+ .sort((a, b) => {
53
+ const aAmount = allocations.get(a.key) ?? 0n;
54
+ const bAmount = allocations.get(b.key) ?? 0n;
55
+ return aAmount === bAmount ? 0 : aAmount > bAmount ? -1 : 1;
56
+ })
57
+ .find((candidate) => (allocations.get(candidate.key) ?? 0n) >
58
+ MIN_ROUTE_INPUT_AMOUNT);
59
+ if (!donor)
60
+ break;
61
+ allocations.set(donor.key, (allocations.get(donor.key) ?? 0n) - MIN_ROUTE_INPUT_AMOUNT);
62
+ allocations.set(item.key, MIN_ROUTE_INPUT_AMOUNT);
63
+ }
44
64
  return allocations;
45
65
  }
66
+ export function allocateDepositInputAmounts(assets, totalAmount, totalValueLocked) {
67
+ if (totalAmount === 0n || totalValueLocked === 0n) {
68
+ const allocations = new Map();
69
+ for (const asset of assets) {
70
+ allocations.set(asset.token.address.toLowerCase(), 0n);
71
+ }
72
+ return allocations;
73
+ }
74
+ return allocateWeightedInputAmounts(assets.map((asset) => ({
75
+ key: asset.token.address.toLowerCase(),
76
+ weight: assetUsd1e18Value(asset),
77
+ denominator: totalValueLocked,
78
+ balance: asset.balance,
79
+ })), totalAmount);
80
+ }
81
+ export function rebalanceDepositInputAmounts(quotes, totalAmount) {
82
+ let denominator = 0n;
83
+ const weights = quotes.map(({ asset, amountIn, amountOut }) => {
84
+ let weight = 0n;
85
+ if (asset.balance > 0n && amountIn > 0n && amountOut > 0n) {
86
+ const inputRatio = (amountOut * UNIT) / asset.balance;
87
+ if (inputRatio > 0n) {
88
+ weight = (amountIn * UNIT) / inputRatio;
89
+ }
90
+ }
91
+ denominator += weight;
92
+ return {
93
+ key: asset.token.address.toLowerCase(),
94
+ weight,
95
+ denominator: 0n,
96
+ balance: asset.balance,
97
+ };
98
+ });
99
+ if (denominator === 0n) {
100
+ return new Map(quotes.map(({ asset, amountIn }) => [
101
+ asset.token.address.toLowerCase(),
102
+ amountIn,
103
+ ]));
104
+ }
105
+ return allocateWeightedInputAmounts(weights.map((item) => ({ ...item, denominator })), totalAmount);
106
+ }
107
+ function allocationsEqual(a, b) {
108
+ if (a.size !== b.size)
109
+ return false;
110
+ for (const [key, value] of a) {
111
+ if (b.get(key) !== value)
112
+ return false;
113
+ }
114
+ return true;
115
+ }
116
+ async function quoteDepositAllocations({ inputAllocations, coinData, fundData, parsed, ctx, vault, }) {
117
+ const allocationQuotes = [];
118
+ if (coinData) {
119
+ const amountIn = inputAllocations.get(coinData.token.address.toLowerCase()) ?? 0n;
120
+ if (coinData.balance > 0n && amountIn === 0n) {
121
+ throw new Error(`Deposit amount is too small to allocate input for asset ${coinData.token.address}`);
122
+ }
123
+ allocationQuotes.push({
124
+ asset: coinData,
125
+ amountIn,
126
+ amountOut: amountIn,
127
+ });
128
+ }
129
+ const assetsToSwap = fundData.assets.filter((asset) => !compareAddresses(asset.token.address, parsed.coin));
130
+ const routeResults = await Promise.all(assetsToSwap.map(async (asset) => {
131
+ const amountIn = inputAllocations.get(asset.token.address.toLowerCase()) ?? 0n;
132
+ if (asset.balance > 0n && amountIn === 0n) {
133
+ throw new Error(`Deposit amount is too small to allocate input for asset ${asset.token.address}`);
134
+ }
135
+ const route = await getRouteIn({
136
+ chainId: ctx.chainId,
137
+ tokenIn: parsed.coin,
138
+ tokenOut: asset.token.address,
139
+ amountIn,
140
+ account: parsed.account,
141
+ vault,
142
+ slippageE2: slippageE2For(parsed.slippageSettings, asset.token.address),
143
+ }, ctx);
144
+ allocationQuotes.push({
145
+ asset,
146
+ amountIn,
147
+ amountOut: BigInt(route.data.amountToReceive),
148
+ });
149
+ return { asset, amountIn, route };
150
+ }));
151
+ return { allocationQuotes, routeResults };
152
+ }
46
153
  export default async function quoteDeposit(params, ctx) {
47
154
  const parsed = quoteDepositSchema.parse(params);
48
155
  if (parsed.referrerFeeBps < 0n || parsed.referrerFeeBps > MAX_BPS) {
@@ -65,32 +172,36 @@ export default async function quoteDeposit(params, ctx) {
65
172
  let fees = (originalAmount * referrerFeeBps) / MAX_BPS;
66
173
  const adjustedAmount = originalAmount - fees;
67
174
  const coinData = fundData.assets.find((asset) => compareAddresses(asset.token.address, parsed.coin));
68
- const inputAllocations = allocateDepositInputAmounts(fundData.assets, adjustedAmount, fundData.totalValueLocked);
69
- const coinInputAmount = coinData
70
- ? (inputAllocations.get(coinData.token.address.toLowerCase()) ?? 0n)
71
- : 0n;
72
- if (coinData && coinData.balance > 0n && coinInputAmount === 0n) {
73
- throw new Error(`Deposit amount is too small to allocate input for asset ${coinData.token.address}`);
74
- }
75
- let lowestInputRatio = coinData && coinData.balance > 0n
76
- ? (coinInputAmount * UNIT) / coinData.balance
77
- : MAX_UINT256;
78
- const assetsToSwap = fundData.assets.filter((asset) => !compareAddresses(asset.token.address, parsed.coin));
79
- const routeResults = await Promise.all(assetsToSwap.map(async (asset) => {
80
- const amountIn = inputAllocations.get(asset.token.address.toLowerCase()) ?? 0n;
81
- if (asset.balance > 0n && amountIn === 0n) {
82
- throw new Error(`Deposit amount is too small to allocate input for asset ${asset.token.address}`);
83
- }
84
- const route = await getRouteIn({
85
- chainId: ctx.chainId,
86
- tokenIn: parsed.coin,
87
- tokenOut: asset.token.address,
88
- amountIn,
89
- account: parsed.account,
175
+ let inputAllocations = allocateDepositInputAmounts(fundData.assets, adjustedAmount, fundData.totalValueLocked);
176
+ let quoted = await quoteDepositAllocations({
177
+ inputAllocations,
178
+ coinData,
179
+ fundData,
180
+ parsed,
181
+ ctx,
182
+ vault,
183
+ });
184
+ for (let i = 0; i < ROUTE_AWARE_ALLOCATION_PASSES; i++) {
185
+ const rebalanced = rebalanceDepositInputAmounts(quoted.allocationQuotes, adjustedAmount);
186
+ if (allocationsEqual(inputAllocations, rebalanced))
187
+ break;
188
+ inputAllocations = rebalanced;
189
+ quoted = await quoteDepositAllocations({
190
+ inputAllocations,
191
+ coinData,
192
+ fundData,
193
+ parsed,
194
+ ctx,
90
195
  vault,
91
- }, ctx);
92
- return { asset, route };
93
- }));
196
+ });
197
+ }
198
+ let lowestInputRatio = MAX_UINT256;
199
+ for (const { asset, amountOut } of quoted.allocationQuotes) {
200
+ if (asset.balance === 0n)
201
+ continue;
202
+ lowestInputRatio = minBigint(lowestInputRatio, (amountOut * UNIT) / asset.balance);
203
+ }
204
+ const routeResults = quoted.routeResults;
94
205
  const coinToken = await new Token(parsed.coin, provider).metadata();
95
206
  const coinUsd1e18Price = await ctx.getPriceUsd1e18(parsed.coin);
96
207
  const externalTokenMetadata = new Map();
@@ -142,7 +253,6 @@ export default async function quoteDeposit(params, ctx) {
142
253
  callData: route.callData,
143
254
  });
144
255
  fees += await tollToCoinUnits(route.toll.amount, route.toll.currency);
145
- lowestInputRatio = minBigint(lowestInputRatio, (BigInt(route.data.amountToReceive) * UNIT) / asset.balance);
146
256
  const bps = route.effectiveSlippageBps;
147
257
  if (bps != null) {
148
258
  const bpsN = BigInt(bps);
@@ -15,6 +15,7 @@ export declare function connectV4Quoter(address: string, provider: Provider): V4
15
15
  export declare function toAdapterPathKeys(path: V4Path): SwapV4Struct['path'];
16
16
  type V4QuoteSource = 'quoter' | 'adapter-preview';
17
17
  export interface ApplyV4TollParams {
18
+ tollIn: boolean;
18
19
  baseToll: {
19
20
  currency: string;
20
21
  amount: bigint;
@@ -32,7 +33,7 @@ export interface ApplyV4TollResult {
32
33
  amountToSend: bigint;
33
34
  initialTollAmount: bigint;
34
35
  }
35
- export declare function applyV4Toll({ baseToll, quotedAmountToReceive, quoteSource, swapAmountIn, }: ApplyV4TollParams): ApplyV4TollResult;
36
+ export declare function applyV4Toll({ tollIn, baseToll, quotedAmountToReceive, quoteSource, swapAmountIn, }: ApplyV4TollParams): ApplyV4TollResult;
36
37
  export declare function toHookAdapterPathKeys(path: V4Path): V4HookPathKey[];
37
38
  export interface GetUniswapV4RouteContext {
38
39
  provider: Provider;
@@ -45,15 +45,17 @@ function isV4HookLockedError(reason) {
45
45
  }
46
46
  return false;
47
47
  }
48
- export function applyV4Toll({ baseToll, quotedAmountToReceive, quoteSource, swapAmountIn, }) {
48
+ export function applyV4Toll({ tollIn, baseToll, quotedAmountToReceive, quoteSource, swapAmountIn, }) {
49
49
  const toll = { ...baseToll };
50
50
  let amountQuoted = quotedAmountToReceive;
51
51
  let amountToSend = swapAmountIn;
52
52
  let initialTollAmount = toll.amount;
53
53
  if (toll.amount === 0n) {
54
54
  if (quoteSource === 'quoter') {
55
- toll.amount = quotedAmountToReceive / TOLL_DIVISOR_20BPS;
56
- amountQuoted -= toll.amount;
55
+ if (!tollIn) {
56
+ toll.amount = quotedAmountToReceive / TOLL_DIVISOR_20BPS;
57
+ amountQuoted -= toll.amount;
58
+ }
57
59
  }
58
60
  initialTollAmount = 0n;
59
61
  }
@@ -194,7 +196,7 @@ export default async function getUniswapV4Route(params, ctx) {
194
196
  throw new Error(`UniswapV4 not supported on chainId ${chainId}`);
195
197
  }
196
198
  const maxSlippageE3 = slippageE2 != null ? BigInt(slippageE2) * 10n : undefined;
197
- const { baseToll, swapAmountIn } = resolveBaseToll(addresses, tokenIn, tokenOut, amountIn);
199
+ const { tollIn, baseToll, swapAmountIn } = resolveBaseToll(addresses, tokenIn, tokenOut, amountIn);
198
200
  const candidates = await discoverV4Paths(chainId, tokenIn, tokenOut, provider);
199
201
  if (candidates.length === 0) {
200
202
  throw new Error('NO_UNISWAP_V4_ROUTES_FOUND');
@@ -254,6 +256,7 @@ export default async function getUniswapV4Route(params, ctx) {
254
256
  const selectedAdapter = bestQuote.adapter ?? adapter;
255
257
  const bestPath = bestQuote.path;
256
258
  const { toll, amountQuoted, amountToSend, initialTollAmount } = applyV4Toll({
259
+ tollIn,
257
260
  baseToll,
258
261
  quotedAmountToReceive: bestQuote.amountToReceive,
259
262
  quoteSource: bestQuote.source,
@@ -296,6 +299,7 @@ export default async function getUniswapV4Route(params, ctx) {
296
299
  amountToSend,
297
300
  amountQuoted,
298
301
  initialTollAmount,
302
+ outputTollE3: tollIn ? 0n : undefined,
299
303
  maxSlippageE3,
300
304
  });
301
305
  return {
@@ -20,7 +20,7 @@ export default async function getVeloraRoute(params, ctx) {
20
20
  tokenOutDecimals ?? fetchDecimals(tokenOut, provider),
21
21
  FundVault__factory.connect(vault, provider).controller(),
22
22
  ]);
23
- const { baseToll, swapAmountIn } = resolveBaseToll(addresses, tokenIn, tokenOut, amountIn);
23
+ const { tollIn, baseToll, swapAmountIn } = resolveBaseToll(addresses, tokenIn, tokenOut, amountIn);
24
24
  const routes = (await Promise.all(SUPPORTED_DEXS.map(async (dex) => {
25
25
  try {
26
26
  if (!isDexConfigured(addresses, dex))
@@ -47,6 +47,7 @@ export default async function getVeloraRoute(params, ctx) {
47
47
  dex,
48
48
  cachedPath,
49
49
  amountIn: swapAmountIn,
50
+ tollIn,
50
51
  toll,
51
52
  vault,
52
53
  controller,
@@ -496,6 +496,7 @@ export default class PoolHelper {
496
496
  amountToSend: inputAmount,
497
497
  amountQuoted: lastAmount(amounts, exchangeFactory),
498
498
  initialTollAmount: swapFee,
499
+ outputTollE3: 0n,
499
500
  slippage,
500
501
  buildSwap: (amountToReceive) => {
501
502
  const data = {
@@ -527,6 +528,7 @@ export default class PoolHelper {
527
528
  amountToSend: inputAmount,
528
529
  amountQuoted: lastAmount(amounts, exchangeFactory),
529
530
  initialTollAmount: swapFee,
531
+ outputTollE3: 0n,
530
532
  slippage,
531
533
  buildSwap: (amountToReceive) => {
532
534
  const data = {
@@ -590,6 +592,7 @@ export default class PoolHelper {
590
592
  amountToSend: inputAmount,
591
593
  amountQuoted: amountOut,
592
594
  initialTollAmount: swapFee,
595
+ outputTollE3: 0n,
593
596
  slippage,
594
597
  buildSwap: (amountToReceive) => {
595
598
  const data = {
@@ -30,6 +30,7 @@ export interface GetRouteFromPathParams {
30
30
  dex: SupportedDex;
31
31
  cachedPath: CachedPath;
32
32
  amountIn: bigint;
33
+ tollIn: boolean;
33
34
  toll: {
34
35
  currency: string;
35
36
  amount: bigint;
@@ -40,5 +41,5 @@ export interface GetRouteFromPathParams {
40
41
  maxSlippageE3?: bigint;
41
42
  prefixTxs?: PrefixTx[];
42
43
  }
43
- export declare function getRouteFromPath({ chainId, addresses, provider, simulator, dex, cachedPath, amountIn, toll, vault, controller, account, maxSlippageE3, prefixTxs, }: GetRouteFromPathParams): Promise<Route>;
44
+ export declare function getRouteFromPath({ chainId, addresses, provider, simulator, dex, cachedPath, amountIn, tollIn, toll, vault, controller, account, maxSlippageE3, prefixTxs, }: GetRouteFromPathParams): Promise<Route>;
44
45
  export {};
@@ -83,7 +83,7 @@ function encodeV3Path(path) {
83
83
  }
84
84
  return solidityPacked(pathEncoding, pathToEncode);
85
85
  }
86
- export async function getRouteFromPath({ chainId, addresses, provider, simulator, dex, cachedPath, amountIn, toll, vault, controller, account, maxSlippageE3, prefixTxs, }) {
86
+ export async function getRouteFromPath({ chainId, addresses, provider, simulator, dex, cachedPath, amountIn, tollIn, toll, vault, controller, account, maxSlippageE3, prefixTxs, }) {
87
87
  if (!cachedPath) {
88
88
  throw new Error('No cached path');
89
89
  }
@@ -107,7 +107,7 @@ export async function getRouteFromPath({ chainId, addresses, provider, simulator
107
107
  ]);
108
108
  let amountQuoted = grossAmountToReceive;
109
109
  let amountToSend = amountIn;
110
- if (toll.amount === 0n) {
110
+ if (!tollIn && toll.amount === 0n) {
111
111
  toll.amount = grossAmountToReceive / TOLL_DIVISOR_20BPS;
112
112
  amountQuoted -= toll.amount;
113
113
  }
@@ -140,6 +140,7 @@ export async function getRouteFromPath({ chainId, addresses, provider, simulator
140
140
  amountToSend,
141
141
  amountQuoted,
142
142
  initialTollAmount: toll.amount,
143
+ outputTollE3: tollIn ? 0n : undefined,
143
144
  maxSlippageE3,
144
145
  });
145
146
  return {
@@ -175,7 +176,7 @@ export async function getRouteFromPath({ chainId, addresses, provider, simulator
175
176
  ]);
176
177
  let amountQuoted = grossAmountToReceive;
177
178
  let amountToSend = amountIn;
178
- if (toll.amount === 0n) {
179
+ if (!tollIn && toll.amount === 0n) {
179
180
  toll.amount = grossAmountToReceive / TOLL_DIVISOR_20BPS;
180
181
  amountQuoted -= toll.amount;
181
182
  }
@@ -208,6 +209,7 @@ export async function getRouteFromPath({ chainId, addresses, provider, simulator
208
209
  amountToSend,
209
210
  amountQuoted,
210
211
  initialTollAmount: toll.amount,
212
+ outputTollE3: tollIn ? 0n : undefined,
211
213
  maxSlippageE3,
212
214
  });
213
215
  return {
@@ -271,7 +273,7 @@ export async function getRouteFromPath({ chainId, addresses, provider, simulator
271
273
  }
272
274
  let amountQuoted = grossAmountToReceive;
273
275
  let amountToSend = amountIn;
274
- if (toll.amount === 0n) {
276
+ if (!tollIn && toll.amount === 0n) {
275
277
  toll.amount = grossAmountToReceive / TOLL_DIVISOR_20BPS;
276
278
  amountQuoted -= toll.amount;
277
279
  }
@@ -307,6 +309,7 @@ export async function getRouteFromPath({ chainId, addresses, provider, simulator
307
309
  amountToSend,
308
310
  amountQuoted,
309
311
  initialTollAmount: toll.amount,
312
+ outputTollE3: tollIn ? 0n : undefined,
310
313
  maxSlippageE3,
311
314
  });
312
315
  return {
@@ -345,7 +348,7 @@ export async function getRouteFromPath({ chainId, addresses, provider, simulator
345
348
  ]);
346
349
  let amountQuoted = grossAmountToReceive;
347
350
  let amountToSend = amountIn;
348
- if (toll.amount === 0n) {
351
+ if (!tollIn && toll.amount === 0n) {
349
352
  toll.amount = grossAmountToReceive / TOLL_DIVISOR_20BPS;
350
353
  amountQuoted -= toll.amount;
351
354
  }
@@ -379,6 +382,7 @@ export async function getRouteFromPath({ chainId, addresses, provider, simulator
379
382
  amountToSend,
380
383
  amountQuoted,
381
384
  initialTollAmount: toll.amount,
385
+ outputTollE3: tollIn ? 0n : undefined,
382
386
  maxSlippageE3,
383
387
  });
384
388
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guru-fund/sdk",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Guru Protocol SDK — quote + transaction builders for on-chain managed funds.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -53,7 +53,7 @@
53
53
  "clean": "rm -rf dist .turbo tsconfig.build.tsbuildinfo",
54
54
  "typecheck": "tsc --noEmit",
55
55
  "test": "node --import tsx --test test/*.test.ts",
56
- "prepack": "pnpm run build"
56
+ "prepack": "npm run build"
57
57
  },
58
58
  "dependencies": {
59
59
  "ethers": "=6.15.0",