Skip to content

Commit cedd984

Browse files
committed
feat: make fee margins configurable
1 parent dd34cdb commit cedd984

File tree

8 files changed

+84
-21
lines changed

8 files changed

+84
-21
lines changed

__tests__/rpcProvider.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Block,
66
CallData,
77
Contract,
8+
FeeEstimate,
89
RPC,
910
RPC06,
1011
RpcProvider,
@@ -79,6 +80,33 @@ describeIfRpc('RPCProvider', () => {
7980
expect(typeof spec).toBe('string');
8081
});
8182

83+
test('configurable margin', async () => {
84+
const p = new RpcProvider({
85+
nodeUrl: provider.channel.nodeUrl,
86+
feeMarginPercentage: {
87+
l1BoundMaxAmount: 0,
88+
l1BoundMaxPricePerUnit: 0,
89+
maxFee: 0,
90+
},
91+
});
92+
const estimateSpy = jest.spyOn(p.channel as any, 'getEstimateFee');
93+
const mockFeeEstimate: FeeEstimate = {
94+
gas_consumed: '0x2',
95+
gas_price: '0x1',
96+
data_gas_consumed: '0x2',
97+
data_gas_price: '0x1',
98+
overall_fee: '0x4',
99+
unit: 'WEI',
100+
};
101+
estimateSpy.mockResolvedValue([mockFeeEstimate]);
102+
const result = (await p.getEstimateFeeBulk([{} as any], {}))[0];
103+
expect(estimateSpy).toHaveBeenCalledTimes(1);
104+
expect(result.suggestedMaxFee).toBe(4n);
105+
expect(result.resourceBounds.l1_gas.max_amount).toBe('0x4');
106+
expect(result.resourceBounds.l1_gas.max_price_per_unit).toBe('0x1');
107+
estimateSpy.mockRestore();
108+
});
109+
82110
describe('Test Estimate message fee', () => {
83111
const L1_ADDRESS = '0x8359E4B0152ed5A731162D3c7B0D8D56edB165A0';
84112
let l1l2ContractAddress: string;

__tests__/utils/stark.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ describe('stark', () => {
8787
};
8888
expect(stark.estimateFeeToBounds(estimateFeeResponse)).toStrictEqual({
8989
l2_gas: { max_amount: '0x0', max_price_per_unit: '0x0' },
90-
l1_gas: { max_amount: '0x6e', max_price_per_unit: '0xf' },
90+
l1_gas: { max_amount: '0x96', max_price_per_unit: '0xf' },
9191
});
9292
expect(stark.estimateFeeToBounds(estimateFeeResponse07)).toStrictEqual({
9393
l2_gas: { max_amount: '0x0', max_price_per_unit: '0x0' },
94-
l1_gas: { max_amount: '0xdc', max_price_per_unit: '0xf' },
94+
l1_gas: { max_amount: '0x12c', max_price_per_unit: '0xf' },
9595
});
9696
});
9797

__tests__/utils/utils.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ describe('computeHashOnElements()', () => {
7070
});
7171
describe('estimatedFeeToMaxFee()', () => {
7272
test('should return maxFee for 0', () => {
73-
const res = stark.estimatedFeeToMaxFee(0, 0.15);
73+
const res = stark.estimatedFeeToMaxFee(0, 15);
7474
expect(res).toBe(0n);
7575
});
7676
test('should return maxFee for 10_000', () => {
77-
const res = stark.estimatedFeeToMaxFee(10_000, 0.15);
77+
const res = stark.estimatedFeeToMaxFee(10_000, 15);
7878
expect(res).toBe(11500n);
7979
});
8080
});

src/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ export enum TransactionHashPrefix {
4848
L1_HANDLER = '0x6c315f68616e646c6572', // encodeShortString('l1_handler'),
4949
}
5050

51+
export const enum feeMarginPercentage {
52+
L1_BOUND_MAX_AMOUNT = 50,
53+
L1_BOUND_MAX_PRICE_PER_UNIT = 50,
54+
MAX_FEE = 50,
55+
}
56+
5157
export const UDC = {
5258
ADDRESS: '0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf',
5359
ENTRYPOINT: 'deployContract',

src/provider/rpc.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,17 @@ import { isSierra } from '../utils/contract';
3131
import { RPCResponseParser } from '../utils/responseParser/rpc';
3232

3333
export class RpcProvider implements ProviderInterface {
34-
private responseParser = new RPCResponseParser();
34+
private responseParser: RPCResponseParser;
3535

3636
public channel: RPC07.RpcChannel | RPC06.RpcChannel;
3737

3838
constructor(optionsOrProvider?: RpcProviderOptions | ProviderInterface | RpcProvider) {
3939
if (optionsOrProvider && 'channel' in optionsOrProvider) {
4040
this.channel = optionsOrProvider.channel;
41+
this.responseParser = (optionsOrProvider as any).responseParser;
4142
} else {
4243
this.channel = new RpcChannel({ ...optionsOrProvider, waitMode: false });
44+
this.responseParser = new RPCResponseParser(optionsOrProvider?.feeMarginPercentage);
4345
}
4446
}
4547

@@ -176,7 +178,7 @@ export class RpcProvider implements ProviderInterface {
176178
// can't be named simulateTransaction because of argument conflict with account
177179
return this.channel
178180
.simulateTransaction(invocations, options)
179-
.then(this.responseParser.parseSimulateTransactionResponse);
181+
.then((r) => this.responseParser.parseSimulateTransactionResponse(r));
180182
}
181183

182184
public async waitForTransaction(txHash: BigNumberish, options?: waitForTransactionOptions) {
@@ -278,7 +280,7 @@ export class RpcProvider implements ProviderInterface {
278280
],
279281
{ blockIdentifier, skipValidate }
280282
)
281-
.then(this.responseParser.parseFeeEstimateResponse);
283+
.then((r) => this.responseParser.parseFeeEstimateResponse(r));
282284
}
283285

284286
public async getDeclareEstimateFee(
@@ -298,7 +300,7 @@ export class RpcProvider implements ProviderInterface {
298300
],
299301
{ blockIdentifier, skipValidate }
300302
)
301-
.then(this.responseParser.parseFeeEstimateResponse);
303+
.then((r) => this.responseParser.parseFeeEstimateResponse(r));
302304
}
303305

304306
public async getDeployAccountEstimateFee(
@@ -318,7 +320,7 @@ export class RpcProvider implements ProviderInterface {
318320
],
319321
{ blockIdentifier, skipValidate }
320322
)
321-
.then(this.responseParser.parseFeeEstimateResponse);
323+
.then((r) => this.responseParser.parseFeeEstimateResponse(r));
322324
}
323325

324326
public async getEstimateFeeBulk(
@@ -327,7 +329,7 @@ export class RpcProvider implements ProviderInterface {
327329
) {
328330
return this.channel
329331
.getEstimateFee(invocations, options)
330-
.then(this.responseParser.parseFeeEstimateBulkResponse);
332+
.then((r) => this.responseParser.parseFeeEstimateBulkResponse(r));
331333
}
332334

333335
public async invokeFunction(

src/types/provider/configuration.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,9 @@ export type RpcProviderOptions = {
1212
specVersion?: string;
1313
default?: boolean;
1414
waitMode?: boolean;
15+
feeMarginPercentage?: {
16+
l1BoundMaxAmount: number;
17+
l1BoundMaxPricePerUnit: number;
18+
maxFee: number;
19+
};
1520
};

src/utils/responseParser/rpc.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
FeeEstimate,
1515
SimulateTransactionResponse,
1616
SimulatedTransaction,
17+
RpcProviderOptions,
1718
} from '../../types/provider';
1819
import { toBigInt } from '../num';
1920
import { estimateFeeToBounds, estimatedFeeToMaxFee } from '../stark';
@@ -31,6 +32,24 @@ export class RPCResponseParser
3132
| 'parseCallContractResponse'
3233
>
3334
{
35+
private margin: RpcProviderOptions['feeMarginPercentage'];
36+
37+
constructor(margin?: RpcProviderOptions['feeMarginPercentage']) {
38+
this.margin = margin;
39+
}
40+
41+
private estimatedFeeToMaxFee(estimatedFee: Parameters<typeof estimatedFeeToMaxFee>[0]) {
42+
return estimatedFeeToMaxFee(estimatedFee, this.margin?.maxFee);
43+
}
44+
45+
private estimateFeeToBounds(estimate: Parameters<typeof estimateFeeToBounds>[0]) {
46+
return estimateFeeToBounds(
47+
estimate,
48+
this.margin?.l1BoundMaxAmount,
49+
this.margin?.l1BoundMaxPricePerUnit
50+
);
51+
}
52+
3453
public parseGetBlockResponse(res: BlockWithTxHashes): GetBlockResponse {
3554
return { status: 'PENDING', ...res } as GetBlockResponse;
3655
}
@@ -58,8 +77,8 @@ export class RPCResponseParser
5877
gas_consumed: toBigInt(val.gas_consumed),
5978
gas_price: toBigInt(val.gas_price),
6079
unit: val.unit,
61-
suggestedMaxFee: estimatedFeeToMaxFee(val.overall_fee),
62-
resourceBounds: estimateFeeToBounds(val),
80+
suggestedMaxFee: this.estimatedFeeToMaxFee(val.overall_fee),
81+
resourceBounds: this.estimateFeeToBounds(val),
6382
};
6483
}
6584

@@ -69,8 +88,8 @@ export class RPCResponseParser
6988
gas_consumed: toBigInt(val.gas_consumed),
7089
gas_price: toBigInt(val.gas_price),
7190
unit: val.unit,
72-
suggestedMaxFee: estimatedFeeToMaxFee(val.overall_fee),
73-
resourceBounds: estimateFeeToBounds(val),
91+
suggestedMaxFee: this.estimatedFeeToMaxFee(val.overall_fee),
92+
resourceBounds: this.estimateFeeToBounds(val),
7493
}));
7594
}
7695

@@ -85,8 +104,8 @@ export class RPCResponseParser
85104
return res.map((it: SimulatedTransaction) => {
86105
return {
87106
...it,
88-
suggestedMaxFee: estimatedFeeToMaxFee(BigInt(it.fee_estimation.overall_fee)),
89-
resourceBounds: estimateFeeToBounds(it.fee_estimation),
107+
suggestedMaxFee: this.estimatedFeeToMaxFee(it.fee_estimation.overall_fee),
108+
resourceBounds: this.estimateFeeToBounds(it.fee_estimation),
90109
};
91110
});
92111
}

src/utils/stark.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getStarkKey, utils } from '@scure/starknet';
22
import { gzip, ungzip } from 'pako';
33

4-
import { ZERO } from '../constants';
4+
import { ZERO, feeMarginPercentage } from '../constants';
55
import {
66
ArraySignatureType,
77
BigNumberish,
@@ -95,14 +95,17 @@ export function signatureToHexArray(sig?: Signature): ArraySignatureType {
9595
/**
9696
* Convert estimated fee to max fee with overhead
9797
*/
98-
export function estimatedFeeToMaxFee(estimatedFee: BigNumberish, overhead: number = 0.5): bigint {
99-
return addPercent(estimatedFee, overhead * 100);
98+
export function estimatedFeeToMaxFee(
99+
estimatedFee: BigNumberish,
100+
overhead: number = feeMarginPercentage.MAX_FEE
101+
): bigint {
102+
return addPercent(estimatedFee, overhead);
100103
}
101104

102105
export function estimateFeeToBounds(
103106
estimate: FeeEstimate | 0n,
104-
amountOverhead: number = 10,
105-
priceOverhead = 50
107+
amountOverhead: number = feeMarginPercentage.L1_BOUND_MAX_AMOUNT,
108+
priceOverhead: number = feeMarginPercentage.L1_BOUND_MAX_PRICE_PER_UNIT
106109
): ResourceBounds {
107110
if (typeof estimate === 'bigint') {
108111
return {

0 commit comments

Comments
 (0)