@zebec-network/admin-sdk 1.0.0

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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +730 -0
  3. package/dist/artifacts/abi/index.d.ts +161 -0
  4. package/dist/artifacts/abi/index.js +7 -0
  5. package/dist/artifacts/abi/token.json +350 -0
  6. package/dist/artifacts/abi/weth.json +153 -0
  7. package/dist/artifacts/abi/zebecCard.json +1229 -0
  8. package/dist/artifacts/index.d.ts +11 -0
  9. package/dist/artifacts/index.js +11 -0
  10. package/dist/artifacts/typechain-types/OdysseyZebecCard.d.ts +559 -0
  11. package/dist/artifacts/typechain-types/OdysseyZebecCard.js +1 -0
  12. package/dist/artifacts/typechain-types/Token.d.ts +149 -0
  13. package/dist/artifacts/typechain-types/Token.js +1 -0
  14. package/dist/artifacts/typechain-types/Weth.d.ts +179 -0
  15. package/dist/artifacts/typechain-types/Weth.js +1 -0
  16. package/dist/artifacts/typechain-types/ZebecCard.d.ts +723 -0
  17. package/dist/artifacts/typechain-types/ZebecCard.js +1 -0
  18. package/dist/artifacts/typechain-types/common.d.ts +50 -0
  19. package/dist/artifacts/typechain-types/common.js +1 -0
  20. package/dist/artifacts/typechain-types/factories/OdysseyZebecCard__factory.d.ts +735 -0
  21. package/dist/artifacts/typechain-types/factories/OdysseyZebecCard__factory.js +959 -0
  22. package/dist/artifacts/typechain-types/factories/Token__factory.d.ts +280 -0
  23. package/dist/artifacts/typechain-types/factories/Token__factory.js +374 -0
  24. package/dist/artifacts/typechain-types/factories/Weth__factory.d.ts +219 -0
  25. package/dist/artifacts/typechain-types/factories/Weth__factory.js +292 -0
  26. package/dist/artifacts/typechain-types/factories/ZebecCard__factory.d.ts +966 -0
  27. package/dist/artifacts/typechain-types/factories/ZebecCard__factory.js +1253 -0
  28. package/dist/artifacts/typechain-types/factories/index.d.ts +4 -0
  29. package/dist/artifacts/typechain-types/factories/index.js +7 -0
  30. package/dist/artifacts/typechain-types/index.d.ts +9 -0
  31. package/dist/artifacts/typechain-types/index.js +8 -0
  32. package/dist/artifacts/zebec_instant_card.d.ts +3683 -0
  33. package/dist/artifacts/zebec_instant_card.js +1 -0
  34. package/dist/artifacts/zebec_instant_card.json +2801 -0
  35. package/dist/artifacts/zebec_proxy_stream.d.ts +1933 -0
  36. package/dist/artifacts/zebec_proxy_stream.js +1 -0
  37. package/dist/artifacts/zebec_proxy_stream.json +1497 -0
  38. package/dist/artifacts/zebec_stake_v1.d.ts +1679 -0
  39. package/dist/artifacts/zebec_stake_v1.js +1 -0
  40. package/dist/artifacts/zebec_stake_v1.json +1255 -0
  41. package/dist/artifacts/zebec_stream.d.ts +1481 -0
  42. package/dist/artifacts/zebec_stream.js +1 -0
  43. package/dist/artifacts/zebec_stream.json +1243 -0
  44. package/dist/constants.d.ts +32 -0
  45. package/dist/constants.js +115 -0
  46. package/dist/index.d.ts +6 -0
  47. package/dist/index.js +6 -0
  48. package/dist/pda.d.ts +23 -0
  49. package/dist/pda.js +125 -0
  50. package/dist/services/evmCardService.d.ts +189 -0
  51. package/dist/services/evmCardService.js +322 -0
  52. package/dist/services/index.d.ts +6 -0
  53. package/dist/services/index.js +6 -0
  54. package/dist/services/proxyStreamService.d.ts +72 -0
  55. package/dist/services/proxyStreamService.js +302 -0
  56. package/dist/services/solanaCardV2Service.d.ts +50 -0
  57. package/dist/services/solanaCardV2Service.js +717 -0
  58. package/dist/services/stakingService.d.ts +39 -0
  59. package/dist/services/stakingService.js +265 -0
  60. package/dist/services/streamServices.d.ts +50 -0
  61. package/dist/services/streamServices.js +322 -0
  62. package/dist/services/suiCardService.d.ts +91 -0
  63. package/dist/services/suiCardService.js +487 -0
  64. package/dist/types.d.ts +433 -0
  65. package/dist/types.js +1 -0
  66. package/dist/utils.d.ts +5 -0
  67. package/dist/utils.js +39 -0
  68. package/package.json +57 -0
@@ -0,0 +1,39 @@
1
+ import { type Address, Program, type Provider } from "@coral-xyz/anchor";
2
+ import type { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js";
3
+ import { TransactionPayload } from "@zebec-network/solana-common";
4
+ import { type ZebecStakeIdlV1 } from "../artifacts/index.js";
5
+ import type { InitLockupInstructionData, LockupInfo, Numeric, RewardScheme, RpcNetwork, StakeInfo, UpdateLockupInstructionData } from "../types.js";
6
+ export declare class ZebecStakeAdminService {
7
+ readonly provider: Provider;
8
+ readonly program: Program<ZebecStakeIdlV1>;
9
+ readonly network: RpcNetwork;
10
+ constructor(provider: Provider, program: Program<ZebecStakeIdlV1>, network: RpcNetwork);
11
+ static create(provider: Provider, network: RpcNetwork): ZebecStakeAdminService;
12
+ private _createPayload;
13
+ getInitLockupInstruction(creator: PublicKey, lockup: PublicKey, stakeToken: PublicKey, rewardToken: PublicKey, rewardVault: PublicKey, stakeVault: PublicKey, data: InitLockupInstructionData): Promise<TransactionInstruction>;
14
+ getUpdateLockupInstruction(updater: PublicKey, lockup: PublicKey, data: UpdateLockupInstructionData): Promise<TransactionInstruction>;
15
+ initLockup(params: {
16
+ stakeToken: Address;
17
+ rewardToken: Address;
18
+ creator?: Address;
19
+ name: string;
20
+ fee: Numeric;
21
+ feeVault: Address;
22
+ rewardSchemes: RewardScheme[];
23
+ minimumStake: Numeric;
24
+ }): Promise<TransactionPayload>;
25
+ updateLockup(params: {
26
+ lockupName: string;
27
+ updater?: Address;
28
+ fee: Numeric;
29
+ feeVault: Address;
30
+ rewardSchemes: RewardScheme[];
31
+ minimumStake: Numeric;
32
+ }): Promise<TransactionPayload>;
33
+ getLockupInfo(lockupAddress: Address): Promise<LockupInfo | null>;
34
+ getAllStakesCount(lockupAddress: Address, commitment?: "finalized" | "confirmed"): Promise<number>;
35
+ getStakeSignatureForStake(stakeInfo: StakeInfo): Promise<string | null>;
36
+ getAllStakesInfo(lockupAddress: Address): Promise<StakeInfo[]>;
37
+ get programId(): PublicKey;
38
+ get connection(): Connection;
39
+ }
@@ -0,0 +1,265 @@
1
+ import { AnchorProvider, Program, translateAddress, } from "@coral-xyz/anchor";
2
+ import { bpsToPercent, percentToBps } from "@zebec-network/core-utils";
3
+ import { getMintDecimals, TEN_BIGNUM, TransactionPayload, } from "@zebec-network/solana-common";
4
+ import { BigNumber } from "bignumber.js";
5
+ import BN from "bn.js";
6
+ import { ZEBEC_STAKE_IDL } from "../artifacts/index.js";
7
+ import { deriveLockupAddress, deriveRewardVaultAddress, deriveStakeVaultAddress, } from "../pda.js";
8
+ export class ZebecStakeAdminService {
9
+ provider;
10
+ program;
11
+ network;
12
+ constructor(provider, program, network) {
13
+ this.provider = provider;
14
+ this.program = program;
15
+ this.network = network;
16
+ }
17
+ static create(provider, network) {
18
+ const connection = provider.connection;
19
+ const rpcEndpoint = connection.rpcEndpoint;
20
+ const connNetwork = rpcEndpoint.includes("devnet")
21
+ ? "devnet"
22
+ : rpcEndpoint.includes("testnet")
23
+ ? "testnet"
24
+ : rpcEndpoint.includes("localhost:8899")
25
+ ? "localnet"
26
+ : "mainnet-beta";
27
+ if (network !== connNetwork) {
28
+ throw new Error(`InvalidOperation: Network mismatch. network and connection network should be same. network: ${network}, connection: ${connNetwork}`);
29
+ }
30
+ const program = new Program(ZEBEC_STAKE_IDL, provider);
31
+ return new ZebecStakeAdminService(provider, program, network);
32
+ }
33
+ async _createPayload(payerKey, instructions, signers, addressLookupTableAccounts) {
34
+ const errorMap = new Map();
35
+ this.program.idl.errors.map((error) => errorMap.set(error.code, error.msg));
36
+ let signTransaction;
37
+ const provider = this.provider;
38
+ if (provider instanceof AnchorProvider) {
39
+ signTransaction = async (tx) => {
40
+ return provider.wallet.signTransaction(tx);
41
+ };
42
+ }
43
+ return new TransactionPayload(this.connection, errorMap, {
44
+ instructions,
45
+ feePayer: payerKey,
46
+ signers: signers ?? [],
47
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
48
+ }, signTransaction);
49
+ }
50
+ async getInitLockupInstruction(creator, lockup, stakeToken, rewardToken, rewardVault, stakeVault, data) {
51
+ return this.program.methods
52
+ .initLockup({
53
+ fee: data.fee,
54
+ durationMap: data.rewardSchemes,
55
+ feeVault: data.feeVault,
56
+ name: data.name,
57
+ minimumStake: data.minimumStake,
58
+ })
59
+ .accountsPartial({
60
+ creator,
61
+ lockup,
62
+ rewardToken,
63
+ rewardVault,
64
+ stakeToken,
65
+ stakeVault,
66
+ })
67
+ .instruction();
68
+ }
69
+ async getUpdateLockupInstruction(updater, lockup, data) {
70
+ return this.program.methods
71
+ .updateLockup({
72
+ durationMap: data.rewardSchemes,
73
+ fee: data.fee,
74
+ feeVault: data.feeVault,
75
+ minimumStake: data.minimumStake,
76
+ })
77
+ .accountsPartial({
78
+ updater,
79
+ lockup,
80
+ })
81
+ .instruction();
82
+ }
83
+ async initLockup(params) {
84
+ const creator = params.creator
85
+ ? translateAddress(params.creator)
86
+ : this.provider.publicKey;
87
+ if (!creator) {
88
+ throw new Error("MissingArgument: Please provide either creator address or publicKey in provider");
89
+ }
90
+ const stakeToken = translateAddress(params.stakeToken);
91
+ const rewardToken = translateAddress(params.rewardToken);
92
+ const feeVault = translateAddress(params.feeVault);
93
+ const stakeTokenDecimals = await getMintDecimals(this.connection, stakeToken);
94
+ const UNITS_PER_STAKE_TOKEN = TEN_BIGNUM.pow(stakeTokenDecimals);
95
+ const rewardSchemes = params.rewardSchemes.map((value) => {
96
+ return {
97
+ duration: new BN(value.duration),
98
+ reward: new BN(percentToBps(value.rewardRate)),
99
+ };
100
+ });
101
+ const lockup = deriveLockupAddress(params.name, this.programId);
102
+ const rewardVault = deriveRewardVaultAddress(lockup, this.programId);
103
+ const stakeVault = deriveStakeVaultAddress(lockup, this.programId);
104
+ const fee = new BN(BigNumber(params.fee).times(UNITS_PER_STAKE_TOKEN).toFixed(0));
105
+ const minimumStake = new BN(BigNumber(params.minimumStake).times(UNITS_PER_STAKE_TOKEN).toFixed(0));
106
+ const instruction = await this.getInitLockupInstruction(creator, lockup, stakeToken, rewardToken, rewardVault, stakeVault, {
107
+ fee,
108
+ feeVault: feeVault,
109
+ name: params.name,
110
+ rewardSchemes,
111
+ minimumStake,
112
+ });
113
+ return this._createPayload(creator, [instruction]);
114
+ }
115
+ async updateLockup(params) {
116
+ const updater = params.updater
117
+ ? translateAddress(params.updater)
118
+ : this.provider.publicKey;
119
+ if (!updater) {
120
+ throw new Error("MissingArgument: Please provide either updater address or publicKey in provider");
121
+ }
122
+ const lockup = deriveLockupAddress(params.lockupName, this.programId);
123
+ const lockupInfo = await this.program.account.lockup.fetch(lockup, this.connection.commitment);
124
+ const stakeToken = lockupInfo.stakedToken.tokenAddress;
125
+ const feeVault = translateAddress(params.feeVault);
126
+ const stakeTokenDecimals = await getMintDecimals(this.connection, stakeToken);
127
+ const UNITS_PER_STAKE_TOKEN = TEN_BIGNUM.pow(stakeTokenDecimals);
128
+ const fee = new BN(BigNumber(params.fee).times(UNITS_PER_STAKE_TOKEN).toFixed(0));
129
+ const minimumStake = new BN(BigNumber(params.minimumStake).times(UNITS_PER_STAKE_TOKEN).toFixed(0));
130
+ const rewardSchemes = params.rewardSchemes.map((value) => {
131
+ return {
132
+ duration: new BN(value.duration),
133
+ reward: new BN(percentToBps(value.rewardRate)),
134
+ };
135
+ });
136
+ const instruction = await this.getUpdateLockupInstruction(updater, lockup, {
137
+ fee,
138
+ feeVault,
139
+ minimumStake,
140
+ rewardSchemes,
141
+ });
142
+ return this._createPayload(updater, [instruction]);
143
+ }
144
+ async getLockupInfo(lockupAddress) {
145
+ const lockupAccount = await this.program.account.lockup.fetchNullable(lockupAddress, this.connection.commitment);
146
+ if (!lockupAccount) {
147
+ return null;
148
+ }
149
+ const stakeTokenAddress = lockupAccount.stakedToken.tokenAddress;
150
+ const stakeTokenDecimals = await getMintDecimals(this.connection, stakeTokenAddress);
151
+ const UNITS_PER_STAKE_TOKEN = TEN_BIGNUM.pow(stakeTokenDecimals);
152
+ return {
153
+ address: lockupAddress,
154
+ feeInfo: {
155
+ fee: BigNumber(lockupAccount.feeInfo.fee.toString())
156
+ .div(UNITS_PER_STAKE_TOKEN)
157
+ .toFixed(),
158
+ feeVault: lockupAccount.feeInfo.feeVault,
159
+ },
160
+ rewardToken: {
161
+ tokenAddress: lockupAccount.rewardToken.tokenAddress,
162
+ },
163
+ stakeToken: {
164
+ tokenAddress: lockupAccount.stakedToken.tokenAddress,
165
+ totalStaked: BigNumber(lockupAccount.stakedToken.totalStaked.toString())
166
+ .div(UNITS_PER_STAKE_TOKEN)
167
+ .toFixed(),
168
+ },
169
+ stakeInfo: {
170
+ name: lockupAccount.stakeInfo.name,
171
+ creator: lockupAccount.stakeInfo.creator,
172
+ rewardSchemes: lockupAccount.stakeInfo.durationMap.map((value) => ({
173
+ duration: value.duration.toNumber(),
174
+ rewardRate: bpsToPercent(value.reward.toString()),
175
+ })),
176
+ minimumStake: BigNumber(lockupAccount.stakeInfo.minimumStake.toString())
177
+ .div(UNITS_PER_STAKE_TOKEN)
178
+ .toFixed(),
179
+ },
180
+ };
181
+ }
182
+ async getAllStakesCount(lockupAddress, commitment) {
183
+ const dataSize = this.program.account.userStakeData.size;
184
+ const accountInfos = await this.connection.getProgramAccounts(this.programId, {
185
+ commitment: commitment || this.connection.commitment || "finalized",
186
+ dataSlice: {
187
+ length: 0,
188
+ offset: 0,
189
+ },
190
+ filters: [
191
+ {
192
+ dataSize,
193
+ },
194
+ {
195
+ memcmp: {
196
+ bytes: lockupAddress.toString(),
197
+ offset: 81,
198
+ },
199
+ },
200
+ ],
201
+ });
202
+ return accountInfos.length;
203
+ }
204
+ async getStakeSignatureForStake(stakeInfo) {
205
+ const commitment = this.connection.commitment === "finalized" ? "finalized" : "confirmed";
206
+ const signatures = await this.connection.getSignaturesForAddress(translateAddress(stakeInfo.address), {}, commitment);
207
+ const stakeSignatures = signatures.filter((s) => {
208
+ return !s.err && (s.blockTime ?? 0) === stakeInfo.createdTime;
209
+ });
210
+ const signatureInfo = stakeSignatures[stakeSignatures.length - 1];
211
+ return signatureInfo ? signatureInfo.signature : null;
212
+ }
213
+ async getAllStakesInfo(lockupAddress) {
214
+ const lockupAccount = await this.program.account.lockup.fetchNullable(lockupAddress, this.connection.commitment);
215
+ if (!lockupAccount) {
216
+ throw new Error(`Lockup account does not exists for address: ${lockupAddress}`);
217
+ }
218
+ const stakeTokenAddress = lockupAccount.stakedToken.tokenAddress;
219
+ const rewardTokenAddress = lockupAccount.rewardToken.tokenAddress;
220
+ const stakeTokenDecimals = await getMintDecimals(this.connection, stakeTokenAddress);
221
+ const rewardTokenDecimals = await getMintDecimals(this.connection, rewardTokenAddress);
222
+ const UNITS_PER_STAKE_TOKEN = TEN_BIGNUM.pow(stakeTokenDecimals);
223
+ const UNITS_PER_REWARD_TOKEN = TEN_BIGNUM.pow(rewardTokenDecimals);
224
+ const dataSize = this.program.account.userStakeData.size;
225
+ const accountInfos = await this.connection.getProgramAccounts(this.programId, {
226
+ commitment: "finalized",
227
+ filters: [
228
+ {
229
+ dataSize,
230
+ },
231
+ {
232
+ memcmp: {
233
+ bytes: lockupAddress.toString(),
234
+ offset: 81,
235
+ },
236
+ },
237
+ ],
238
+ });
239
+ return accountInfos.map((accountInfo) => {
240
+ const stakeAccount = this.program.coder.accounts.decode(this.program.idl.accounts[2].name, accountInfo.account.data);
241
+ const info = {
242
+ address: accountInfo.pubkey.toString(),
243
+ nonce: BigInt(stakeAccount.nonce.toString()),
244
+ createdTime: stakeAccount.createdTime.toNumber(),
245
+ stakedAmount: BigNumber(stakeAccount.stakedAmount.toString())
246
+ .div(UNITS_PER_STAKE_TOKEN)
247
+ .toFixed(),
248
+ rewardAmount: BigNumber(stakeAccount.rewardAmount.toString())
249
+ .div(UNITS_PER_REWARD_TOKEN)
250
+ .toFixed(),
251
+ stakeClaimed: stakeAccount.stakeClaimed,
252
+ lockPeriod: stakeAccount.lockPeriod.toNumber(),
253
+ lockup: stakeAccount.lockup.toString(),
254
+ staker: stakeAccount.staker.toString(),
255
+ };
256
+ return info;
257
+ });
258
+ }
259
+ get programId() {
260
+ return this.program.programId;
261
+ }
262
+ get connection() {
263
+ return this.provider.connection;
264
+ }
265
+ }
@@ -0,0 +1,50 @@
1
+ import { type Address, Program, type Provider } from "@coral-xyz/anchor";
2
+ import { type Commitment, type Connection, PublicKey, type TransactionInstruction } from "@solana/web3.js";
3
+ import { TransactionPayload } from "@zebec-network/solana-common";
4
+ import BN from "bn.js";
5
+ import { type ZebecStreamIdl } from "../artifacts/index.js";
6
+ import type { InitializeStreamConfigParams, RpcNetwork, StreamConfigInfo, StreamMetadataInfo, TokenMetadata, UpdateStreamConfigParams, WhiteListTokensParams } from "../types.js";
7
+ export declare class ZebecStreamAdminService {
8
+ readonly streamConfigName: string;
9
+ readonly provider: Provider;
10
+ readonly network: RpcNetwork;
11
+ readonly program: Program<ZebecStreamIdl>;
12
+ constructor(streamConfigName: string, provider: Provider, network: RpcNetwork, program: Program<ZebecStreamIdl>);
13
+ static create(streamConfigName: string, provider: Provider, network: RpcNetwork): ZebecStreamAdminService;
14
+ private _createPayload;
15
+ get connection(): Connection;
16
+ get programId(): PublicKey;
17
+ getInitializeStreamConfigInstruction(admin: PublicKey, streamConfig: PublicKey, config: {
18
+ baseFee: BN;
19
+ frequencies: BN[];
20
+ platformFee: BN;
21
+ withdrawAccount: PublicKey;
22
+ configName: string;
23
+ feeTiers: {
24
+ minAmount: BN;
25
+ maxAmount: BN;
26
+ fee: BN;
27
+ }[];
28
+ feeVault: PublicKey;
29
+ }): Promise<TransactionInstruction>;
30
+ getUpdateStreamConfigInstruction(admin: PublicKey, streamConfig: PublicKey, config: {
31
+ baseFee: BN;
32
+ frequencies: BN[];
33
+ platformFee: BN;
34
+ withdrawAccount: PublicKey;
35
+ configName: string;
36
+ feeTiers: {
37
+ minAmount: BN;
38
+ maxAmount: BN;
39
+ fee: BN;
40
+ }[];
41
+ feeVault: PublicKey;
42
+ }): Promise<TransactionInstruction>;
43
+ getWhitelistTokensInstruction(admin: PublicKey, streamConfig: PublicKey, tokens: PublicKey[]): Promise<TransactionInstruction>;
44
+ initializeStreamConfig(params: InitializeStreamConfigParams): Promise<TransactionPayload>;
45
+ updateStreamConfig(params: UpdateStreamConfigParams): Promise<TransactionPayload>;
46
+ whiteListTokens(params: WhiteListTokensParams): Promise<TransactionPayload>;
47
+ getStreamConfigInfo(configName: string, commitment?: Commitment): Promise<StreamConfigInfo>;
48
+ getWhitelistedTokens(configName: string, commitment?: "processed" | "confirmed" | "finalized"): Promise<TokenMetadata[]>;
49
+ getStreamMetadataInfo(streamMetadata: Address, commitment?: Commitment): Promise<StreamMetadataInfo>;
50
+ }
@@ -0,0 +1,322 @@
1
+ import { AnchorProvider, Program, translateAddress, utils, } from "@coral-xyz/anchor";
2
+ import { fetchMetadata, MPL_TOKEN_METADATA_PROGRAM_ID, } from "@metaplex-foundation/mpl-token-metadata";
3
+ import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
4
+ import { fromWeb3JsPublicKey, toWeb3JsPublicKey, } from "@metaplex-foundation/umi-web3js-adapters";
5
+ import { PublicKey, } from "@solana/web3.js";
6
+ import { bpsToPercent, percentToBps } from "@zebec-network/core-utils";
7
+ import { getMintDecimals, TEN_BIGNUM, TransactionPayload, UNITS_PER_USDC, USDC_DECIMALS, } from "@zebec-network/solana-common";
8
+ import { BigNumber } from "bignumber.js";
9
+ import BN from "bn.js";
10
+ import { Buffer } from "buffer";
11
+ import { ZEBEC_STREAM_IDL } from "../artifacts/index.js";
12
+ import { deriveStreamConfigPda } from "../pda.js";
13
+ export class ZebecStreamAdminService {
14
+ streamConfigName;
15
+ provider;
16
+ network;
17
+ program;
18
+ constructor(streamConfigName, provider, network, program) {
19
+ this.streamConfigName = streamConfigName;
20
+ this.provider = provider;
21
+ this.network = network;
22
+ this.program = program;
23
+ }
24
+ static create(streamConfigName, provider, network) {
25
+ const connection = provider.connection;
26
+ const rpcEndpoint = connection.rpcEndpoint;
27
+ const connNetwork = rpcEndpoint.includes("devnet")
28
+ ? "devnet"
29
+ : rpcEndpoint.includes("testnet")
30
+ ? "testnet"
31
+ : "mainnet-beta";
32
+ if (connNetwork === "testnet") {
33
+ throw new Error("InvalidOperation: Testnet is not supported. Please use connection with devnet or mainnet-beta network.");
34
+ }
35
+ if (network !== connNetwork) {
36
+ throw new Error(`InvalidOperation: Network mismatch. network and connection network should be same. network: ${network}, connection: ${connNetwork}`);
37
+ }
38
+ const program = new Program(ZEBEC_STREAM_IDL, provider);
39
+ return new ZebecStreamAdminService(streamConfigName, provider, network, program);
40
+ }
41
+ async _createPayload(payerKey, instructions, signers, addressLookupTableAccounts) {
42
+ const errorMap = new Map();
43
+ this.program.idl.errors.map((error) => errorMap.set(error.code, error.msg));
44
+ const provider = this.provider;
45
+ let signTransaction;
46
+ if (provider instanceof AnchorProvider) {
47
+ signTransaction = async (tx) => {
48
+ return provider.wallet.signTransaction(tx);
49
+ };
50
+ }
51
+ return new TransactionPayload(this.connection, errorMap, {
52
+ instructions: instructions,
53
+ feePayer: payerKey,
54
+ signers: signers ?? [],
55
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
56
+ }, signTransaction);
57
+ }
58
+ get connection() {
59
+ return this.provider.connection;
60
+ }
61
+ get programId() {
62
+ return this.program.programId;
63
+ }
64
+ async getInitializeStreamConfigInstruction(admin, streamConfig, config) {
65
+ return this.program.methods
66
+ .initializeConfig({
67
+ baseFee: config.baseFee,
68
+ frequencies: config.frequencies,
69
+ platformFee: config.platformFee,
70
+ withdrawAccount: config.withdrawAccount,
71
+ configName: config.configName,
72
+ feeTier: config.feeTiers,
73
+ feeVault: config.feeVault,
74
+ })
75
+ .accountsPartial({
76
+ admin,
77
+ streamConfig,
78
+ })
79
+ .instruction();
80
+ }
81
+ async getUpdateStreamConfigInstruction(admin, streamConfig, config) {
82
+ return this.program.methods
83
+ .updateConfig({
84
+ baseFee: config.baseFee,
85
+ frequencies: config.frequencies,
86
+ platformFee: config.platformFee,
87
+ withdrawAccount: config.withdrawAccount,
88
+ configName: config.configName,
89
+ feeTier: config.feeTiers,
90
+ feeVault: config.feeVault,
91
+ })
92
+ .accountsPartial({
93
+ admin,
94
+ streamConfig,
95
+ })
96
+ .instruction();
97
+ }
98
+ async getWhitelistTokensInstruction(admin, streamConfig, tokens) {
99
+ return this.program.methods
100
+ .whitelistTokens({
101
+ tokens,
102
+ })
103
+ .accountsPartial({
104
+ admin,
105
+ streamConfig,
106
+ })
107
+ .instruction();
108
+ }
109
+ async initializeStreamConfig(params) {
110
+ const admin = params.admin
111
+ ? translateAddress(params.admin)
112
+ : this.provider.publicKey;
113
+ if (!admin) {
114
+ throw new Error("Either provide admin or create provider with public key");
115
+ }
116
+ const [streamConfig] = deriveStreamConfigPda(this.streamConfigName, this.programId);
117
+ const baseFee = new BN(percentToBps(params.config.baseFeePercent));
118
+ const frequencies = params.config.frequencies.map((frequency) => new BN(frequency));
119
+ const platformFee = new BN(percentToBps(params.config.platformFeePercent));
120
+ const withdrawAccount = translateAddress(params.config.withdrawAccount);
121
+ const feeVault = translateAddress(params.config.feeVault);
122
+ const feeTiers = params.config.feeTiers.map((tier) => {
123
+ return {
124
+ minAmount: new BN(BigNumber(tier.minThreshold)
125
+ .times(TEN_BIGNUM.pow(USDC_DECIMALS))
126
+ .toFixed(0)),
127
+ maxAmount: new BN(BigNumber(tier.maxThreshold)
128
+ .times(TEN_BIGNUM.pow(USDC_DECIMALS))
129
+ .toFixed(0)),
130
+ fee: new BN(percentToBps(tier.feeRateInPercent)),
131
+ };
132
+ });
133
+ const ix = await this.getInitializeStreamConfigInstruction(admin, streamConfig, {
134
+ baseFee,
135
+ frequencies,
136
+ platformFee,
137
+ withdrawAccount,
138
+ feeTiers,
139
+ configName: this.streamConfigName,
140
+ feeVault,
141
+ });
142
+ return this._createPayload(admin, [ix]);
143
+ }
144
+ async updateStreamConfig(params) {
145
+ const admin = params.admin
146
+ ? translateAddress(params.admin)
147
+ : this.provider.publicKey;
148
+ if (!admin) {
149
+ throw new Error("Either provide admin or create provider with public key");
150
+ }
151
+ const [streamConfig] = deriveStreamConfigPda(this.streamConfigName, this.programId);
152
+ const baseFee = new BN(percentToBps(params.config.baseFeePercent));
153
+ const frequencies = params.config.frequencies.map((frequency) => new BN(frequency));
154
+ const platformFee = new BN(percentToBps(params.config.platformFeePercent));
155
+ const withdrawAccount = translateAddress(params.config.withdrawAccount);
156
+ const feeVault = translateAddress(params.config.feeVault);
157
+ const feeTiers = params.config.feeTiers.map((tier) => {
158
+ return {
159
+ minAmount: new BN(BigNumber(tier.minThreshold)
160
+ .times(TEN_BIGNUM.pow(USDC_DECIMALS))
161
+ .toFixed(0)),
162
+ maxAmount: new BN(BigNumber(tier.maxThreshold)
163
+ .times(TEN_BIGNUM.pow(USDC_DECIMALS))
164
+ .toFixed(0)),
165
+ fee: new BN(percentToBps(tier.feeRateInPercent)),
166
+ };
167
+ });
168
+ const ix = await this.getUpdateStreamConfigInstruction(admin, streamConfig, {
169
+ baseFee,
170
+ frequencies,
171
+ platformFee,
172
+ withdrawAccount,
173
+ feeTiers,
174
+ configName: this.streamConfigName,
175
+ feeVault,
176
+ });
177
+ return this._createPayload(admin, [ix]);
178
+ }
179
+ async whiteListTokens(params) {
180
+ const admin = translateAddress(params.admin);
181
+ const [streamConfig] = deriveStreamConfigPda(this.streamConfigName, this.programId);
182
+ const tokens = params.tokens.map((token) => translateAddress(token));
183
+ const ix = await this.getWhitelistTokensInstruction(admin, streamConfig, tokens);
184
+ return this._createPayload(admin, [ix]);
185
+ }
186
+ async getStreamConfigInfo(configName, commitment) {
187
+ const [config] = deriveStreamConfigPda(configName, this.programId);
188
+ const configInfo = await this.program.account.streamConfig.fetch(config, commitment ?? this.connection.commitment);
189
+ return {
190
+ address: config,
191
+ configName: configInfo.configName,
192
+ admin: configInfo.admin,
193
+ withdrawerAccount: configInfo.withdrawAccount,
194
+ whitelistedTokens: configInfo.whitelistedTokens,
195
+ platformFee: Number(bpsToPercent(configInfo.platformFee.toNumber())),
196
+ baseFee: Number(bpsToPercent(configInfo.baseFee.toNumber())),
197
+ frequencies: configInfo.frequencies.map((frequency) => frequency.toNumber()),
198
+ feeTiers: configInfo.feeTiers.tiers.map((tier) => ({
199
+ feeRateInPercent: bpsToPercent(tier.fee.toNumber()),
200
+ minThreshold: BigNumber(tier.minAmount.toString())
201
+ .div(UNITS_PER_USDC)
202
+ .toFixed(),
203
+ maxThreshold: BigNumber(tier.maxAmount.toString())
204
+ .div(UNITS_PER_USDC)
205
+ .toFixed(),
206
+ })),
207
+ feeVault: configInfo.feeVault,
208
+ };
209
+ }
210
+ async getWhitelistedTokens(configName, commitment) {
211
+ const [config] = deriveStreamConfigPda(configName, this.programId);
212
+ const configInfo = await this.program.account.streamConfig.fetch(config, commitment ?? this.connection.commitment);
213
+ const whitelistedTokens = configInfo.whitelistedTokens;
214
+ const umi = createUmi(this.connection.rpcEndpoint, commitment);
215
+ return Promise.all(whitelistedTokens.map(async (mint) => {
216
+ const mplProgramId = toWeb3JsPublicKey(MPL_TOKEN_METADATA_PROGRAM_ID);
217
+ const [metadata] = PublicKey.findProgramAddressSync([Buffer.from("metadata"), mplProgramId.toBytes(), mint.toBytes()], mplProgramId);
218
+ const mintInfo = await this.connection.getParsedAccountInfo(mint, commitment);
219
+ if (!mintInfo.value) {
220
+ throw new Error(`Failed to fetch mint account: ${mint.toString()}`);
221
+ }
222
+ // Check if the account is a valid mint account
223
+ const mintData = mintInfo.value.data;
224
+ if (Buffer.isBuffer(mintData) || mintData.parsed.type !== "mint") {
225
+ throw new Error(`The provided address is not a valid mint account: ${mint.toString()}`);
226
+ }
227
+ try {
228
+ const metadataInfo = await fetchMetadata(umi, fromWeb3JsPublicKey(metadata));
229
+ return {
230
+ mint,
231
+ decimals: Number(mintData.parsed.info.decimals),
232
+ freezeAuthority: mintData.parsed.info.freezeAuthority
233
+ ? new PublicKey(mintData.parsed.info.freezeAuthority)
234
+ : null,
235
+ supply: BigNumber(mintData.parsed.info.supply)
236
+ .div(TEN_BIGNUM.pow(mintData.parsed.info.decimals))
237
+ .toFixed(),
238
+ isInitialized: Boolean(mintData.parsed.info.isInitialized),
239
+ mintAuthority: mintData.parsed.info.mintAuthority
240
+ ? new PublicKey(mintData.parsed.info.mintAuthority)
241
+ : null,
242
+ metadata: {
243
+ address: metadata,
244
+ updateAuthority: toWeb3JsPublicKey(metadataInfo.updateAuthority),
245
+ name: metadataInfo.name,
246
+ symbol: metadataInfo.symbol,
247
+ uri: metadataInfo.uri,
248
+ },
249
+ };
250
+ }
251
+ catch (error) {
252
+ if (error instanceof Error &&
253
+ error.message.includes("The account of type [Metadata] was not found at the provided address")) {
254
+ console.warn(`Failed to fetch metadata for mint: ${mint.toBase58()}`, error);
255
+ return {
256
+ mint,
257
+ decimals: Number(mintData.parsed.info.decimals),
258
+ freezeAuthority: mintData.parsed.info.freezeAuthority
259
+ ? new PublicKey(mintData.parsed.info.freezeAuthority)
260
+ : null,
261
+ supply: BigNumber(mintData.parsed.info.supply)
262
+ .div(TEN_BIGNUM.pow(mintData.parsed.info.decimals))
263
+ .toFixed(),
264
+ isInitialized: Boolean(mintData.parsed.info.isInitialized),
265
+ mintAuthority: mintData.parsed.info.mintAuthority
266
+ ? new PublicKey(mintData.parsed.info.mintAuthority)
267
+ : null,
268
+ metadata: null,
269
+ };
270
+ }
271
+ throw error;
272
+ }
273
+ }));
274
+ }
275
+ async getStreamMetadataInfo(streamMetadata, commitment) {
276
+ const metadataInfo = await this.program.account.paymentStream.fetch(translateAddress(streamMetadata), commitment ?? this.connection.commitment);
277
+ const streamToken = metadataInfo.financials.streamToken;
278
+ const streamTokenDecimals = await getMintDecimals(this.connection, streamToken);
279
+ const unitsPerStreamToken = TEN_BIGNUM.pow(streamTokenDecimals);
280
+ const depositedAmount = BigNumber(metadataInfo.financials.depositedAmount.toString())
281
+ .div(unitsPerStreamToken)
282
+ .toFixed();
283
+ const withdrawnAmount = BigNumber(metadataInfo.financials.withdrawnAmount.toString())
284
+ .div(unitsPerStreamToken)
285
+ .toFixed();
286
+ const cliffPercentage = Number(bpsToPercent(metadataInfo.financials.cliffPercentage.toNumber()));
287
+ return {
288
+ address: translateAddress(streamMetadata),
289
+ parties: {
290
+ sender: metadataInfo.parties.sender,
291
+ receiver: metadataInfo.parties.receiver,
292
+ },
293
+ financials: {
294
+ streamToken,
295
+ cliffPercentage,
296
+ depositedAmount,
297
+ withdrawnAmount,
298
+ },
299
+ schedule: {
300
+ startTime: metadataInfo.schedule.startTime.toNumber(),
301
+ endTime: metadataInfo.schedule.endTime.toNumber(),
302
+ lastWithdrawTime: metadataInfo.schedule.lastWithdrawTime.toNumber(),
303
+ frequency: metadataInfo.schedule.frequency.toNumber(),
304
+ duration: metadataInfo.schedule.duration.toNumber(),
305
+ pausedTimestamp: metadataInfo.schedule.pausedTimestamp.toNumber(),
306
+ pausedInterval: metadataInfo.schedule.pausedInterval.toNumber(),
307
+ canceledTimestamp: metadataInfo.schedule.canceledTimestamp.toNumber(),
308
+ },
309
+ permissions: {
310
+ cancelableBySender: Boolean(metadataInfo.permissions.cancelableBySender),
311
+ cancelableByRecipient: Boolean(metadataInfo.permissions.cancelableBySender),
312
+ automaticWithdrawal: Boolean(metadataInfo.permissions.automaticWithdrawal),
313
+ transferableBySender: Boolean(metadataInfo.permissions.transferableBySender),
314
+ transferableByRecipient: Boolean(metadataInfo.permissions.transferableByRecipient),
315
+ canTopup: Boolean(metadataInfo.permissions.canTopup),
316
+ isPausable: Boolean(metadataInfo.permissions.isPausable),
317
+ rateUpdatable: Boolean(metadataInfo.permissions.rateUpdatable),
318
+ },
319
+ streamName: utils.bytes.utf8.decode(Uint8Array.from(metadataInfo.streamName).filter((byte) => byte !== 0)), // Remove padding zeros
320
+ };
321
+ }
322
+ }