@sign-global/tokentable 0.0.4 → 0.0.6

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 (125) hide show
  1. package/dist/cjs/airdrop/core/ton/AirdropClientV21.d.ts +29 -0
  2. package/dist/cjs/airdrop/core/ton/AirdropClientV21.js +112 -0
  3. package/dist/cjs/airdrop/core/ton/AirdropClientV21.js.map +1 -0
  4. package/dist/cjs/airdrop/core/ton/AirdropService.d.ts +11 -2
  5. package/dist/cjs/airdrop/core/ton/AirdropService.js +15 -1
  6. package/dist/cjs/airdrop/core/ton/AirdropService.js.map +1 -1
  7. package/dist/cjs/airdrop/core/ton/v21/build/Leaf.compiled.json +1 -0
  8. package/dist/cjs/airdrop/core/ton/v21/utils/constants.d.ts +44 -0
  9. package/dist/cjs/airdrop/core/ton/v21/utils/constants.js +51 -0
  10. package/dist/cjs/airdrop/core/ton/v21/utils/constants.js.map +1 -0
  11. package/dist/cjs/airdrop/core/ton/v21/utils/date.utils.d.ts +3 -0
  12. package/dist/cjs/airdrop/core/ton/v21/utils/date.utils.js +15 -0
  13. package/dist/cjs/airdrop/core/ton/v21/utils/date.utils.js.map +1 -0
  14. package/dist/cjs/airdrop/core/ton/v21/utils/index.d.ts +3 -0
  15. package/dist/cjs/airdrop/core/ton/v21/utils/index.js +20 -0
  16. package/dist/cjs/airdrop/core/ton/v21/utils/index.js.map +1 -0
  17. package/dist/cjs/airdrop/core/ton/v21/utils/ton.utils.d.ts +17 -0
  18. package/dist/cjs/airdrop/core/ton/v21/utils/ton.utils.js +87 -0
  19. package/dist/cjs/airdrop/core/ton/v21/utils/ton.utils.js.map +1 -0
  20. package/dist/cjs/airdrop/core/ton/v21/wrappers/Leaf.d.ts +24 -0
  21. package/dist/cjs/airdrop/core/ton/v21/wrappers/Leaf.js +60 -0
  22. package/dist/cjs/airdrop/core/ton/v21/wrappers/Leaf.js.map +1 -0
  23. package/dist/cjs/airdrop/core/ton/v21/wrappers/MerkleTokenDistributor.d.ts +66 -0
  24. package/dist/cjs/airdrop/core/ton/v21/wrappers/MerkleTokenDistributor.js +255 -0
  25. package/dist/cjs/airdrop/core/ton/v21/wrappers/MerkleTokenDistributor.js.map +1 -0
  26. package/dist/cjs/airdrop/core/ton/v21/wrappers/index.d.ts +2 -0
  27. package/dist/cjs/airdrop/core/ton/v21/wrappers/index.js +19 -0
  28. package/dist/cjs/airdrop/core/ton/v21/wrappers/index.js.map +1 -0
  29. package/dist/cjs/airdrop/index.d.ts +10 -12
  30. package/dist/cjs/airdrop/index.js +115 -110
  31. package/dist/cjs/airdrop/index.js.map +1 -1
  32. package/dist/cjs/airdrop/services/index.d.ts +6 -1
  33. package/dist/cjs/airdrop/services/index.js +6 -1
  34. package/dist/cjs/airdrop/services/index.js.map +1 -1
  35. package/dist/cjs/airdrop/strategies/AirdropStrategyFactory.d.ts +13 -0
  36. package/dist/cjs/airdrop/strategies/AirdropStrategyFactory.js +22 -0
  37. package/dist/cjs/airdrop/strategies/AirdropStrategyFactory.js.map +1 -0
  38. package/dist/cjs/airdrop/strategies/EvmAirdropStrategy.d.ts +26 -0
  39. package/dist/cjs/airdrop/strategies/EvmAirdropStrategy.js +79 -0
  40. package/dist/cjs/airdrop/strategies/EvmAirdropStrategy.js.map +1 -0
  41. package/dist/cjs/airdrop/strategies/IAirdropStrategy.d.ts +27 -0
  42. package/dist/cjs/airdrop/strategies/IAirdropStrategy.js +3 -0
  43. package/dist/cjs/airdrop/strategies/IAirdropStrategy.js.map +1 -0
  44. package/dist/cjs/airdrop/strategies/StrategyCache.d.ts +10 -0
  45. package/dist/cjs/airdrop/strategies/StrategyCache.js +32 -0
  46. package/dist/cjs/airdrop/strategies/StrategyCache.js.map +1 -0
  47. package/dist/cjs/airdrop/strategies/TonAirdropStrategy.d.ts +26 -0
  48. package/dist/cjs/airdrop/strategies/TonAirdropStrategy.js +64 -0
  49. package/dist/cjs/airdrop/strategies/TonAirdropStrategy.js.map +1 -0
  50. package/dist/cjs/airdrop/types/index.d.ts +7 -1
  51. package/dist/cjs/airdrop/types/index.js +1 -0
  52. package/dist/cjs/airdrop/types/index.js.map +1 -1
  53. package/dist/esm/airdrop/core/ton/AirdropClientV21.d.ts +29 -0
  54. package/dist/esm/airdrop/core/ton/AirdropClientV21.js +105 -0
  55. package/dist/esm/airdrop/core/ton/AirdropClientV21.js.map +1 -0
  56. package/dist/esm/airdrop/core/ton/AirdropService.d.ts +11 -2
  57. package/dist/esm/airdrop/core/ton/AirdropService.js +15 -1
  58. package/dist/esm/airdrop/core/ton/AirdropService.js.map +1 -1
  59. package/dist/esm/airdrop/core/ton/v21/build/Leaf.compiled.json +1 -0
  60. package/dist/esm/airdrop/core/ton/v21/utils/constants.d.ts +44 -0
  61. package/dist/esm/airdrop/core/ton/v21/utils/constants.js +48 -0
  62. package/dist/esm/airdrop/core/ton/v21/utils/constants.js.map +1 -0
  63. package/dist/esm/airdrop/core/ton/v21/utils/date.utils.d.ts +3 -0
  64. package/dist/esm/airdrop/core/ton/v21/utils/date.utils.js +10 -0
  65. package/dist/esm/airdrop/core/ton/v21/utils/date.utils.js.map +1 -0
  66. package/dist/esm/airdrop/core/ton/v21/utils/index.d.ts +3 -0
  67. package/dist/esm/airdrop/core/ton/v21/utils/index.js +4 -0
  68. package/dist/esm/airdrop/core/ton/v21/utils/index.js.map +1 -0
  69. package/dist/esm/airdrop/core/ton/v21/utils/ton.utils.d.ts +17 -0
  70. package/dist/esm/airdrop/core/ton/v21/utils/ton.utils.js +69 -0
  71. package/dist/esm/airdrop/core/ton/v21/utils/ton.utils.js.map +1 -0
  72. package/dist/esm/airdrop/core/ton/v21/wrappers/Leaf.d.ts +24 -0
  73. package/dist/esm/airdrop/core/ton/v21/wrappers/Leaf.js +55 -0
  74. package/dist/esm/airdrop/core/ton/v21/wrappers/Leaf.js.map +1 -0
  75. package/dist/esm/airdrop/core/ton/v21/wrappers/MerkleTokenDistributor.d.ts +66 -0
  76. package/dist/esm/airdrop/core/ton/v21/wrappers/MerkleTokenDistributor.js +249 -0
  77. package/dist/esm/airdrop/core/ton/v21/wrappers/MerkleTokenDistributor.js.map +1 -0
  78. package/dist/esm/airdrop/core/ton/v21/wrappers/index.d.ts +2 -0
  79. package/dist/esm/airdrop/core/ton/v21/wrappers/index.js +3 -0
  80. package/dist/esm/airdrop/core/ton/v21/wrappers/index.js.map +1 -0
  81. package/dist/esm/airdrop/index.d.ts +10 -12
  82. package/dist/esm/airdrop/index.js +117 -112
  83. package/dist/esm/airdrop/index.js.map +1 -1
  84. package/dist/esm/airdrop/services/index.d.ts +6 -1
  85. package/dist/esm/airdrop/services/index.js +4 -0
  86. package/dist/esm/airdrop/services/index.js.map +1 -1
  87. package/dist/esm/airdrop/strategies/AirdropStrategyFactory.d.ts +13 -0
  88. package/dist/esm/airdrop/strategies/AirdropStrategyFactory.js +18 -0
  89. package/dist/esm/airdrop/strategies/AirdropStrategyFactory.js.map +1 -0
  90. package/dist/esm/airdrop/strategies/EvmAirdropStrategy.d.ts +26 -0
  91. package/dist/esm/airdrop/strategies/EvmAirdropStrategy.js +75 -0
  92. package/dist/esm/airdrop/strategies/EvmAirdropStrategy.js.map +1 -0
  93. package/dist/esm/airdrop/strategies/IAirdropStrategy.d.ts +27 -0
  94. package/dist/esm/airdrop/strategies/IAirdropStrategy.js +2 -0
  95. package/dist/esm/airdrop/strategies/IAirdropStrategy.js.map +1 -0
  96. package/dist/esm/airdrop/strategies/StrategyCache.d.ts +10 -0
  97. package/dist/esm/airdrop/strategies/StrategyCache.js +28 -0
  98. package/dist/esm/airdrop/strategies/StrategyCache.js.map +1 -0
  99. package/dist/esm/airdrop/strategies/TonAirdropStrategy.d.ts +26 -0
  100. package/dist/esm/airdrop/strategies/TonAirdropStrategy.js +60 -0
  101. package/dist/esm/airdrop/strategies/TonAirdropStrategy.js.map +1 -0
  102. package/dist/esm/airdrop/types/index.d.ts +7 -1
  103. package/dist/esm/airdrop/types/index.js +1 -0
  104. package/dist/esm/airdrop/types/index.js.map +1 -1
  105. package/package.json +1 -1
  106. package/src/airdrop/core/ton/AirdropClientV21.ts +131 -0
  107. package/src/airdrop/core/ton/AirdropService.ts +18 -1
  108. package/src/airdrop/core/ton/v21/build/Leaf.compiled.json +1 -0
  109. package/src/airdrop/core/ton/v21/build/MerkleTokenDistributor.compiled.json +1 -0
  110. package/src/airdrop/core/ton/v21/utils/constants.ts +48 -0
  111. package/src/airdrop/core/ton/v21/utils/date.utils.ts +11 -0
  112. package/src/airdrop/core/ton/v21/utils/index.ts +3 -0
  113. package/src/airdrop/core/ton/v21/utils/ton.utils.ts +84 -0
  114. package/src/airdrop/core/ton/v21/wrappers/Leaf.ts +69 -0
  115. package/src/airdrop/core/ton/v21/wrappers/MerkleTokenDistributor.ts +346 -0
  116. package/src/airdrop/core/ton/v21/wrappers/index.ts +2 -0
  117. package/src/airdrop/index.ts +140 -139
  118. package/src/airdrop/services/index.ts +6 -1
  119. package/src/airdrop/strategies/AirdropStrategyFactory.ts +29 -0
  120. package/src/airdrop/strategies/EvmAirdropStrategy.ts +85 -0
  121. package/src/airdrop/strategies/IAirdropStrategy.ts +25 -0
  122. package/src/airdrop/strategies/StrategyCache.ts +34 -0
  123. package/src/airdrop/strategies/TonAirdropStrategy.ts +72 -0
  124. package/src/airdrop/tests/index.test.ts +100 -0
  125. package/src/airdrop/types/index.ts +8 -1
@@ -1,20 +1,17 @@
1
1
  import { TonClient } from '@ton/ton';
2
- import { TonConnectUI } from '@tonconnect/ui-react';
3
- import { AirdropService as TonAirdropService } from './core/ton/AirdropService';
4
- import { AirdropService as EvmAirdropService } from './core/evm/AirdropService';
5
- import { getAirdropPro, getAirdropProject, getAirdropQuery } from './services';
2
+ import { getAirdropPro, getAirdropProject, getAirdropQuery, getAirdropContractConfig } from './services';
6
3
  import {
7
- ChainId,
8
4
  ChainType,
9
- ETokenType,
10
- EvmAirdropVersionEnum,
11
5
  IAirdropClaim,
12
6
  IAirdropOptions,
7
+ IContractConfig,
13
8
  IProject,
14
9
  IWalletClient
15
10
  } from './types';
16
11
  import { getTonProjectIdByAddress } from './common';
17
- import { WalletClient as ViemWalletClient } from 'viem';
12
+ import { AirdropStrategyFactory } from './strategies/AirdropStrategyFactory';
13
+ import { IAirdropStrategy } from './strategies/IAirdropStrategy';
14
+ import { strategyCache } from './strategies/StrategyCache';
18
15
 
19
16
  export class AirdropClient {
20
17
  private tonClient?: TonClient;
@@ -23,11 +20,9 @@ export class AirdropClient {
23
20
  private endpoint?: string;
24
21
  private project: IProject | undefined;
25
22
  protected claims?: IAirdropClaim[];
26
- private contractInstanceWithWallet: TonAirdropService | EvmAirdropService | undefined;
27
- private contractInstanceWithoutWallet: TonAirdropService | EvmAirdropService | undefined;
28
23
  private projectPromise: Promise<void> | undefined;
29
24
  private claimHook: ((v: any) => any) | undefined;
30
- private cachedVersion: string | null = null;
25
+ private contractConfig: IContractConfig | null = null;
31
26
 
32
27
  constructor(params: IAirdropOptions) {
33
28
  if ('slug' in params) {
@@ -39,21 +34,41 @@ export class AirdropClient {
39
34
  this.projectPromise = this.initializeProjects();
40
35
  }
41
36
 
42
- protected async initializeProjects(address?: string) {
43
- let projectId: string | undefined = this.projectId;
44
- if (this.slug) {
45
- const res = await getAirdropPro(this.slug, this.endpoint);
46
- const projectMap = res?.childProjectMapping || {};
37
+ private async fetchProjectFromSlug(address?: string): Promise<string | undefined> {
38
+ if (!this.slug) return undefined;
39
+
40
+ const res = await getAirdropPro(this.slug, this.endpoint);
41
+ const projectMap = res?.childProjectMapping || {};
42
+
43
+ return getTonProjectIdByAddress({
44
+ address,
45
+ slug: this.slug,
46
+ data: projectMap
47
+ });
48
+ }
47
49
 
48
- let legacyProjectId: string | undefined;
49
- legacyProjectId = getTonProjectIdByAddress({ address, slug: this.slug, data: projectMap });
50
+ private async fetchProjectDetails(projectId: string): Promise<void> {
51
+ this.project = await getAirdropProject(projectId, this.endpoint);
52
+ }
50
53
 
51
- if (legacyProjectId) {
52
- projectId = legacyProjectId;
54
+ protected async initializeProjects(address?: string) {
55
+ try {
56
+ let projectId = this.projectId;
57
+
58
+ // If slug is provided, try to fetch project ID from slug
59
+ if (!projectId) {
60
+ projectId = await this.fetchProjectFromSlug(address);
53
61
  }
62
+
63
+ if (!projectId) {
64
+ throw new Error('Project ID is required and could not be determined');
65
+ }
66
+
67
+ // Fetch project details
68
+ await this.fetchProjectDetails(projectId);
69
+ } catch (error: any) {
70
+ throw new Error(`Failed to initialize project: ${error.message}`);
54
71
  }
55
- if (!projectId) throw new Error('Project ID is required');
56
- this.project = await getAirdropProject(projectId, this.endpoint);
57
72
  }
58
73
 
59
74
  async getProjectInfo() {
@@ -82,46 +97,45 @@ export class AirdropClient {
82
97
  return this.claims;
83
98
  }
84
99
 
85
- async getAirdropContractWrapper(options?: {
86
- walletClient?: IWalletClient;
87
- }): Promise<TonAirdropService | EvmAirdropService> {
100
+ public async getStrategy(walletClient?: IWalletClient): Promise<IAirdropStrategy> {
88
101
  const project = await this.getProjectInfo();
102
+ const hasWallet = !!walletClient;
103
+
104
+ // Try to get cached strategy
105
+ let strategy = strategyCache.getStrategy(
106
+ project.contractAddress,
107
+ project.chainId,
108
+ project.chainType,
109
+ hasWallet
110
+ );
89
111
 
90
- if (options?.walletClient) {
91
- if (!this.contractInstanceWithWallet) {
92
- const instance =
93
- project.chainType === ChainType.Evm
94
- ? new EvmAirdropService({
95
- contractAddress: project.contractAddress,
96
- chainId: Number(project.chainId) as ChainId,
97
- walletClient: options.walletClient as ViemWalletClient
98
- })
99
- : new TonAirdropService({
100
- chainId: project.chainId.toString(),
101
- address: project.contractAddress,
102
- walletClient: options.walletClient as TonConnectUI,
103
- tonClient: this.tonClient
104
- });
105
- this.contractInstanceWithWallet = instance;
106
- }
107
- return this.contractInstanceWithWallet;
108
- } else {
109
- if (!this.contractInstanceWithoutWallet) {
110
- const instance =
111
- project.chainType === ChainType.Evm
112
- ? new EvmAirdropService({
113
- contractAddress: project.contractAddress,
114
- chainId: Number(project.chainId) as ChainId
115
- })
116
- : new TonAirdropService({
117
- chainId: project.chainId.toString(),
118
- address: project.contractAddress,
119
- tonClient: this.tonClient
120
- });
121
- this.contractInstanceWithoutWallet = instance;
122
- }
123
- return this.contractInstanceWithoutWallet;
112
+ if (!strategy) {
113
+ // Create new strategy if not cached
114
+ strategy = AirdropStrategyFactory.createStrategy({
115
+ chainType: project.chainType,
116
+ contractAddress: project.contractAddress,
117
+ chainId: project.chainId,
118
+ walletClient,
119
+ tonClient: this.tonClient
120
+ });
121
+
122
+ // Initialize the strategy
123
+ await strategy.initialize(walletClient);
124
+
125
+ // Cache the initialized strategy
126
+ strategyCache.setStrategy(
127
+ project.contractAddress,
128
+ project.chainId,
129
+ project.chainType,
130
+ strategy,
131
+ hasWallet
132
+ );
133
+ } else if (hasWallet) {
134
+ // If we have a wallet and the strategy exists, just initialize with the new wallet
135
+ await strategy.initialize(walletClient);
124
136
  }
137
+
138
+ return strategy;
125
139
  }
126
140
 
127
141
  private getClaimDataById(claimId: string) {
@@ -139,115 +153,102 @@ export class AirdropClient {
139
153
  options?: { onLoading?: () => void }
140
154
  ) {
141
155
  const claimData = this.getClaimDataById(claimId);
142
- const project = await this.getProjectInfo();
143
-
144
- const contractWrapper = await this.getAirdropContractWrapper({
145
- walletClient
146
- });
147
-
148
- if (project.chainType === ChainType.Evm) {
149
- const version = await contractWrapper.getVersion();
150
- const needsFeeData = !(
151
- project.tokenType === ETokenType.Native &&
152
- [EvmAirdropVersionEnum.V3, EvmAirdropVersionEnum.V4].includes(version as EvmAirdropVersionEnum)
153
- );
154
-
155
- const value = needsFeeData ? await this.getClaimFee(BigInt(claimData.amount)) : undefined;
156
-
157
- let extraData = {
158
- isLegacy: true,
159
- attestationId: BigInt(1)
160
- };
161
- if (this.claimHook) {
162
- extraData = this.claimHook(claimData);
163
- }
156
+ const strategy = await this.getStrategy(walletClient);
164
157
 
165
- return (contractWrapper as EvmAirdropService).claim({
166
- proof: claimData.proof,
167
- group: claimData.group,
168
- data: claimData.data,
169
- value: value as bigint | undefined,
170
- extraData: extraData
171
- });
158
+ let extraData = {
159
+ isLegacy: true,
160
+ attestationId: BigInt(0)
161
+ }
162
+ if (this.claimHook) {
163
+ extraData = this.claimHook(claimData);
172
164
  }
173
165
 
174
- return (contractWrapper as TonAirdropService).claim(claimData, options);
166
+ return strategy.claim(claimData, options);
175
167
  }
176
168
 
177
169
  async checkClaimed(claimId: string): Promise<boolean> {
178
170
  const claimData = this.getClaimDataById(claimId);
179
- const project = await this.getProjectInfo();
171
+ const strategy = await this.getStrategy();
172
+ return strategy.checkClaimed(claimData);
173
+ }
180
174
 
181
- const contractWrapper = await this.getAirdropContractWrapper();
175
+ private async getContractConfig(): Promise<IContractConfig | null> {
176
+ try {
177
+ const project = await this.getProjectInfo();
182
178
 
183
- if (project.chainType === ChainType.Evm) {
184
- return (contractWrapper as EvmAirdropService).isLeafUsed(claimData.leaf);
179
+ if (!this.contractConfig) {
180
+ this.contractConfig = await getAirdropContractConfig({
181
+ chainId: project.chainId,
182
+ chainType: project.chainType,
183
+ contractAddress: project.contractAddress
184
+ }, this.endpoint);
185
+ }
186
+ return this.contractConfig;
187
+ } catch (error) {
188
+ console.error('Error fetching contract config:', error);
189
+ return null;
185
190
  }
186
-
187
- return (contractWrapper as TonAirdropService).checkClaimed(claimData);
188
191
  }
189
192
 
190
- async getPaused() {
191
- const project = await this.getProjectInfo();
192
- const contractWrapper = await this.getAirdropContractWrapper();
193
- if (project.chainType === ChainType.Evm) {
194
- throw new Error('EVM chain is not supported');
193
+ async getVersion(): Promise<string> {
194
+ const config = await this.getContractConfig();
195
+ if (config?.version) {
196
+ return config.version;
195
197
  }
196
- return (contractWrapper as TonAirdropService).getPaused();
198
+
199
+ const strategy = await this.getStrategy();
200
+ return strategy.getVersion();
197
201
  }
198
202
 
199
- async getVersion() {
200
- if (this.cachedVersion) {
201
- return this.cachedVersion;
203
+ async getPaused(): Promise<boolean> {
204
+ const config = await this.getContractConfig();
205
+ if (config && config?.paused !== null) {
206
+ return config.paused;
202
207
  }
203
- const contractWrapper = await this.getAirdropContractWrapper();
204
208
 
205
- this.cachedVersion = await contractWrapper?.getVersion();
206
- return this.cachedVersion;
209
+ const strategy = await this.getStrategy();
210
+ if (!strategy.getPaused) {
211
+ throw new Error('getPaused is not supported for this chain');
212
+ }
213
+ return strategy.getPaused();
207
214
  }
208
215
 
209
- async batchCheckClaimed() {
210
- const project = await this.getProjectInfo();
216
+ async getClaimFee(amount?: bigint): Promise<string | bigint> {
217
+ const config = await this.getContractConfig();
218
+ if (config?.claimFee) {
219
+ return config.claimFee;
220
+ }
211
221
 
212
- if (project.chainType === ChainType.Ton) {
213
- throw new Error('TON chain is not supported');
222
+ const strategy = await this.getStrategy();
223
+ if (!strategy.getClaimFee) {
224
+ throw new Error('getClaimFee is not supported for this chain');
214
225
  }
226
+ return strategy.getClaimFee(this.project?.contractAddress || '', amount || BigInt(0));
227
+ }
215
228
 
216
- const contractWrapper = await this.getAirdropContractWrapper();
229
+ async batchCheckClaimed(): Promise<{ result: boolean; status: string }[]> {
230
+ const strategy = await this.getStrategy();
231
+ if (!strategy.batchCheckClaimed) {
232
+ throw new Error('batchCheckClaimed is not supported for this chain');
233
+ }
217
234
  const leafs = this.claims?.map((it) => it.leaf);
218
-
219
235
  if (!leafs) return [];
220
-
221
- return (contractWrapper as EvmAirdropService).batchFetchClaimed(leafs);
236
+ return strategy.batchCheckClaimed(leafs);
222
237
  }
223
238
 
224
- async getNFT() {
225
- const project = await this.getProjectInfo();
226
-
227
- if (project.chainType === ChainType.Ton) {
228
- throw new Error('TON chain is not supported');
239
+ async getNFT(): Promise<string> {
240
+ const strategy = await this.getStrategy();
241
+ if (!strategy.getNFT) {
242
+ throw new Error('getNFT is not supported for this chain');
229
243
  }
230
-
231
- const contractWrapper = await this.getAirdropContractWrapper();
232
-
233
- return (contractWrapper as EvmAirdropService).getNFT();
244
+ return strategy.getNFT();
234
245
  }
235
246
 
236
- async getKycThreshold() {
237
- const project = await this.getProjectInfo();
238
-
239
- if (project.chainType === ChainType.Ton) {
240
- throw new Error('TON chain is not supported');
247
+ async getKycThreshold(): Promise<bigint> {
248
+ const strategy = await this.getStrategy();
249
+ if (!strategy.getKycThreshold) {
250
+ throw new Error('getKycThreshold is not supported for this chain');
241
251
  }
242
- const contractWrapper = await this.getAirdropContractWrapper();
243
- return (contractWrapper as EvmAirdropService).getKycThreshold();
244
- }
245
-
246
- async getClaimFee(amount?: bigint) {
247
- const project = await this.getProjectInfo();
248
-
249
- const contractWrapper = await this.getAirdropContractWrapper({});
250
-
251
- return contractWrapper.getClaimFee(project.contractAddress, amount || BigInt(1));
252
+ return strategy.getKycThreshold();
252
253
  }
253
254
  }
@@ -1,6 +1,6 @@
1
1
  import { apiClient } from '../../utils/api-client';
2
2
  import { safeParseJSON } from '../../utils/utils';
3
- import { IAirdropData, IProject } from '../types';
3
+ import { IAirdropData, IContractConfig, IProject } from '../types';
4
4
 
5
5
  export { TonWalletService } from './wallet-service/ton';
6
6
 
@@ -54,6 +54,11 @@ export const getAirdropQuery = async (
54
54
  return client.post(`/airdrop-open/query`, data);
55
55
  };
56
56
 
57
+ export const getAirdropContractConfig = async (data: { chainId: string; chainType: string; contractAddress: string }, baseURL?: string): Promise<IContractConfig> => {
58
+ const client = createApiClient(baseURL);
59
+ return client.post(`/airdrop-open/contract/config`, data);
60
+ };
61
+
57
62
  export const getJettonInfo = async (
58
63
  data: { address: string; isTestnet: 'true' | 'false' },
59
64
  baseURL?: string
@@ -0,0 +1,29 @@
1
+ import { ChainType, IWalletClient } from '../types';
2
+ import { EvmAirdropStrategy } from './EvmAirdropStrategy';
3
+ import { IAirdropStrategy, IAirdropStrategyConstructor } from './IAirdropStrategy';
4
+ import { TonAirdropStrategy } from './TonAirdropStrategy';
5
+ import { TonClient } from '@ton/ton';
6
+
7
+ export class AirdropStrategyFactory {
8
+ private static strategyMap: Record<ChainType, IAirdropStrategyConstructor> = {
9
+ [ChainType.Evm]: EvmAirdropStrategy,
10
+ [ChainType.Ton]: TonAirdropStrategy
11
+ };
12
+
13
+ static createStrategy(params: {
14
+ chainType: ChainType;
15
+ contractAddress: string;
16
+ chainId: string | number;
17
+ walletClient?: IWalletClient;
18
+ tonClient?: TonClient;
19
+ }): IAirdropStrategy {
20
+ const { chainType, ...strategyParams } = params;
21
+ const StrategyClass = this.strategyMap[chainType];
22
+
23
+ if (!StrategyClass) {
24
+ throw new Error(`Unsupported chain type: ${chainType}`);
25
+ }
26
+
27
+ return new StrategyClass(strategyParams);
28
+ }
29
+ }
@@ -0,0 +1,85 @@
1
+ import { AirdropService as EvmAirdropService } from '../core/evm/AirdropService';
2
+ import { ChainId, IWalletClient } from '../types';
3
+ import { IAirdropStrategy } from './IAirdropStrategy';
4
+ import { WalletClient as ViemWalletClient } from 'viem';
5
+
6
+ export class EvmAirdropStrategy implements IAirdropStrategy {
7
+ private service: EvmAirdropService | undefined;
8
+ private readonly contractAddress: string;
9
+ private readonly chainId: ChainId;
10
+ private walletClient?: ViemWalletClient;
11
+ private cachedVersion: string | null = null;
12
+
13
+ constructor(params: {
14
+ contractAddress: string;
15
+ chainId: string | number;
16
+ walletClient?: IWalletClient;
17
+ }) {
18
+ this.contractAddress = params.contractAddress;
19
+ this.chainId = Number(params.chainId) as ChainId;
20
+ if (params.walletClient) {
21
+ this.walletClient = params.walletClient as ViemWalletClient;
22
+ }
23
+ }
24
+
25
+ async initialize(walletClient?: IWalletClient): Promise<void> {
26
+ if (walletClient) {
27
+ this.walletClient = walletClient as ViemWalletClient;
28
+ }
29
+ this.service = new EvmAirdropService({
30
+ contractAddress: this.contractAddress,
31
+ chainId: this.chainId,
32
+ walletClient: this.walletClient
33
+ });
34
+ }
35
+
36
+ isInitialized(): boolean {
37
+ return !!this.service;
38
+ }
39
+
40
+ async getVersion(): Promise<string> {
41
+ if (this.cachedVersion) {
42
+ return this.cachedVersion;
43
+ }
44
+ if (!this.service) throw new Error('Strategy not initialized');
45
+ this.cachedVersion = await this.service.getVersion();
46
+ return this.cachedVersion;
47
+ }
48
+
49
+ async claim(claimData: any): Promise<any> {
50
+ if (!this.service) throw new Error('Strategy not initialized');
51
+ const { proof, group, data, value, extraData } = claimData;
52
+ return this.service.claim({
53
+ proof,
54
+ group,
55
+ data,
56
+ value,
57
+ extraData
58
+ });
59
+ }
60
+
61
+ async checkClaimed(claimData: any): Promise<boolean> {
62
+ if (!this.service) throw new Error('Strategy not initialized');
63
+ return this.service.isLeafUsed(claimData.leaf);
64
+ }
65
+
66
+ async batchCheckClaimed(leafs: string[]): Promise<{ result: boolean; status: string }[]> {
67
+ if (!this.service) throw new Error('Strategy not initialized');
68
+ return this.service.batchFetchClaimed(leafs);
69
+ }
70
+
71
+ async getNFT(): Promise<string> {
72
+ if (!this.service) throw new Error('Strategy not initialized');
73
+ return this.service.getNFT() as unknown as string;
74
+ }
75
+
76
+ async getKycThreshold(): Promise<bigint> {
77
+ if (!this.service) throw new Error('Strategy not initialized');
78
+ return this.service.getKycThreshold();
79
+ }
80
+
81
+ async getClaimFee(contractAddress: string, amount: bigint): Promise<bigint> {
82
+ if (!this.service) throw new Error('Strategy not initialized');
83
+ return this.service.getClaimFee(contractAddress, amount) as unknown as bigint;
84
+ }
85
+ }
@@ -0,0 +1,25 @@
1
+ import { IWalletClient } from '../types';
2
+ import { TonClient } from '@ton/ton';
3
+
4
+ export interface IAirdropStrategy {
5
+ getVersion(): Promise<string>;
6
+ claim(claimData: any, options?: { onLoading?: () => void }): Promise<any>;
7
+ checkClaimed(claimData: any): Promise<boolean>;
8
+ isInitialized(): boolean;
9
+ initialize(walletClient?: IWalletClient): Promise<void>;
10
+ getPaused?(): Promise<boolean>;
11
+ // EVM specific methods
12
+ batchCheckClaimed?(leafs: string[]): Promise<Array<{ result: boolean; status: string }>>;
13
+ getNFT?(): Promise<string>;
14
+ getKycThreshold?(): Promise<bigint>;
15
+ getClaimFee?(contractAddress: string, amount: bigint): Promise<string | bigint>;
16
+ }
17
+
18
+ export interface IAirdropStrategyConstructor {
19
+ new (params: {
20
+ contractAddress: string;
21
+ chainId: string | number;
22
+ walletClient?: IWalletClient;
23
+ tonClient?: TonClient;
24
+ }): IAirdropStrategy;
25
+ }
@@ -0,0 +1,34 @@
1
+ import { IAirdropStrategy } from './IAirdropStrategy';
2
+
3
+ export class StrategyCache {
4
+ private readOnlyStrategies: Map<string, IAirdropStrategy> = new Map();
5
+ private walletStrategies: Map<string, IAirdropStrategy> = new Map();
6
+
7
+ private getCacheKey(contractAddress: string, chainId: string | number, chainType: string): string {
8
+ return `${chainType}:${contractAddress}:${chainId}`;
9
+ }
10
+
11
+ getStrategy(contractAddress: string, chainId: string | number, chainType: string, hasWallet: boolean = false): IAirdropStrategy | undefined {
12
+ const cache = hasWallet ? this.walletStrategies : this.readOnlyStrategies;
13
+ const cacheKey = this.getCacheKey(contractAddress, chainId, chainType);
14
+ return cache.get(cacheKey);
15
+ }
16
+
17
+ setStrategy(contractAddress: string, chainId: string | number, chainType: string, strategy: IAirdropStrategy, hasWallet: boolean = false): void {
18
+ const cache = hasWallet ? this.walletStrategies : this.readOnlyStrategies;
19
+ const cacheKey = this.getCacheKey(contractAddress, chainId, chainType);
20
+ cache.set(cacheKey, strategy);
21
+ }
22
+
23
+ clearStrategy(contractAddress: string, chainId: string | number, chainType: string, hasWallet?: boolean): void {
24
+ const cacheKey = this.getCacheKey(contractAddress, chainId, chainType);
25
+ if (hasWallet === undefined || hasWallet) {
26
+ this.walletStrategies.delete(cacheKey);
27
+ }
28
+ if (hasWallet === undefined || !hasWallet) {
29
+ this.readOnlyStrategies.delete(cacheKey);
30
+ }
31
+ }
32
+ }
33
+
34
+ export const strategyCache = new StrategyCache();
@@ -0,0 +1,72 @@
1
+ import { TonClient } from '@ton/ton';
2
+ import { TonConnectUI } from '@tonconnect/ui-react';
3
+ import { AirdropService as TonAirdropService } from '../core/ton/AirdropService';
4
+ import { IWalletClient } from '../types';
5
+ import { IAirdropStrategy } from './IAirdropStrategy';
6
+
7
+ export class TonAirdropStrategy implements IAirdropStrategy {
8
+ private service: TonAirdropService | undefined;
9
+ private readonly contractAddress: string;
10
+ private readonly chainId: string;
11
+ private readonly tonClient?: TonClient;
12
+ private walletClient?: TonConnectUI;
13
+ private cachedVersion: string | null = null;
14
+
15
+ constructor(params: {
16
+ contractAddress: string;
17
+ chainId: string | number;
18
+ walletClient?: IWalletClient;
19
+ tonClient?: TonClient;
20
+ }) {
21
+ this.contractAddress = params.contractAddress;
22
+ this.chainId = params.chainId.toString();
23
+ this.tonClient = params.tonClient;
24
+ if (params.walletClient) {
25
+ this.walletClient = params.walletClient as TonConnectUI;
26
+ }
27
+ }
28
+
29
+ async initialize(walletClient?: IWalletClient): Promise<void> {
30
+ if (walletClient) {
31
+ this.walletClient = walletClient as TonConnectUI;
32
+ }
33
+ this.service = new TonAirdropService({
34
+ address: this.contractAddress,
35
+ chainId: this.chainId,
36
+ walletClient: this.walletClient,
37
+ tonClient: this.tonClient
38
+ });
39
+ }
40
+
41
+ isInitialized(): boolean {
42
+ return !!this.service;
43
+ }
44
+
45
+ async getVersion(): Promise<string> {
46
+ if (!this.service) throw new Error('Strategy not initialized');
47
+ if (!this.cachedVersion) {
48
+ this.cachedVersion = await this.service.getVersion();
49
+ }
50
+ return this.cachedVersion;
51
+ }
52
+
53
+ async claim(claimData: any, options?: { onLoading?: () => void }): Promise<any> {
54
+ if (!this.service) throw new Error('Strategy not initialized');
55
+ return this.service.claim(claimData, options);
56
+ }
57
+
58
+ async checkClaimed(claimData: any): Promise<boolean> {
59
+ if (!this.service) throw new Error('Strategy not initialized');
60
+ return this.service.checkClaimed(claimData);
61
+ }
62
+
63
+ async getPaused(): Promise<boolean> {
64
+ if (!this.service) throw new Error('Strategy not initialized');
65
+ return this.service.getPaused();
66
+ }
67
+
68
+ async getClaimFee(contractAddress: string, amount: bigint): Promise<string> {
69
+ if (!this.service) throw new Error('Strategy not initialized');
70
+ return this.service.getClaimFee(contractAddress, amount);
71
+ }
72
+ }