Skip to content

Commit 1f48dc6

Browse files
authored
fix: make the provider create method generic to enable it when mixins are applied (#1370)
1 parent e369fe2 commit 1f48dc6

File tree

3 files changed

+45
-19
lines changed

3 files changed

+45
-19
lines changed

__tests__/rpcProvider.test.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
import { getStarkKey, Signature, utils } from '@scure/starknet';
2+
import { hasMixin } from 'ts-mixer';
3+
import {
4+
contracts,
5+
createBlockForDevnet,
6+
createTestProvider,
7+
describeIfDevnet,
8+
describeIfNotDevnet,
9+
describeIfRpc,
10+
describeIfTestnet,
11+
ETHtokenAddress,
12+
getTestAccount,
13+
waitNextBlock,
14+
} from './config/fixtures';
15+
import { initializeMatcher } from './config/schema';
216
import typedDataExample from '../__mocks__/typedData/baseExample.json';
317
import {
418
Account,
519
Block,
620
CallData,
721
Contract,
822
FeeEstimate,
23+
LibraryError,
924
ProviderInterface,
1025
RPC,
1126
RPCResponseParser,
@@ -19,21 +34,11 @@ import {
1934
import { StarknetChainId } from '../src/global/constants';
2035
import { felt, uint256 } from '../src/utils/calldata/cairo';
2136
import { toBigInt, toHexString } from '../src/utils/num';
22-
import {
23-
contracts,
24-
createBlockForDevnet,
25-
createTestProvider,
26-
describeIfDevnet,
27-
describeIfNotDevnet,
28-
describeIfRpc,
29-
describeIfTestnet,
30-
ETHtokenAddress,
31-
getTestAccount,
32-
waitNextBlock,
33-
} from './config/fixtures';
34-
import { initializeMatcher } from './config/schema';
35-
import { isBoolean } from '../src/utils/typed';
3637
import { isVersion } from '../src/utils/provider';
38+
import { isBoolean } from '../src/utils/typed';
39+
import { RpcProvider as BaseRpcProvider } from '../src/provider/rpc';
40+
import { RpcProvider as ExtendedRpcProvider } from '../src/provider/extensions/default';
41+
import { StarknetId } from '../src/provider/extensions/starknetId';
3742

3843
describeIfRpc('RPCProvider', () => {
3944
let rpcProvider: RpcProvider;
@@ -53,6 +58,16 @@ describeIfRpc('RPCProvider', () => {
5358
await createBlockForDevnet();
5459
});
5560

61+
test('create should be usable by the base and extended RpcProvider, but not Account', async () => {
62+
const nodeUrl = process.env.TEST_RPC_URL;
63+
const base = await BaseRpcProvider.create({ nodeUrl });
64+
const extended = await ExtendedRpcProvider.create({ nodeUrl });
65+
66+
expect(hasMixin(base, StarknetId)).toBe(false);
67+
expect(hasMixin(extended, StarknetId)).toBe(true);
68+
await expect(Account.create()).rejects.toThrow(LibraryError);
69+
});
70+
5671
test('detect spec version with create', async () => {
5772
const providerTest = await RpcProvider.create({ nodeUrl: process.env.TEST_RPC_URL });
5873
const { channel } = providerTest;

src/account/default.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
UDC,
77
ZERO,
88
} from '../global/constants';
9-
import { Provider, ProviderInterface } from '../provider';
9+
import { LibraryError, Provider, ProviderInterface } from '../provider';
1010
import { Signer, SignerInterface } from '../signer';
1111
import {
1212
AccountInvocations,
@@ -119,6 +119,13 @@ export class Account extends Provider implements AccountInterface {
119119
});
120120
}
121121

122+
/** @deprecated @hidden */
123+
// The deprecation tag is meant to discourage use, not to signal future removal
124+
// it should only be removed if the relationship with the corresponding Provider.create(...) method changes
125+
static async create(): Promise<never> {
126+
throw new LibraryError('Not supported');
127+
}
128+
122129
// provided version or contract based preferred transactionVersion
123130
protected getPreferredVersion(type12: ETransactionVersion, type3: ETransactionVersion) {
124131
if (this.transactionVersion === ETransactionVersion.V3) return type3;

src/provider/rpc.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,18 +92,22 @@ export class RpcProvider implements ProviderInterface {
9292
* auto configure channel based on provided node
9393
* leave space for other async before constructor
9494
*/
95-
static async create(optionsOrProvider?: RpcProviderOptions) {
95+
// NOTE: the generic T and 'this' reference are used so that the expanded class is generated when a mixin is applied
96+
static async create<T extends RpcProvider>(
97+
this: { new (...args: ConstructorParameters<typeof RpcProvider>): T },
98+
optionsOrProvider?: RpcProviderOptions
99+
): Promise<T> {
96100
const channel = new RPC07.RpcChannel({ ...optionsOrProvider });
97101
const spec = await channel.getSpecVersion();
98102

99103
if (isVersion('0.7', spec)) {
100-
return new RpcProvider({ ...optionsOrProvider, specVersion: '0.7' });
104+
return new this({ ...optionsOrProvider, specVersion: '0.7' }) as T;
101105
}
102106
if (isVersion('0.8', spec)) {
103-
return new RpcProvider({ ...optionsOrProvider, specVersion: '0.8' });
107+
return new this({ ...optionsOrProvider, specVersion: '0.8' }) as T;
104108
}
105109

106-
throw new Error('unable to detect specification version');
110+
throw new LibraryError('Unable to detect specification version');
107111
}
108112

109113
public fetch(method: string, params?: object, id: string | number = 0) {

0 commit comments

Comments
 (0)