Skip to content

Commit 433a6aa

Browse files
authored
feat(sdk): construct raw spoke provider util (#903)
1 parent 4cd24be commit 433a6aa

File tree

9 files changed

+459
-16
lines changed

9 files changed

+459
-16
lines changed

packages/sdk/docs/HOW_TO_CREATE_A_SPOKE_PROVIDER.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,44 @@ Raw spoke providers are ideal for:
4343

4444
**Note**: When using raw spoke providers with Sodax features, you must pass the `raw: true` flag to methods like `createIntent()`, `supply()`, etc. This ensures the methods return raw transaction data instead of attempting to execute transactions.
4545

46+
### Constructing a Raw Spoke Provider from Config
47+
48+
When you have a generic config object (e.g. from API or runtime) and want a single entry point to build the correct raw spoke provider for any supported chain, use the **`constructRawSpokeProvider`** helper. It dispatches on `config.chainConfig.chain.type` and returns the appropriate raw spoke provider instance.
49+
50+
**Signature**
51+
52+
```typescript
53+
function constructRawSpokeProvider(config: RawSpokeProviderConfig): RawSpokeProvider
54+
```
55+
56+
- **Parameter**: `config`A chain-specific raw spoke provider config (`EvmRawSpokeProviderConfig`, `SonicRawSpokeProviderConfig`, `StellarRawSpokeProviderConfig`, `SolanaRawSpokeProviderConfig`, `IconRawSpokeProviderConfig`, `InjectiveRawSpokeProviderConfig`, or `SuiRawSpokeProviderConfig`). The config must include `chainConfig` with `chain.type` set to one of: `EVM`, `STELLAR`, `SOLANA`, `ICON`, `INJECTIVE`, `SUI`. For Sonic, use EVM config with `chain.id === SONIC_MAINNET_CHAIN_ID`.
57+
- **Returns**: A `RawSpokeProvider` instance (e.g. `EvmRawSpokeProvider`, `StellarRawSpokeProvider`, `SolanaRawSpokeProvider`, etc.).
58+
- **Throws**: `Error` with message `Unsupported chain type: ${chainType}` if `chain.type` is not supported.
59+
60+
**Example**
61+
62+
```typescript
63+
import {
64+
constructRawSpokeProvider,
65+
spokeChainConfig,
66+
ARBITRUM_MAINNET_CHAIN_ID,
67+
type RawSpokeProviderConfig,
68+
type EvmRawSpokeProviderConfig,
69+
} from "@sodax/sdk";
70+
71+
// Config might come from API or be built at runtime
72+
const config: EvmRawSpokeProviderConfig = {
73+
walletAddress: "0x...",
74+
chainConfig: spokeChainConfig[ARBITRUM_MAINNET_CHAIN_ID],
75+
rpcUrl: "https://arb1.arbitrum.io/rpc",
76+
};
77+
78+
const rawSpokeProvider = constructRawSpokeProvider(config);
79+
// rawSpokeProvider is EvmRawSpokeProvider — use with Sodax features and raw: true
80+
```
81+
82+
Use `constructRawSpokeProvider` when the chain type is determined at runtime or when you want one code path that works for all supported chains. For a known chain type, you can still instantiate the specific raw spoke provider class directly (e.g. `new EvmRawSpokeProvider(...)`) as shown in the chain-specific sections below.
83+
4684
## Prerequisites
4785

4886
Before creating a spoke provider, ensure you have:
@@ -1073,6 +1111,7 @@ For detailed staking documentation, see [STAKING.md](./STAKING.md).
10731111
- `InjectiveRawSpokeProvider` for Injective blockchain
10741112
- `IconRawSpokeProvider` for ICON blockchain
10751113
- `SolanaRawSpokeProvider` for Solana blockchain
1114+
- **`constructRawSpokeProvider(config)`** — builds the correct raw spoke provider from a `RawSpokeProviderConfig` based on `chainConfig.chain.type` (use when chain is chosen at runtime).
10761115

10771116
- **When to use Raw Spoke Providers:**
10781117
- Backend services creating transaction payloads

packages/sdk/src/shared/entities/Providers.ts

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@ import {
1111
createPublicClient,
1212
} from 'viem';
1313
import { getEvmViemChain } from '../constants.js';
14-
import type { InjectiveRawSpokeProvider, InjectiveSpokeProvider } from './injective/InjectiveSpokeProvider.js';
15-
import type { IconRawSpokeProvider, IconSpokeProvider } from './icon/IconSpokeProvider.js';
16-
import type { SolanaRawSpokeProvider, SolanaSpokeProvider } from './solana/SolanaSpokeProvider.js';
17-
import type { SuiRawSpokeProvider, SuiSpokeProvider } from './sui/SuiSpokeProvider.js';
14+
import type {
15+
InjectiveRawSpokeProvider,
16+
InjectiveRawSpokeProviderConfig,
17+
InjectiveSpokeProvider,
18+
} from './injective/InjectiveSpokeProvider.js';
19+
import type { IconRawSpokeProvider, IconRawSpokeProviderConfig, IconSpokeProvider } from './icon/IconSpokeProvider.js';
20+
import type {
21+
SolanaRawSpokeProvider,
22+
SolanaRawSpokeProviderConfig,
23+
SolanaSpokeProvider,
24+
} from './solana/SolanaSpokeProvider.js';
25+
import type { SuiRawSpokeProvider, SuiRawSpokeProviderConfig, SuiSpokeProvider } from './sui/SuiSpokeProvider.js';
1826
import {
1927
SONIC_MAINNET_CHAIN_ID,
2028
type IEvmWalletProvider,
@@ -32,7 +40,11 @@ import {
3240
} from '@sodax/types';
3341
import type { ConfigService } from '../config/ConfigService.js';
3442
import { getHubChainConfig } from '../config/ConfigService.js';
35-
import type { StellarRawSpokeProvider, StellarSpokeProvider } from './stellar/StellarSpokeProvider.js';
43+
import type {
44+
StellarRawSpokeProvider,
45+
StellarRawSpokeProviderConfig,
46+
StellarSpokeProvider,
47+
} from './stellar/StellarSpokeProvider.js';
3648

3749
export type CustomProvider = { request(...args: unknown[]): Promise<unknown> };
3850

@@ -129,6 +141,12 @@ export class SonicSpokeProvider extends SonicBaseSpokeProvider implements ISpoke
129141
}
130142
}
131143

144+
export type SonicRawSpokeProviderConfig = {
145+
walletAddress: Address;
146+
chainConfig: SonicSpokeChainConfig;
147+
rpcUrl?: string;
148+
};
149+
132150
export class SonicRawSpokeProvider extends SonicBaseSpokeProvider implements IRawSpokeProvider {
133151
public readonly walletProvider: WalletAddressProvider;
134152
public readonly raw = true;
@@ -170,6 +188,12 @@ export class EvmSpokeProvider extends EvmBaseSpokeProvider implements ISpokeProv
170188
}
171189
}
172190

191+
export type EvmRawSpokeProviderConfig = {
192+
walletAddress: Address;
193+
chainConfig: EvmSpokeChainConfig;
194+
rpcUrl?: string;
195+
};
196+
173197
export class EvmRawSpokeProvider extends EvmBaseSpokeProvider implements IRawSpokeProvider {
174198
public readonly walletProvider: WalletAddressProvider;
175199
public readonly raw = true;
@@ -188,7 +212,6 @@ export type IWalletProvider =
188212
| IStellarWalletProvider
189213
| ISuiWalletProvider
190214
| IIconWalletProvider
191-
| IInjectiveWalletProvider
192215
| ISolanaWalletProvider;
193216

194217
export type SpokeProvider = (
@@ -213,4 +236,16 @@ export type RawSpokeProvider = (
213236
) &
214237
IRawSpokeProvider;
215238

239+
export type RawSpokeProviderConfig = (
240+
| EvmRawSpokeProviderConfig
241+
| InjectiveRawSpokeProviderConfig
242+
| IconRawSpokeProviderConfig
243+
| SuiRawSpokeProviderConfig
244+
| StellarRawSpokeProviderConfig
245+
| SolanaRawSpokeProviderConfig
246+
| SonicRawSpokeProviderConfig
247+
) & {
248+
chainConfig: SpokeChainConfig;
249+
};
250+
216251
export type SpokeProviderType = SpokeProvider | RawSpokeProvider;

packages/sdk/src/shared/entities/icon/IconSpokeProvider.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ export class IconSpokeProvider extends IconBaseSpokeProvider implements ISpokePr
3535
}
3636
}
3737

38+
export type IconRawSpokeProviderConfig = {
39+
chainConfig: IconSpokeChainConfig;
40+
walletAddress: string;
41+
};
42+
3843
export class IconRawSpokeProvider extends IconBaseSpokeProvider implements IRawSpokeProvider {
3944
public readonly walletProvider: WalletAddressProvider;
4045
public readonly raw = true;

packages/sdk/src/shared/entities/injective/InjectiveSpokeProvider.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ export class InjectiveBaseSpokeProvider {
174174
}
175175
}
176176

177+
export type InjectiveRawSpokeProviderConfig = {
178+
chainConfig: InjectiveSpokeChainConfig;
179+
walletAddress: string;
180+
};
181+
177182
export class InjectiveRawSpokeProvider extends InjectiveBaseSpokeProvider implements IRawSpokeProvider {
178183
public readonly walletProvider: WalletAddressProvider;
179184
public readonly raw = true;

packages/sdk/src/shared/entities/stellar/StellarSpokeProvider.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,12 @@ export class StellarBaseSpokeProvider {
316316
}
317317
}
318318

319+
export type StellarRawSpokeProviderConfig = {
320+
walletAddress: string;
321+
chainConfig: StellarSpokeChainConfig;
322+
rpcConfig?: StellarRpcConfig;
323+
};
324+
319325
export class StellarRawSpokeProvider extends StellarBaseSpokeProvider implements IRawSpokeProvider {
320326
public readonly walletProvider: WalletAddressProvider;
321327
public readonly raw = true;

packages/sdk/src/shared/entities/sui/SuiSpokeProvider.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ export class SuiBaseSpokeProvider {
244244
}
245245
}
246246

247+
export type SuiRawSpokeProviderConfig = {
248+
chainConfig: SuiSpokeChainConfig;
249+
walletAddress: string;
250+
};
251+
247252
export class SuiRawSpokeProvider extends SuiBaseSpokeProvider implements IRawSpokeProvider {
248253
public readonly walletProvider: WalletAddressProvider;
249254
public readonly raw = true;

packages/sdk/src/shared/guards.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ import {
1414
type SonicRawSpokeProvider,
1515
type RawSpokeProvider,
1616
type SpokeProviderType,
17+
type EvmRawSpokeProviderConfig,
18+
type SonicRawSpokeProviderConfig,
19+
type RawSpokeProviderConfig,
1720
} from './entities/Providers.js';
1821
import { InjectiveSpokeProvider, type InjectiveRawSpokeProvider } from './entities/injective/InjectiveSpokeProvider.js';
1922
import { IconSpokeProvider, type IconRawSpokeProvider } from './entities/icon/IconSpokeProvider.js';
20-
import { SolanaSpokeProvider, type SolanaRawSpokeProvider } from './entities/solana/SolanaSpokeProvider.js';
23+
import { SolanaSpokeProvider, type SolanaRawSpokeProvider, type SolanaRawSpokeProviderConfig } from './entities/solana/SolanaSpokeProvider.js';
2124
import { SuiSpokeProvider, type SuiRawSpokeProvider } from './entities/sui/SuiSpokeProvider.js';
22-
import { StellarSpokeProvider, type StellarRawSpokeProvider } from './entities/stellar/StellarSpokeProvider.js';
25+
import { StellarSpokeProvider, type StellarRawSpokeProvider, type StellarRawSpokeProviderConfig } from './entities/stellar/StellarSpokeProvider.js';
2326
import type {
2427
EvmSpokeProviderType,
2528
IconSpokeProviderType,
@@ -466,3 +469,45 @@ export function isSonicRawSpokeProvider(value: unknown): value is SonicRawSpokeP
466469
export function isAddressString(value: unknown): value is string {
467470
return typeof value === 'string';
468471
}
472+
473+
export function isEvmRawSpokeProviderConfig(value: RawSpokeProviderConfig): value is EvmRawSpokeProviderConfig {
474+
return (
475+
typeof value === 'object' &&
476+
value !== null &&
477+
'walletAddress' in value &&
478+
'chainConfig' in value &&
479+
value.chainConfig.chain.type === 'EVM'
480+
);
481+
}
482+
483+
export function isSonicRawSpokeProviderConfig(value: RawSpokeProviderConfig): value is SonicRawSpokeProviderConfig {
484+
return (
485+
typeof value === 'object' &&
486+
value !== null &&
487+
'walletAddress' in value &&
488+
'chainConfig' in value &&
489+
value.chainConfig.chain.type === 'EVM' &&
490+
value.chainConfig.chain.id === SONIC_MAINNET_CHAIN_ID
491+
);
492+
}
493+
494+
export function isStellarRawSpokeProviderConfig(value: RawSpokeProviderConfig): value is StellarRawSpokeProviderConfig {
495+
return (
496+
typeof value === 'object' &&
497+
value !== null &&
498+
'walletAddress' in value &&
499+
'chainConfig' in value &&
500+
value.chainConfig.chain.type === 'STELLAR'
501+
);
502+
}
503+
504+
export function isSolanaRawSpokeProviderConfig(value: RawSpokeProviderConfig): value is SolanaRawSpokeProviderConfig {
505+
return (
506+
typeof value === 'object' &&
507+
value !== null &&
508+
'walletAddress' in value &&
509+
'chainConfig' in value &&
510+
'connection' in value &&
511+
value.chainConfig.chain.type === 'SOLANA'
512+
);
513+
}

0 commit comments

Comments
 (0)