Skip to content

Commit e77e2c7

Browse files
feat: add solana chains
1 parent ad91f01 commit e77e2c7

File tree

9 files changed

+171
-17
lines changed

9 files changed

+171
-17
lines changed

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
"types": "./dist/evm/releases/index.d.ts",
6363
"default": "./dist/evm/releases/index.js"
6464
},
65+
"./solana": {
66+
"types": "./dist/solana/index.d.ts",
67+
"default": "./dist/solana/index.js"
68+
},
6569
"./package.json": "./package.json",
6670
"./dist/*": "./dist/*"
6771
},

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export * from "./evm";
21
export * from "./helpers";
32
export * from "./sablier";
43
export * from "./types";

src/sablier.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* @file This file exports the Sablier object, singleton that contains the queries for the
3-
* chains, contracts, and releases.
3+
* chains, contracts, and releases for both evm and solana compatible chains.
44
*
55
* @example
66
* ```typescript
77
* import { sablier } from "sablier";
88
*
9-
* const lockupContract = sablier.contracts.get({
9+
* const lockupContract = sablier.evmContractsQueries.get({
1010
* chainId: mainnet.id,
1111
* contractName: "SablierLockup",
1212
* release: releases.lockup["v2.0"],
@@ -19,6 +19,7 @@ import _ from "lodash";
1919
import { chainsQueries as evmChainsQueries } from "./evm/chains/queries";
2020
import { contractsQueries as evmContractsQueries } from "./evm/contracts/queries";
2121
import { releasesQueries as evmReleasesQueries } from "./evm/releases/queries";
22+
import { chainsQueries as solanaChainsQueries } from "./solana/chains/queries";
2223

2324
const evmDeploymentsQueries = {
2425
/**
@@ -36,8 +37,16 @@ const evmDeploymentsQueries = {
3637
};
3738

3839
export const sablier = {
40+
/* -------------------------------------------------------------------------- */
41+
/* EVM */
42+
/* -------------------------------------------------------------------------- */
3943
evmChains: evmChainsQueries,
4044
evmContracts: evmContractsQueries,
4145
evmDeployments: evmDeploymentsQueries,
4246
evmReleases: evmReleasesQueries,
47+
48+
/* -------------------------------------------------------------------------- */
49+
/* SOLANA */
50+
/* -------------------------------------------------------------------------- */
51+
solanaChains: solanaChainsQueries,
4352
};

src/solana/chains/data.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import type { Sablier } from "@src/types";
2+
3+
const CHAIN_ID_SOLANA_MAINNET_BETA = 900000010;
4+
const CHAIN_ID_SOLANA_DEVNET = 900000020;
5+
6+
export const solana_devnet: Sablier.Solana.Chain = {
7+
blockExplorers: {
8+
default: { name: "Explorer", url: "https://solscan.io?cluster=devnet" },
9+
solanaFm: {
10+
name: "Solana FM",
11+
url: "https://solana.fm?cluster=devnet-alpha",
12+
},
13+
},
14+
chainlink: {
15+
feed: "99B2bTijsU6f1GCT73HmdR7HCFFjGMBcPZY6jZ96ynrR",
16+
program: "HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny",
17+
},
18+
contracts: {},
19+
definition: {
20+
chainCode: "SOLDEV",
21+
chainId: CHAIN_ID_SOLANA_DEVNET,
22+
cluster: "devnet",
23+
},
24+
id: CHAIN_ID_SOLANA_DEVNET,
25+
isSupportedByUI: true,
26+
isTestnet: true,
27+
name: "Devnet",
28+
nativeCurrency: {
29+
coinGeckoId: "solana",
30+
decimals: 9,
31+
name: "Solana",
32+
symbol: "SOL",
33+
},
34+
rpc: {
35+
defaults: ["https://api.devnet-beta.solana.com/"],
36+
helius: (key) => `https://devnet.helius-rpc.com/?api-key=${key}`,
37+
},
38+
rpcUrls: {
39+
default: {
40+
http: ["https://api.devnet-beta.solana.com/"],
41+
},
42+
},
43+
slug: "solana-devnet",
44+
} as const;
45+
46+
export const solana_mainnetBeta: Sablier.Solana.Chain = {
47+
blockExplorers: {
48+
default: { name: "Explorer", url: "https://solscan.io" },
49+
solanaFm: { name: "Solana FM", url: "https://solana.fm" },
50+
},
51+
chainlink: {
52+
feed: "99B2bTijsU6f1GCT73HmdR7HCFFjGMBcPZY6jZ96ynrR",
53+
program: "HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny",
54+
},
55+
contracts: {},
56+
definition: {
57+
chainCode: "SOL",
58+
chainId: CHAIN_ID_SOLANA_MAINNET_BETA,
59+
cluster: "mainnet-beta",
60+
},
61+
id: CHAIN_ID_SOLANA_MAINNET_BETA,
62+
isSupportedByUI: true,
63+
isTestnet: false,
64+
name: "Solana",
65+
nativeCurrency: {
66+
coinGeckoId: "solana",
67+
decimals: 9,
68+
name: "Solana",
69+
symbol: "SOL",
70+
},
71+
rpc: {
72+
defaults: ["https://api.mainnet-beta.solana.com/"],
73+
helius: (key) => `https://mainnet.helius-rpc.com/?api-key=${key}`,
74+
},
75+
rpcUrls: {
76+
default: {
77+
http: ["https://api.mainnet-beta.solana.com/"],
78+
},
79+
},
80+
slug: "solana-mainnet-beta",
81+
} as const;

src/solana/chains/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * as chains from "./data";
2+
export * from "./data";

src/solana/chains/queries.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { sortChains } from "@src/helpers";
2+
import type { Sablier } from "@src/types";
3+
import _ from "lodash";
4+
import * as chains from "./data";
5+
6+
export const chainsQueries = {
7+
get: (chainId: number): Sablier.Solana.Chain | undefined => {
8+
return _.find(chains, (c) => c.id === chainId);
9+
},
10+
getAll: (): Sablier.Solana.Chain[] => {
11+
return sortChains(_.values(chains));
12+
},
13+
getBySlug: (slug: string): Sablier.Solana.Chain | undefined => {
14+
return _.find(chains, (c) => c.slug === slug);
15+
},
16+
getMainnets: (): Sablier.Solana.Chain[] => {
17+
return sortChains(_.filter(_.values(chains), (c) => !c.isTestnet));
18+
},
19+
getOrThrow: (chainId: number): Sablier.Solana.Chain => {
20+
const chain = _.find(chains, (c) => c.id === chainId);
21+
if (!chain) {
22+
throw new Error(`Sablier SDK: Chain with ID ${chainId} not found`);
23+
}
24+
return chain;
25+
},
26+
getTestnets: (): Sablier.Solana.Chain[] => {
27+
return sortChains(_.filter(_.values(chains), (c) => c.isTestnet));
28+
},
29+
};

src/solana/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export { chains } from "./chains";
12
export * from "./idl";

src/types.ts

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@ export namespace Sablier {
66
/* TYPES */
77
/* -------------------------------------------------------------------------- */
88

9+
type BaseChain = ViemChain & {
10+
blockExplorers: {
11+
[key: string]: ChainBlockExplorer;
12+
default: ChainBlockExplorer;
13+
};
14+
/** Whether this chain is supported by the Sablier Interface at https://app.sablier.com. */
15+
isSupportedByUI: boolean;
16+
/** Whether this is a testnet network. */
17+
isTestnet: boolean;
18+
/** Whether this is a zkEVM like zkSync. */
19+
nativeCurrency: ViemChain["nativeCurrency"] & {
20+
coinGeckoId: string;
21+
};
22+
slug: string;
23+
};
24+
925
/**
1026
* @see https://github.com/wevm/viem/discussions/3678
1127
*/
@@ -30,20 +46,8 @@ export namespace Sablier {
3046
export type Address = `0x${string}`;
3147

3248
export type AbiMap = { [contractName: string]: readonly object[] };
33-
export type Chain = ViemChain & {
34-
blockExplorers: {
35-
[key: string]: ChainBlockExplorer;
36-
default: ChainBlockExplorer;
37-
};
38-
/** Whether this chain is supported by the Sablier Interface at https://app.sablier.com. */
39-
isSupportedByUI: boolean;
40-
/** Whether this is a testnet network. */
41-
isTestnet: boolean;
42-
/** Whether this is a zkEVM like zkSync. */
49+
export type Chain = BaseChain & {
4350
isZK: boolean;
44-
nativeCurrency: ViemChain["nativeCurrency"] & {
45-
coinGeckoId: string;
46-
};
4751
rpc: {
4852
/** Alchemy RPC URL generator. */
4953
alchemy?: (apiKey: string) => string;
@@ -200,6 +204,31 @@ export namespace Sablier {
200204
export type Version = Version.Airdrops | Version.Flow | Version.Legacy | Version.Lockup;
201205
}
202206

207+
export namespace Solana {
208+
export type Address = string;
209+
export type ChainCode = "SOL" | "SOLDEV" | "SOLTEST";
210+
export type Cluster = "mainnet-beta" | "devnet" | "testnet";
211+
212+
export type Chain = BaseChain & {
213+
rpc: {
214+
/** Helius RPC URL generator. */
215+
helius?: (apiKey: string) => string;
216+
/** Default RPC URL. */
217+
defaults: string[];
218+
};
219+
/** Used in deployment files to identify the chain, e.g., arbitrum-sepolia. */
220+
chainlink: {
221+
program: Address; // Chainlink program used to retrieve on-chain price feeds
222+
feed: Address; // Account providing the SOL/USD price feed data.
223+
};
224+
definition: {
225+
chainCode: ChainCode;
226+
chainId: number;
227+
cluster: Cluster;
228+
};
229+
};
230+
}
231+
203232
export namespace Indexer {
204233
type Common = {
205234
chainId: number;

tests/helpers/missing.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const MISSING_CONTRACTS: ProtocolMap = {
5353
lockup: MISSING_LOCKUP,
5454
};
5555

56-
// Chains for which we completely lack broadcasts.
56+
// chains for which we completely lack broadcasts.
5757
export const MISSING_CHAINS: number[] = [chains.iotex.id, chains.ronin.id, chains.tangle.id];
5858

5959
export function isKnownMissing(release: Sablier.EVM.Release, chain: Sablier.EVM.Chain, contractName: string): boolean {

0 commit comments

Comments
 (0)