@viwoapp/sdk 0.1.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,2285 @@
1
+ // src/core/index.ts
2
+ import {
3
+ Connection,
4
+ PublicKey as PublicKey2,
5
+ Transaction
6
+ } from "@solana/web3.js";
7
+ import { BN } from "@coral-xyz/anchor";
8
+
9
+ // src/constants.ts
10
+ import { PublicKey } from "@solana/web3.js";
11
+ var PROGRAM_IDS = {
12
+ vcoinToken: new PublicKey("Gg1dtrjAfGYi6NLC31WaJjZNBoucvD98rK2h1u9qrUjn"),
13
+ vevcoinToken: new PublicKey("FB39ae9x53FxVL3pER9LqCPEx2TRnEnQP55i838Upnjx"),
14
+ stakingProtocol: new PublicKey("6EFcistyr2E81adLUcuBJRr8W2xzpt3D3dFYEcMewpWu"),
15
+ transferHook: new PublicKey("9K14FcDRrBeHKD9FPNYeVJaEqJQTac2xspJyb1mM6m48"),
16
+ identityProtocol: new PublicKey("3egAds3pFR5oog6iQCN42KPvgih8HQz2FGybNjiVWixG"),
17
+ fiveAProtocol: new PublicKey("783PbtJw5cc7yatnr9fsvTGSnkKaV6iJe6E8VUPTYrT8"),
18
+ contentRegistry: new PublicKey("MJn1A4MPCBPJGWWuZrtq7bHSo2G289sUwW3ej2wcmLV"),
19
+ governanceProtocol: new PublicKey("3R256kBN9iXozjypQFRAmegBhd6HJqXWqdNG7Th78HYe"),
20
+ sscreProtocol: new PublicKey("6AJNcQSfoiE2UAeUDyJUBumS9SBwhAdSznoAeYpXrxXZ"),
21
+ vilinkProtocol: new PublicKey("CFGXTS2MueQwTYTMMTBQbRWzJtSTC2p4ZRuKPpLDmrv7"),
22
+ gaslessProtocol: new PublicKey("FcXJAjzJs8eVY2WTRFXynQBpC7WZUqKZppyp9xS6PaB3")
23
+ };
24
+ var SEEDS = {
25
+ // VCoin
26
+ vcoinConfig: "vcoin-config",
27
+ // veVCoin
28
+ vevcoinConfig: "vevcoin-config",
29
+ userVevcoin: "user-vevcoin",
30
+ // Staking
31
+ stakingPool: "staking-pool",
32
+ userStake: "user-stake",
33
+ // Governance
34
+ governanceConfig: "governance-config",
35
+ proposal: "proposal",
36
+ voteRecord: "vote",
37
+ delegation: "delegation",
38
+ // SSCRE
39
+ poolConfig: "pool-config",
40
+ epoch: "epoch",
41
+ userClaim: "user-claim",
42
+ // ViLink
43
+ vilinkConfig: "vilink-config",
44
+ action: "action",
45
+ userStats: "user-stats",
46
+ dapp: "dapp",
47
+ // Gasless
48
+ gaslessConfig: "gasless-config",
49
+ sessionKey: "session-key",
50
+ userGasless: "user-gasless",
51
+ feeVault: "fee-vault",
52
+ // Identity
53
+ identityConfig: "identity-config",
54
+ identity: "identity",
55
+ // 5A
56
+ fiveAConfig: "five-a-config",
57
+ userScore: "user-score",
58
+ vouch: "vouch",
59
+ // Content
60
+ registryConfig: "registry-config",
61
+ content: "content",
62
+ userEnergy: "user-energy"
63
+ };
64
+ var VCOIN_DECIMALS = 9;
65
+ var VEVCOIN_DECIMALS = 9;
66
+ var VCOIN_TOTAL_SUPPLY = 1e9;
67
+ var VCOIN_INITIAL_CIRCULATING = 1e8;
68
+ var STAKING_TIERS = {
69
+ none: { minStake: 0, feeDiscount: 0, boost: 1, minLock: 0 },
70
+ bronze: { minStake: 1e3, feeDiscount: 10, boost: 1.1, minLock: 0 },
71
+ silver: { minStake: 5e3, feeDiscount: 20, boost: 1.2, minLock: 0 },
72
+ gold: { minStake: 2e4, feeDiscount: 30, boost: 1.3, minLock: 0 },
73
+ platinum: { minStake: 1e5, feeDiscount: 50, boost: 1.4, minLock: 0 }
74
+ };
75
+ var LOCK_DURATIONS = {
76
+ none: 0,
77
+ oneMonth: 30 * 24 * 3600,
78
+ threeMonths: 90 * 24 * 3600,
79
+ sixMonths: 180 * 24 * 3600,
80
+ oneYear: 365 * 24 * 3600
81
+ };
82
+ var SSCRE_CONSTANTS = {
83
+ primaryReserves: 35e7,
84
+ // 350M VCoin
85
+ secondaryReserves: 4e7,
86
+ // 40M VCoin
87
+ epochDuration: 30 * 24 * 3600,
88
+ // 30 days
89
+ claimWindow: 90 * 24 * 3600,
90
+ // 90 days
91
+ gaslessFeeBps: 100,
92
+ // 1%
93
+ minClaimAmount: 1
94
+ // 1 VCoin
95
+ };
96
+ var VILINK_CONSTANTS = {
97
+ maxActionExpiry: 7 * 24 * 3600,
98
+ // 7 days
99
+ minTipAmount: 0.1,
100
+ // 0.1 VCoin
101
+ maxTipAmount: 1e4,
102
+ // 10,000 VCoin
103
+ platformFeeBps: 250
104
+ // 2.5%
105
+ };
106
+ var ACTION_SCOPES = {
107
+ tip: 1 << 0,
108
+ vouch: 1 << 1,
109
+ content: 1 << 2,
110
+ governance: 1 << 3,
111
+ transfer: 1 << 4,
112
+ stake: 1 << 5,
113
+ claim: 1 << 6,
114
+ follow: 1 << 7,
115
+ all: 65535
116
+ };
117
+ var GASLESS_CONSTANTS = {
118
+ sessionDuration: 24 * 3600,
119
+ // 24 hours
120
+ maxSessionActions: 1e3,
121
+ maxSessionSpend: 1e5,
122
+ // 100,000 VCoin
123
+ defaultSolFee: 5e3,
124
+ // 0.000005 SOL
125
+ vcoinFeeMultiplier: 100,
126
+ sscreDeductionBps: 100,
127
+ // 1%
128
+ dailySubsidyBudget: 10,
129
+ // 10 SOL
130
+ maxSubsidizedPerUser: 50
131
+ };
132
+ var FIVE_A_CONSTANTS = {
133
+ maxScore: 1e4,
134
+ // 100.00 with 2 decimal precision
135
+ scoreWeights: {
136
+ authenticity: 25,
137
+ // A1 - "Are you a real person?"
138
+ accuracy: 20,
139
+ // A2 - "Is your content quality?"
140
+ agility: 15,
141
+ // A3 - "Are you fast?"
142
+ activity: 25,
143
+ // A4 - "Do you show up daily?"
144
+ approved: 15
145
+ // A5 - "Does the community like you?"
146
+ },
147
+ scoreMultipliers: {
148
+ "0-20": 0.1,
149
+ "20-40": 0.4,
150
+ "40-60": 0.7,
151
+ "60-80": 1,
152
+ "80-100": 1.2
153
+ }
154
+ };
155
+ var CONTENT_CONSTANTS = {
156
+ maxEnergy: 100,
157
+ energyRegenRate: 10,
158
+ // per hour
159
+ createCost: 10,
160
+ editCost: 5,
161
+ deleteCost: 0
162
+ };
163
+ var GOVERNANCE_CONSTANTS = {
164
+ minProposalThreshold: 100,
165
+ // 100 veVCoin
166
+ votingDuration: 7 * 24 * 3600,
167
+ // 7 days
168
+ executionDelay: 2 * 24 * 3600,
169
+ // 2 days
170
+ vetoWindow: 24 * 3600,
171
+ // 1 day
172
+ quorumBps: 400
173
+ // 4%
174
+ };
175
+
176
+ // src/core/index.ts
177
+ var ViWoConnection = class {
178
+ constructor(config) {
179
+ this.commitment = config.commitment || "confirmed";
180
+ this.connection = new Connection(
181
+ config.endpoint,
182
+ {
183
+ commitment: this.commitment,
184
+ wsEndpoint: config.wsEndpoint
185
+ }
186
+ );
187
+ }
188
+ /**
189
+ * Get current slot
190
+ */
191
+ async getSlot() {
192
+ return this.connection.getSlot(this.commitment);
193
+ }
194
+ /**
195
+ * Get current block time
196
+ */
197
+ async getBlockTime() {
198
+ const slot = await this.getSlot();
199
+ return this.connection.getBlockTime(slot);
200
+ }
201
+ /**
202
+ * Check if connection is healthy
203
+ */
204
+ async isHealthy() {
205
+ try {
206
+ await this.connection.getVersion();
207
+ return true;
208
+ } catch {
209
+ return false;
210
+ }
211
+ }
212
+ };
213
+ var PDAs = class {
214
+ constructor(programIds = PROGRAM_IDS) {
215
+ this.programIds = programIds;
216
+ }
217
+ // VCoin PDAs
218
+ getVCoinConfig() {
219
+ const [pda] = PublicKey2.findProgramAddressSync(
220
+ [Buffer.from(SEEDS.vcoinConfig)],
221
+ this.programIds.vcoinToken
222
+ );
223
+ return pda;
224
+ }
225
+ // Staking PDAs
226
+ getStakingPool() {
227
+ const [pda] = PublicKey2.findProgramAddressSync(
228
+ [Buffer.from(SEEDS.stakingPool)],
229
+ this.programIds.stakingProtocol
230
+ );
231
+ return pda;
232
+ }
233
+ getUserStake(user) {
234
+ const [pda] = PublicKey2.findProgramAddressSync(
235
+ [Buffer.from(SEEDS.userStake), user.toBuffer()],
236
+ this.programIds.stakingProtocol
237
+ );
238
+ return pda;
239
+ }
240
+ // Governance PDAs
241
+ getGovernanceConfig() {
242
+ const [pda] = PublicKey2.findProgramAddressSync(
243
+ [Buffer.from(SEEDS.governanceConfig)],
244
+ this.programIds.governanceProtocol
245
+ );
246
+ return pda;
247
+ }
248
+ getProposal(proposalId) {
249
+ const [pda] = PublicKey2.findProgramAddressSync(
250
+ [Buffer.from(SEEDS.proposal), proposalId.toArrayLike(Buffer, "le", 8)],
251
+ this.programIds.governanceProtocol
252
+ );
253
+ return pda;
254
+ }
255
+ getVoteRecord(user, proposal) {
256
+ const [pda] = PublicKey2.findProgramAddressSync(
257
+ [Buffer.from(SEEDS.voteRecord), user.toBuffer(), proposal.toBuffer()],
258
+ this.programIds.governanceProtocol
259
+ );
260
+ return pda;
261
+ }
262
+ // SSCRE PDAs
263
+ getRewardsPoolConfig() {
264
+ const [pda] = PublicKey2.findProgramAddressSync(
265
+ [Buffer.from(SEEDS.poolConfig)],
266
+ this.programIds.sscreProtocol
267
+ );
268
+ return pda;
269
+ }
270
+ getEpochDistribution(epoch) {
271
+ const [pda] = PublicKey2.findProgramAddressSync(
272
+ [Buffer.from(SEEDS.epoch), epoch.toArrayLike(Buffer, "le", 8)],
273
+ this.programIds.sscreProtocol
274
+ );
275
+ return pda;
276
+ }
277
+ getUserClaim(user) {
278
+ const [pda] = PublicKey2.findProgramAddressSync(
279
+ [Buffer.from(SEEDS.userClaim), user.toBuffer()],
280
+ this.programIds.sscreProtocol
281
+ );
282
+ return pda;
283
+ }
284
+ // ViLink PDAs
285
+ getViLinkConfig() {
286
+ const [pda] = PublicKey2.findProgramAddressSync(
287
+ [Buffer.from(SEEDS.vilinkConfig)],
288
+ this.programIds.vilinkProtocol
289
+ );
290
+ return pda;
291
+ }
292
+ getViLinkAction(creator, timestamp) {
293
+ const [pda] = PublicKey2.findProgramAddressSync(
294
+ [
295
+ Buffer.from(SEEDS.action),
296
+ creator.toBuffer(),
297
+ timestamp.toArrayLike(Buffer, "le", 8)
298
+ ],
299
+ this.programIds.vilinkProtocol
300
+ );
301
+ return pda;
302
+ }
303
+ getUserActionStats(user) {
304
+ const [pda] = PublicKey2.findProgramAddressSync(
305
+ [Buffer.from(SEEDS.userStats), user.toBuffer()],
306
+ this.programIds.vilinkProtocol
307
+ );
308
+ return pda;
309
+ }
310
+ // Gasless PDAs
311
+ getGaslessConfig() {
312
+ const [pda] = PublicKey2.findProgramAddressSync(
313
+ [Buffer.from(SEEDS.gaslessConfig)],
314
+ this.programIds.gaslessProtocol
315
+ );
316
+ return pda;
317
+ }
318
+ getSessionKey(user, sessionPubkey) {
319
+ const [pda] = PublicKey2.findProgramAddressSync(
320
+ [
321
+ Buffer.from(SEEDS.sessionKey),
322
+ user.toBuffer(),
323
+ sessionPubkey.toBuffer()
324
+ ],
325
+ this.programIds.gaslessProtocol
326
+ );
327
+ return pda;
328
+ }
329
+ getUserGaslessStats(user) {
330
+ const [pda] = PublicKey2.findProgramAddressSync(
331
+ [Buffer.from(SEEDS.userGasless), user.toBuffer()],
332
+ this.programIds.gaslessProtocol
333
+ );
334
+ return pda;
335
+ }
336
+ // Identity PDAs
337
+ getIdentityConfig() {
338
+ const [pda] = PublicKey2.findProgramAddressSync(
339
+ [Buffer.from(SEEDS.identityConfig)],
340
+ this.programIds.identityProtocol
341
+ );
342
+ return pda;
343
+ }
344
+ getUserIdentity(user) {
345
+ const [pda] = PublicKey2.findProgramAddressSync(
346
+ [Buffer.from(SEEDS.identity), user.toBuffer()],
347
+ this.programIds.identityProtocol
348
+ );
349
+ return pda;
350
+ }
351
+ // 5A Protocol PDAs
352
+ getFiveAConfig() {
353
+ const [pda] = PublicKey2.findProgramAddressSync(
354
+ [Buffer.from(SEEDS.fiveAConfig)],
355
+ this.programIds.fiveAProtocol
356
+ );
357
+ return pda;
358
+ }
359
+ getUserScore(user) {
360
+ const [pda] = PublicKey2.findProgramAddressSync(
361
+ [Buffer.from(SEEDS.userScore), user.toBuffer()],
362
+ this.programIds.fiveAProtocol
363
+ );
364
+ return pda;
365
+ }
366
+ // Content PDAs
367
+ getContentRegistryConfig() {
368
+ const [pda] = PublicKey2.findProgramAddressSync(
369
+ [Buffer.from(SEEDS.registryConfig)],
370
+ this.programIds.contentRegistry
371
+ );
372
+ return pda;
373
+ }
374
+ getContentRecord(contentId) {
375
+ const [pda] = PublicKey2.findProgramAddressSync(
376
+ [Buffer.from(SEEDS.content), Buffer.from(contentId)],
377
+ this.programIds.contentRegistry
378
+ );
379
+ return pda;
380
+ }
381
+ getUserEnergy(user) {
382
+ const [pda] = PublicKey2.findProgramAddressSync(
383
+ [Buffer.from(SEEDS.userEnergy), user.toBuffer()],
384
+ this.programIds.contentRegistry
385
+ );
386
+ return pda;
387
+ }
388
+ };
389
+ var TransactionBuilder = class {
390
+ constructor() {
391
+ this.instructions = [];
392
+ }
393
+ add(instruction) {
394
+ this.instructions.push(instruction);
395
+ return this;
396
+ }
397
+ addMany(instructions) {
398
+ this.instructions.push(...instructions);
399
+ return this;
400
+ }
401
+ build() {
402
+ const tx = new Transaction();
403
+ for (const ix of this.instructions) {
404
+ tx.add(ix);
405
+ }
406
+ return tx;
407
+ }
408
+ clear() {
409
+ this.instructions = [];
410
+ return this;
411
+ }
412
+ get length() {
413
+ return this.instructions.length;
414
+ }
415
+ };
416
+ function formatVCoin(amount, decimals = 9) {
417
+ const amountBN = typeof amount === "number" ? new BN(amount) : amount;
418
+ const divisor = new BN(10).pow(new BN(decimals));
419
+ const whole = amountBN.div(divisor).toString();
420
+ const fraction = amountBN.mod(divisor).toString().padStart(decimals, "0");
421
+ return `${whole}.${fraction}`;
422
+ }
423
+ function parseVCoin(amount, decimals = 9) {
424
+ if (typeof amount === "number") {
425
+ amount = amount.toString();
426
+ }
427
+ const [whole, fraction = ""] = amount.split(".");
428
+ const paddedFraction = fraction.padEnd(decimals, "0").slice(0, decimals);
429
+ return new BN(whole + paddedFraction);
430
+ }
431
+ function getCurrentTimestamp() {
432
+ return Math.floor(Date.now() / 1e3);
433
+ }
434
+ function timestampToDate(timestamp) {
435
+ const ts = typeof timestamp === "number" ? timestamp : timestamp.toNumber();
436
+ return new Date(ts * 1e3);
437
+ }
438
+ function dateToTimestamp(date) {
439
+ return Math.floor(date.getTime() / 1e3);
440
+ }
441
+
442
+ // src/types.ts
443
+ var StakingTier = /* @__PURE__ */ ((StakingTier2) => {
444
+ StakingTier2[StakingTier2["None"] = 0] = "None";
445
+ StakingTier2[StakingTier2["Bronze"] = 1] = "Bronze";
446
+ StakingTier2[StakingTier2["Silver"] = 2] = "Silver";
447
+ StakingTier2[StakingTier2["Gold"] = 3] = "Gold";
448
+ StakingTier2[StakingTier2["Platinum"] = 4] = "Platinum";
449
+ return StakingTier2;
450
+ })(StakingTier || {});
451
+ var ProposalStatus = /* @__PURE__ */ ((ProposalStatus2) => {
452
+ ProposalStatus2[ProposalStatus2["Active"] = 0] = "Active";
453
+ ProposalStatus2[ProposalStatus2["Passed"] = 1] = "Passed";
454
+ ProposalStatus2[ProposalStatus2["Rejected"] = 2] = "Rejected";
455
+ ProposalStatus2[ProposalStatus2["Executed"] = 3] = "Executed";
456
+ ProposalStatus2[ProposalStatus2["Cancelled"] = 4] = "Cancelled";
457
+ return ProposalStatus2;
458
+ })(ProposalStatus || {});
459
+ var ActionType = /* @__PURE__ */ ((ActionType2) => {
460
+ ActionType2[ActionType2["Tip"] = 0] = "Tip";
461
+ ActionType2[ActionType2["Vouch"] = 1] = "Vouch";
462
+ ActionType2[ActionType2["Follow"] = 2] = "Follow";
463
+ ActionType2[ActionType2["Challenge"] = 3] = "Challenge";
464
+ ActionType2[ActionType2["Stake"] = 4] = "Stake";
465
+ ActionType2[ActionType2["ContentReact"] = 5] = "ContentReact";
466
+ ActionType2[ActionType2["Delegate"] = 6] = "Delegate";
467
+ ActionType2[ActionType2["Vote"] = 7] = "Vote";
468
+ return ActionType2;
469
+ })(ActionType || {});
470
+ var FeeMethod = /* @__PURE__ */ ((FeeMethod2) => {
471
+ FeeMethod2[FeeMethod2["PlatformSubsidized"] = 0] = "PlatformSubsidized";
472
+ FeeMethod2[FeeMethod2["VCoinDeduction"] = 1] = "VCoinDeduction";
473
+ FeeMethod2[FeeMethod2["SSCREDeduction"] = 2] = "SSCREDeduction";
474
+ return FeeMethod2;
475
+ })(FeeMethod || {});
476
+ var VerificationLevel = /* @__PURE__ */ ((VerificationLevel2) => {
477
+ VerificationLevel2[VerificationLevel2["None"] = 0] = "None";
478
+ VerificationLevel2[VerificationLevel2["Basic"] = 1] = "Basic";
479
+ VerificationLevel2[VerificationLevel2["KYC"] = 2] = "KYC";
480
+ VerificationLevel2[VerificationLevel2["Full"] = 3] = "Full";
481
+ VerificationLevel2[VerificationLevel2["Enhanced"] = 4] = "Enhanced";
482
+ return VerificationLevel2;
483
+ })(VerificationLevel || {});
484
+ var ContentState = /* @__PURE__ */ ((ContentState2) => {
485
+ ContentState2[ContentState2["Active"] = 0] = "Active";
486
+ ContentState2[ContentState2["Hidden"] = 1] = "Hidden";
487
+ ContentState2[ContentState2["Deleted"] = 2] = "Deleted";
488
+ ContentState2[ContentState2["Flagged"] = 3] = "Flagged";
489
+ return ContentState2;
490
+ })(ContentState || {});
491
+
492
+ // src/staking/index.ts
493
+ import { PublicKey as PublicKey3, Transaction as Transaction2 } from "@solana/web3.js";
494
+ import { BN as BN2 } from "@coral-xyz/anchor";
495
+ var StakingClient = class {
496
+ constructor(client) {
497
+ this.client = client;
498
+ }
499
+ /**
500
+ * Get staking pool configuration
501
+ */
502
+ async getPool() {
503
+ try {
504
+ const poolPda = this.client.pdas.getStakingPool();
505
+ const accountInfo = await this.client.connection.connection.getAccountInfo(poolPda);
506
+ if (!accountInfo) {
507
+ return null;
508
+ }
509
+ return {
510
+ authority: new PublicKey3(accountInfo.data.slice(8, 40)),
511
+ vcoinMint: new PublicKey3(accountInfo.data.slice(40, 72)),
512
+ vevcoinMint: new PublicKey3(accountInfo.data.slice(72, 104)),
513
+ totalStaked: new BN2(accountInfo.data.slice(104, 112), "le"),
514
+ totalVevcoinMinted: new BN2(accountInfo.data.slice(112, 120), "le"),
515
+ paused: accountInfo.data[120] !== 0
516
+ };
517
+ } catch {
518
+ return null;
519
+ }
520
+ }
521
+ /**
522
+ * Get user stake information
523
+ */
524
+ async getUserStake(user) {
525
+ const target = user || this.client.publicKey;
526
+ if (!target) {
527
+ throw new Error("No user specified and wallet not connected");
528
+ }
529
+ try {
530
+ const stakePda = this.client.pdas.getUserStake(target);
531
+ const accountInfo = await this.client.connection.connection.getAccountInfo(stakePda);
532
+ if (!accountInfo) {
533
+ return null;
534
+ }
535
+ return {
536
+ user: new PublicKey3(accountInfo.data.slice(8, 40)),
537
+ stakedAmount: new BN2(accountInfo.data.slice(40, 48), "le"),
538
+ vevcoinBalance: new BN2(accountInfo.data.slice(48, 56), "le"),
539
+ tier: accountInfo.data[56],
540
+ lockEndTime: new BN2(accountInfo.data.slice(57, 65), "le"),
541
+ lastUpdateTime: new BN2(accountInfo.data.slice(65, 73), "le")
542
+ };
543
+ } catch {
544
+ return null;
545
+ }
546
+ }
547
+ /**
548
+ * Calculate tier based on stake amount
549
+ */
550
+ calculateTier(stakeAmount) {
551
+ const amount = typeof stakeAmount === "number" ? stakeAmount : stakeAmount.toNumber() / Math.pow(10, VCOIN_DECIMALS);
552
+ if (amount >= STAKING_TIERS.platinum.minStake) return 4;
553
+ if (amount >= STAKING_TIERS.gold.minStake) return 3;
554
+ if (amount >= STAKING_TIERS.silver.minStake) return 2;
555
+ if (amount >= STAKING_TIERS.bronze.minStake) return 1;
556
+ return 0;
557
+ }
558
+ /**
559
+ * Calculate veVCoin amount for given stake
560
+ * Formula: ve_vcoin = staked_amount × (lock_duration / 4_years) × tier_boost
561
+ */
562
+ calculateVeVCoin(amount, lockDuration) {
563
+ const FOUR_YEARS = 4 * 365 * 24 * 3600;
564
+ const lockRatio = lockDuration / FOUR_YEARS;
565
+ const tier = this.calculateTier(amount);
566
+ const tierBoosts = [1, 1.1, 1.2, 1.3, 1.4];
567
+ const tierBoost = tierBoosts[tier];
568
+ const multiplier = lockRatio * tierBoost;
569
+ const vevcoinAmount = amount.toNumber() * multiplier;
570
+ return new BN2(Math.floor(vevcoinAmount));
571
+ }
572
+ /**
573
+ * Get tier name
574
+ */
575
+ getTierName(tier) {
576
+ const names = ["None", "Bronze", "Silver", "Gold", "Platinum"];
577
+ return names[tier] || "Unknown";
578
+ }
579
+ /**
580
+ * Get tier info
581
+ */
582
+ getTierInfo(tier) {
583
+ const tiers = [
584
+ STAKING_TIERS.none,
585
+ STAKING_TIERS.bronze,
586
+ STAKING_TIERS.silver,
587
+ STAKING_TIERS.gold,
588
+ STAKING_TIERS.platinum
589
+ ];
590
+ return tiers[tier] || STAKING_TIERS.none;
591
+ }
592
+ /**
593
+ * Check if user can unstake
594
+ */
595
+ async canUnstake(user) {
596
+ const stakeInfo = await this.getUserStake(user);
597
+ if (!stakeInfo) {
598
+ return { canUnstake: false, reason: "No active stake found" };
599
+ }
600
+ if (stakeInfo.stakedAmount.isZero()) {
601
+ return { canUnstake: false, reason: "No staked amount" };
602
+ }
603
+ const now = Math.floor(Date.now() / 1e3);
604
+ if (stakeInfo.lockEndTime.toNumber() > now) {
605
+ const remaining = stakeInfo.lockEndTime.toNumber() - now;
606
+ const days = Math.ceil(remaining / 86400);
607
+ return { canUnstake: false, reason: `Lock period active: ${days} days remaining` };
608
+ }
609
+ return { canUnstake: true };
610
+ }
611
+ /**
612
+ * Get staking statistics
613
+ */
614
+ async getStats() {
615
+ const pool = await this.getPool();
616
+ const userStake = this.client.publicKey ? await this.getUserStake() : null;
617
+ return {
618
+ totalStaked: pool ? formatVCoin(pool.totalStaked) : "0",
619
+ totalVevcoin: pool ? formatVCoin(pool.totalVevcoinMinted) : "0",
620
+ userStake: userStake ? formatVCoin(userStake.stakedAmount) : null,
621
+ userVevcoin: userStake ? formatVCoin(userStake.vevcoinBalance) : null,
622
+ userTier: userStake ? this.getTierName(userStake.tier) : null
623
+ };
624
+ }
625
+ // ============ Transaction Building ============
626
+ // Note: Full implementation would use Anchor IDL
627
+ /**
628
+ * Build stake instruction
629
+ * @param params Stake parameters
630
+ * @returns Transaction to sign and send
631
+ */
632
+ async buildStakeTransaction(params) {
633
+ if (!this.client.publicKey) {
634
+ throw new Error("Wallet not connected");
635
+ }
636
+ const tx = new Transaction2();
637
+ return tx;
638
+ }
639
+ /**
640
+ * Build unstake instruction
641
+ * @returns Transaction to sign and send
642
+ */
643
+ async buildUnstakeTransaction() {
644
+ if (!this.client.publicKey) {
645
+ throw new Error("Wallet not connected");
646
+ }
647
+ const { canUnstake, reason } = await this.canUnstake();
648
+ if (!canUnstake) {
649
+ throw new Error(reason);
650
+ }
651
+ const tx = new Transaction2();
652
+ return tx;
653
+ }
654
+ /**
655
+ * Build extend lock instruction
656
+ * @param newDuration New lock duration in seconds
657
+ * @returns Transaction to sign and send
658
+ */
659
+ async buildExtendLockTransaction(newDuration) {
660
+ if (!this.client.publicKey) {
661
+ throw new Error("Wallet not connected");
662
+ }
663
+ const tx = new Transaction2();
664
+ return tx;
665
+ }
666
+ };
667
+
668
+ // src/governance/index.ts
669
+ import { PublicKey as PublicKey4, Transaction as Transaction3 } from "@solana/web3.js";
670
+ import { BN as BN3 } from "@coral-xyz/anchor";
671
+ var GovernanceClient = class {
672
+ constructor(client) {
673
+ this.client = client;
674
+ }
675
+ /**
676
+ * Get governance configuration
677
+ */
678
+ async getConfig() {
679
+ try {
680
+ const configPda = this.client.pdas.getGovernanceConfig();
681
+ const accountInfo = await this.client.connection.connection.getAccountInfo(configPda);
682
+ if (!accountInfo) {
683
+ return null;
684
+ }
685
+ return {
686
+ authority: new PublicKey4(accountInfo.data.slice(8, 40)),
687
+ proposalCount: new BN3(accountInfo.data.slice(40, 48), "le"),
688
+ vevcoinMint: new PublicKey4(accountInfo.data.slice(48, 80)),
689
+ paused: accountInfo.data[80] !== 0
690
+ };
691
+ } catch {
692
+ return null;
693
+ }
694
+ }
695
+ /**
696
+ * Get proposal by ID
697
+ */
698
+ async getProposal(proposalId) {
699
+ try {
700
+ const proposalPda = this.client.pdas.getProposal(proposalId);
701
+ const accountInfo = await this.client.connection.connection.getAccountInfo(proposalPda);
702
+ if (!accountInfo) {
703
+ return null;
704
+ }
705
+ const data = accountInfo.data;
706
+ return {
707
+ id: new BN3(data.slice(8, 16), "le"),
708
+ proposer: new PublicKey4(data.slice(16, 48)),
709
+ title: Buffer.from(data.slice(48, 112)).toString("utf8").replace(/\0/g, ""),
710
+ descriptionHash: new Uint8Array(data.slice(112, 144)),
711
+ startTime: new BN3(data.slice(144, 152), "le"),
712
+ endTime: new BN3(data.slice(152, 160), "le"),
713
+ votesFor: new BN3(data.slice(160, 168), "le"),
714
+ votesAgainst: new BN3(data.slice(168, 176), "le"),
715
+ status: data[176],
716
+ executed: data[177] !== 0,
717
+ category: data[178]
718
+ };
719
+ } catch {
720
+ return null;
721
+ }
722
+ }
723
+ /**
724
+ * Get all active proposals
725
+ */
726
+ async getActiveProposals() {
727
+ const config = await this.getConfig();
728
+ if (!config) return [];
729
+ const proposals = [];
730
+ const now = Math.floor(Date.now() / 1e3);
731
+ const proposalCount = config.proposalCount.toNumber();
732
+ const startFrom = Math.max(0, proposalCount - 20);
733
+ for (let i = startFrom; i < proposalCount; i++) {
734
+ const proposal = await this.getProposal(new BN3(i));
735
+ if (proposal && proposal.endTime.toNumber() > now && proposal.status === 0) {
736
+ proposals.push(proposal);
737
+ }
738
+ }
739
+ return proposals;
740
+ }
741
+ /**
742
+ * Get user's vote record for a proposal
743
+ */
744
+ async getVoteRecord(proposalId, user) {
745
+ const target = user || this.client.publicKey;
746
+ if (!target) {
747
+ throw new Error("No user specified and wallet not connected");
748
+ }
749
+ try {
750
+ const proposalPda = this.client.pdas.getProposal(proposalId);
751
+ const votePda = this.client.pdas.getVoteRecord(target, proposalPda);
752
+ const accountInfo = await this.client.connection.connection.getAccountInfo(votePda);
753
+ if (!accountInfo) {
754
+ return null;
755
+ }
756
+ const data = accountInfo.data;
757
+ return {
758
+ user: new PublicKey4(data.slice(8, 40)),
759
+ proposal: new PublicKey4(data.slice(40, 72)),
760
+ votePower: new BN3(data.slice(72, 80), "le"),
761
+ support: data[80] !== 0,
762
+ votedAt: new BN3(data.slice(81, 89), "le")
763
+ };
764
+ } catch {
765
+ return null;
766
+ }
767
+ }
768
+ /**
769
+ * Check if user has voted on a proposal
770
+ */
771
+ async hasVoted(proposalId, user) {
772
+ const voteRecord = await this.getVoteRecord(proposalId, user);
773
+ return voteRecord !== null;
774
+ }
775
+ /**
776
+ * Calculate user's voting power
777
+ */
778
+ async getVotingPower(user) {
779
+ const target = user || this.client.publicKey;
780
+ if (!target) {
781
+ throw new Error("No user specified and wallet not connected");
782
+ }
783
+ const vevcoinBalance = await this.client.getVeVCoinBalance(target);
784
+ const fiveAMultiplier = 1;
785
+ return new BN3(Math.floor(vevcoinBalance.toNumber() * fiveAMultiplier));
786
+ }
787
+ /**
788
+ * Get proposal status text
789
+ */
790
+ getStatusText(status) {
791
+ const statuses = ["Active", "Passed", "Rejected", "Executed", "Cancelled"];
792
+ return statuses[status] || "Unknown";
793
+ }
794
+ /**
795
+ * Check if proposal can be executed
796
+ */
797
+ async canExecute(proposalId) {
798
+ const proposal = await this.getProposal(proposalId);
799
+ if (!proposal) {
800
+ return { canExecute: false, reason: "Proposal not found" };
801
+ }
802
+ if (proposal.executed) {
803
+ return { canExecute: false, reason: "Proposal already executed" };
804
+ }
805
+ if (proposal.status !== 1 /* Passed */) {
806
+ return { canExecute: false, reason: "Proposal has not passed" };
807
+ }
808
+ const now = Math.floor(Date.now() / 1e3);
809
+ const executionDelay = proposal.endTime.toNumber() + GOVERNANCE_CONSTANTS.executionDelay;
810
+ if (now < executionDelay) {
811
+ const remaining = executionDelay - now;
812
+ const hours = Math.ceil(remaining / 3600);
813
+ return { canExecute: false, reason: `Execution delay: ${hours} hours remaining` };
814
+ }
815
+ return { canExecute: true };
816
+ }
817
+ /**
818
+ * Get proposal progress
819
+ */
820
+ async getProposalProgress(proposalId) {
821
+ const proposal = await this.getProposal(proposalId);
822
+ if (!proposal) {
823
+ throw new Error("Proposal not found");
824
+ }
825
+ const totalVotes = proposal.votesFor.add(proposal.votesAgainst);
826
+ const forPct = totalVotes.isZero() ? 0 : proposal.votesFor.toNumber() / totalVotes.toNumber() * 100;
827
+ const againstPct = 100 - forPct;
828
+ const quorumThreshold = new BN3(GOVERNANCE_CONSTANTS.quorumBps).mul(new BN3(1e4));
829
+ const quorumReached = totalVotes.gte(quorumThreshold);
830
+ const now = Math.floor(Date.now() / 1e3);
831
+ const timeRemaining = Math.max(0, proposal.endTime.toNumber() - now);
832
+ return {
833
+ votesFor: formatVCoin(proposal.votesFor),
834
+ votesAgainst: formatVCoin(proposal.votesAgainst),
835
+ totalVotes: formatVCoin(totalVotes),
836
+ forPercentage: forPct,
837
+ againstPercentage: againstPct,
838
+ quorumReached,
839
+ timeRemaining
840
+ };
841
+ }
842
+ // ============ Transaction Building ============
843
+ /**
844
+ * Build create proposal transaction
845
+ */
846
+ async buildCreateProposalTransaction(params) {
847
+ if (!this.client.publicKey) {
848
+ throw new Error("Wallet not connected");
849
+ }
850
+ const votingPower = await this.getVotingPower();
851
+ if (votingPower.toNumber() < GOVERNANCE_CONSTANTS.minProposalThreshold) {
852
+ throw new Error(`Insufficient voting power. Need ${GOVERNANCE_CONSTANTS.minProposalThreshold} veVCoin`);
853
+ }
854
+ const tx = new Transaction3();
855
+ return tx;
856
+ }
857
+ /**
858
+ * Build vote transaction
859
+ */
860
+ async buildVoteTransaction(proposalId, support) {
861
+ if (!this.client.publicKey) {
862
+ throw new Error("Wallet not connected");
863
+ }
864
+ const hasVoted = await this.hasVoted(proposalId);
865
+ if (hasVoted) {
866
+ throw new Error("Already voted on this proposal");
867
+ }
868
+ const tx = new Transaction3();
869
+ return tx;
870
+ }
871
+ /**
872
+ * Build execute proposal transaction
873
+ */
874
+ async buildExecuteTransaction(proposalId) {
875
+ if (!this.client.publicKey) {
876
+ throw new Error("Wallet not connected");
877
+ }
878
+ const { canExecute, reason } = await this.canExecute(proposalId);
879
+ if (!canExecute) {
880
+ throw new Error(reason);
881
+ }
882
+ const tx = new Transaction3();
883
+ return tx;
884
+ }
885
+ };
886
+
887
+ // src/rewards/index.ts
888
+ import { PublicKey as PublicKey5, Transaction as Transaction4 } from "@solana/web3.js";
889
+ import { BN as BN4 } from "@coral-xyz/anchor";
890
+ var RewardsClient = class {
891
+ constructor(client) {
892
+ this.client = client;
893
+ }
894
+ /**
895
+ * Get rewards pool configuration
896
+ */
897
+ async getPoolConfig() {
898
+ try {
899
+ const configPda = this.client.pdas.getRewardsPoolConfig();
900
+ const accountInfo = await this.client.connection.connection.getAccountInfo(configPda);
901
+ if (!accountInfo) {
902
+ return null;
903
+ }
904
+ const data = accountInfo.data;
905
+ return {
906
+ authority: new PublicKey5(data.slice(8, 40)),
907
+ vcoinMint: new PublicKey5(data.slice(40, 72)),
908
+ currentEpoch: new BN4(data.slice(72, 80), "le"),
909
+ totalDistributed: new BN4(data.slice(80, 88), "le"),
910
+ remainingReserves: new BN4(data.slice(88, 96), "le"),
911
+ paused: data[96] !== 0
912
+ };
913
+ } catch {
914
+ return null;
915
+ }
916
+ }
917
+ /**
918
+ * Get epoch distribution details
919
+ */
920
+ async getEpochDistribution(epoch) {
921
+ try {
922
+ const epochPda = this.client.pdas.getEpochDistribution(epoch);
923
+ const accountInfo = await this.client.connection.connection.getAccountInfo(epochPda);
924
+ if (!accountInfo) {
925
+ return null;
926
+ }
927
+ const data = accountInfo.data;
928
+ return {
929
+ epoch: new BN4(data.slice(8, 16), "le"),
930
+ merkleRoot: new Uint8Array(data.slice(16, 48)),
931
+ totalAllocation: new BN4(data.slice(48, 56), "le"),
932
+ totalClaimed: new BN4(data.slice(56, 64), "le"),
933
+ claimsCount: new BN4(data.slice(64, 72), "le"),
934
+ isFinalized: data[72] !== 0
935
+ };
936
+ } catch {
937
+ return null;
938
+ }
939
+ }
940
+ /**
941
+ * Get current epoch
942
+ */
943
+ async getCurrentEpoch() {
944
+ const config = await this.getPoolConfig();
945
+ return config?.currentEpoch || new BN4(0);
946
+ }
947
+ /**
948
+ * Get user claim history
949
+ */
950
+ async getUserClaim(user) {
951
+ const target = user || this.client.publicKey;
952
+ if (!target) {
953
+ throw new Error("No user specified and wallet not connected");
954
+ }
955
+ try {
956
+ const claimPda = this.client.pdas.getUserClaim(target);
957
+ const accountInfo = await this.client.connection.connection.getAccountInfo(claimPda);
958
+ if (!accountInfo) {
959
+ return null;
960
+ }
961
+ const data = accountInfo.data;
962
+ return {
963
+ user: new PublicKey5(data.slice(8, 40)),
964
+ lastClaimedEpoch: new BN4(data.slice(40, 48), "le"),
965
+ totalClaimed: new BN4(data.slice(48, 56), "le"),
966
+ claimsCount: data.readUInt32LE(56)
967
+ };
968
+ } catch {
969
+ return null;
970
+ }
971
+ }
972
+ /**
973
+ * Check if user has claimed for an epoch
974
+ */
975
+ async hasClaimedEpoch(epoch, user) {
976
+ const userClaim = await this.getUserClaim(user);
977
+ if (!userClaim) return false;
978
+ const epochNum = epoch.toNumber();
979
+ if (epochNum <= 255) {
980
+ return userClaim.lastClaimedEpoch.gte(epoch);
981
+ }
982
+ return userClaim.lastClaimedEpoch.gte(epoch);
983
+ }
984
+ /**
985
+ * Get unclaimed epochs
986
+ */
987
+ async getUnclaimedEpochs(user) {
988
+ const currentEpoch = await this.getCurrentEpoch();
989
+ const userClaim = await this.getUserClaim(user);
990
+ const unclaimed = [];
991
+ const startEpoch = userClaim ? userClaim.lastClaimedEpoch.toNumber() + 1 : 1;
992
+ for (let e = startEpoch; e <= currentEpoch.toNumber(); e++) {
993
+ const epochDist = await this.getEpochDistribution(new BN4(e));
994
+ if (epochDist?.isFinalized) {
995
+ const now = Math.floor(Date.now() / 1e3);
996
+ const claimExpiry = epochDist.epoch.toNumber() * SSCRE_CONSTANTS.epochDuration + SSCRE_CONSTANTS.claimWindow;
997
+ if (now <= claimExpiry) {
998
+ unclaimed.push(new BN4(e));
999
+ }
1000
+ }
1001
+ }
1002
+ return unclaimed;
1003
+ }
1004
+ /**
1005
+ * Get rewards statistics
1006
+ */
1007
+ async getStats() {
1008
+ const config = await this.getPoolConfig();
1009
+ const userClaim = this.client.publicKey ? await this.getUserClaim() : null;
1010
+ const totalReserves = SSCRE_CONSTANTS.primaryReserves * 1e9;
1011
+ const remaining = config?.remainingReserves.toNumber() || 0;
1012
+ const reservePct = remaining / totalReserves * 100;
1013
+ return {
1014
+ currentEpoch: config?.currentEpoch.toNumber() || 0,
1015
+ totalDistributed: config ? formatVCoin(config.totalDistributed) : "0",
1016
+ remainingReserves: config ? formatVCoin(config.remainingReserves) : "0",
1017
+ reservePercentage: reservePct,
1018
+ userTotalClaimed: userClaim ? formatVCoin(userClaim.totalClaimed) : null,
1019
+ userClaimsCount: userClaim?.claimsCount || null
1020
+ };
1021
+ }
1022
+ /**
1023
+ * Calculate gasless fee for claim
1024
+ */
1025
+ calculateGaslessFee(amount) {
1026
+ const fee = amount.muln(SSCRE_CONSTANTS.gaslessFeeBps).divn(1e4);
1027
+ return fee;
1028
+ }
1029
+ /**
1030
+ * Calculate net claim amount after fee
1031
+ */
1032
+ calculateNetClaim(amount) {
1033
+ const fee = this.calculateGaslessFee(amount);
1034
+ return amount.sub(fee);
1035
+ }
1036
+ // ============ Merkle Proof Utilities ============
1037
+ /**
1038
+ * Verify merkle proof locally
1039
+ */
1040
+ verifyMerkleProof(proof, root, leaf) {
1041
+ let computedHash = leaf;
1042
+ for (const proofElement of proof) {
1043
+ const combined = new Uint8Array(64);
1044
+ if (this.compareBytes(computedHash, proofElement) < 0) {
1045
+ combined.set(computedHash, 0);
1046
+ combined.set(proofElement, 32);
1047
+ } else {
1048
+ combined.set(proofElement, 0);
1049
+ combined.set(computedHash, 32);
1050
+ }
1051
+ computedHash = this.hashBytes(combined);
1052
+ }
1053
+ return this.compareBytes(computedHash, root) === 0;
1054
+ }
1055
+ /**
1056
+ * Compute leaf hash from user data
1057
+ */
1058
+ computeLeaf(user, amount, epoch) {
1059
+ const data = new Uint8Array(48);
1060
+ data.set(user.toBytes(), 0);
1061
+ data.set(amount.toArrayLike(Buffer, "le", 8), 32);
1062
+ data.set(epoch.toArrayLike(Buffer, "le", 8), 40);
1063
+ return this.hashBytes(data);
1064
+ }
1065
+ compareBytes(a, b) {
1066
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
1067
+ if (a[i] !== b[i]) {
1068
+ return a[i] - b[i];
1069
+ }
1070
+ }
1071
+ return a.length - b.length;
1072
+ }
1073
+ hashBytes(data) {
1074
+ const hash = new Uint8Array(32);
1075
+ for (let i = 0; i < data.length; i++) {
1076
+ hash[i % 32] ^= data[i];
1077
+ }
1078
+ return hash;
1079
+ }
1080
+ // ============ Transaction Building ============
1081
+ /**
1082
+ * Build claim rewards transaction
1083
+ */
1084
+ async buildClaimTransaction(params) {
1085
+ if (!this.client.publicKey) {
1086
+ throw new Error("Wallet not connected");
1087
+ }
1088
+ if (params.amount.lt(new BN4(SSCRE_CONSTANTS.minClaimAmount * 1e9))) {
1089
+ throw new Error(`Claim amount below minimum: ${SSCRE_CONSTANTS.minClaimAmount} VCoin`);
1090
+ }
1091
+ const hasClaimed = await this.hasClaimedEpoch(params.epoch);
1092
+ if (hasClaimed) {
1093
+ throw new Error("Already claimed for this epoch");
1094
+ }
1095
+ const tx = new Transaction4();
1096
+ return tx;
1097
+ }
1098
+ };
1099
+
1100
+ // src/vilink/index.ts
1101
+ import { PublicKey as PublicKey6, Transaction as Transaction5 } from "@solana/web3.js";
1102
+ import { BN as BN5 } from "@coral-xyz/anchor";
1103
+ var ViLinkClient = class {
1104
+ constructor(client) {
1105
+ this.client = client;
1106
+ }
1107
+ /**
1108
+ * Get ViLink configuration
1109
+ */
1110
+ async getConfig() {
1111
+ try {
1112
+ const configPda = this.client.pdas.getViLinkConfig();
1113
+ const accountInfo = await this.client.connection.connection.getAccountInfo(configPda);
1114
+ if (!accountInfo) {
1115
+ return null;
1116
+ }
1117
+ const data = accountInfo.data;
1118
+ return {
1119
+ authority: new PublicKey6(data.slice(8, 40)),
1120
+ vcoinMint: new PublicKey6(data.slice(40, 72)),
1121
+ treasury: new PublicKey6(data.slice(72, 104)),
1122
+ enabledActions: data[200],
1123
+ totalActionsCreated: new BN5(data.slice(201, 209), "le"),
1124
+ totalActionsExecuted: new BN5(data.slice(209, 217), "le"),
1125
+ totalTipVolume: new BN5(data.slice(217, 225), "le"),
1126
+ paused: data[225] !== 0,
1127
+ platformFeeBps: data.readUInt16LE(226)
1128
+ };
1129
+ } catch {
1130
+ return null;
1131
+ }
1132
+ }
1133
+ /**
1134
+ * Get action by ID
1135
+ */
1136
+ async getAction(creator, timestamp) {
1137
+ try {
1138
+ const actionPda = this.client.pdas.getViLinkAction(creator, timestamp);
1139
+ const accountInfo = await this.client.connection.connection.getAccountInfo(actionPda);
1140
+ if (!accountInfo) {
1141
+ return null;
1142
+ }
1143
+ const data = accountInfo.data;
1144
+ return {
1145
+ actionId: new Uint8Array(data.slice(8, 40)),
1146
+ creator: new PublicKey6(data.slice(40, 72)),
1147
+ target: new PublicKey6(data.slice(72, 104)),
1148
+ actionType: data[104],
1149
+ amount: new BN5(data.slice(105, 113), "le"),
1150
+ expiresAt: new BN5(data.slice(145, 153), "le"),
1151
+ executed: data[153] !== 0,
1152
+ executionCount: data.readUInt32LE(193),
1153
+ maxExecutions: data.readUInt32LE(197)
1154
+ };
1155
+ } catch {
1156
+ return null;
1157
+ }
1158
+ }
1159
+ /**
1160
+ * Get user action statistics
1161
+ */
1162
+ async getUserStats(user) {
1163
+ const target = user || this.client.publicKey;
1164
+ if (!target) {
1165
+ throw new Error("No user specified and wallet not connected");
1166
+ }
1167
+ try {
1168
+ const statsPda = this.client.pdas.getUserActionStats(target);
1169
+ const accountInfo = await this.client.connection.connection.getAccountInfo(statsPda);
1170
+ if (!accountInfo) {
1171
+ return null;
1172
+ }
1173
+ const data = accountInfo.data;
1174
+ return {
1175
+ user: new PublicKey6(data.slice(8, 40)),
1176
+ actionsCreated: new BN5(data.slice(40, 48), "le"),
1177
+ actionsExecuted: new BN5(data.slice(48, 56), "le"),
1178
+ tipsSent: new BN5(data.slice(56, 64), "le"),
1179
+ tipsReceived: new BN5(data.slice(64, 72), "le"),
1180
+ vcoinSent: new BN5(data.slice(72, 80), "le"),
1181
+ vcoinReceived: new BN5(data.slice(80, 88), "le")
1182
+ };
1183
+ } catch {
1184
+ return null;
1185
+ }
1186
+ }
1187
+ /**
1188
+ * Get action type name
1189
+ */
1190
+ getActionTypeName(actionType) {
1191
+ const names = [
1192
+ "Tip",
1193
+ "Vouch",
1194
+ "Follow",
1195
+ "Challenge",
1196
+ "Stake",
1197
+ "ContentReact",
1198
+ "Delegate",
1199
+ "Vote"
1200
+ ];
1201
+ return names[actionType] || "Unknown";
1202
+ }
1203
+ /**
1204
+ * Check if action type is enabled
1205
+ */
1206
+ async isActionTypeEnabled(actionType) {
1207
+ const config = await this.getConfig();
1208
+ if (!config) return false;
1209
+ return (config.enabledActions & 1 << actionType) !== 0;
1210
+ }
1211
+ /**
1212
+ * Check if action is valid for execution
1213
+ */
1214
+ async isActionValid(creator, timestamp) {
1215
+ const action = await this.getAction(creator, timestamp);
1216
+ if (!action) {
1217
+ return { valid: false, reason: "Action not found" };
1218
+ }
1219
+ const now = getCurrentTimestamp();
1220
+ if (now > action.expiresAt.toNumber()) {
1221
+ return { valid: false, reason: "Action has expired" };
1222
+ }
1223
+ if (action.executed && action.maxExecutions === 1) {
1224
+ return { valid: false, reason: "Action already executed" };
1225
+ }
1226
+ if (action.maxExecutions > 0 && action.executionCount >= action.maxExecutions) {
1227
+ return { valid: false, reason: "Max executions reached" };
1228
+ }
1229
+ return { valid: true };
1230
+ }
1231
+ /**
1232
+ * Calculate platform fee for tip
1233
+ */
1234
+ calculateFee(amount) {
1235
+ const fee = amount.muln(VILINK_CONSTANTS.platformFeeBps).divn(1e4);
1236
+ return {
1237
+ fee,
1238
+ net: amount.sub(fee)
1239
+ };
1240
+ }
1241
+ // ============ URI Utilities ============
1242
+ /**
1243
+ * Generate ViLink URI from action ID
1244
+ */
1245
+ generateUri(actionId, baseUrl = "viwo://action") {
1246
+ const idHex = Buffer.from(actionId).toString("hex");
1247
+ return `${baseUrl}/${idHex}`;
1248
+ }
1249
+ /**
1250
+ * Parse action ID from URI
1251
+ */
1252
+ parseUri(uri) {
1253
+ const match = uri.match(/viwo:\/\/action\/([a-f0-9]{64})/i);
1254
+ if (!match) return null;
1255
+ return new Uint8Array(Buffer.from(match[1], "hex"));
1256
+ }
1257
+ /**
1258
+ * Generate QR code data for action
1259
+ */
1260
+ generateQRData(actionId) {
1261
+ return this.generateUri(actionId, "https://viwoapp.com/action");
1262
+ }
1263
+ /**
1264
+ * Generate shareable link with metadata
1265
+ */
1266
+ generateShareableLink(actionId, metadata) {
1267
+ const baseUri = this.generateUri(actionId, "https://viwoapp.com/action");
1268
+ if (!metadata) return baseUri;
1269
+ const params = new URLSearchParams();
1270
+ if (metadata.title) params.set("t", metadata.title);
1271
+ if (metadata.amount) params.set("a", metadata.amount);
1272
+ return `${baseUri}?${params.toString()}`;
1273
+ }
1274
+ // ============ Transaction Building ============
1275
+ /**
1276
+ * Build create tip action transaction
1277
+ */
1278
+ async buildCreateTipAction(params) {
1279
+ if (!this.client.publicKey) {
1280
+ throw new Error("Wallet not connected");
1281
+ }
1282
+ const minAmount = parseVCoin(VILINK_CONSTANTS.minTipAmount.toString());
1283
+ const maxAmount = parseVCoin(VILINK_CONSTANTS.maxTipAmount.toString());
1284
+ if (params.amount.lt(minAmount)) {
1285
+ throw new Error(`Tip amount below minimum: ${VILINK_CONSTANTS.minTipAmount} VCoin`);
1286
+ }
1287
+ if (params.amount.gt(maxAmount)) {
1288
+ throw new Error(`Tip amount exceeds maximum: ${VILINK_CONSTANTS.maxTipAmount} VCoin`);
1289
+ }
1290
+ const tx = new Transaction5();
1291
+ return tx;
1292
+ }
1293
+ /**
1294
+ * Build create vouch action transaction
1295
+ */
1296
+ async buildCreateVouchAction(params) {
1297
+ if (!this.client.publicKey) {
1298
+ throw new Error("Wallet not connected");
1299
+ }
1300
+ const tx = new Transaction5();
1301
+ return tx;
1302
+ }
1303
+ /**
1304
+ * Build create follow action transaction
1305
+ */
1306
+ async buildCreateFollowAction(params) {
1307
+ if (!this.client.publicKey) {
1308
+ throw new Error("Wallet not connected");
1309
+ }
1310
+ const tx = new Transaction5();
1311
+ return tx;
1312
+ }
1313
+ /**
1314
+ * Build execute tip action transaction
1315
+ */
1316
+ async buildExecuteTipAction(creator, timestamp) {
1317
+ if (!this.client.publicKey) {
1318
+ throw new Error("Wallet not connected");
1319
+ }
1320
+ const { valid, reason } = await this.isActionValid(creator, timestamp);
1321
+ if (!valid) {
1322
+ throw new Error(reason);
1323
+ }
1324
+ const action = await this.getAction(creator, timestamp);
1325
+ if (action?.creator.equals(this.client.publicKey)) {
1326
+ throw new Error("Cannot execute own action");
1327
+ }
1328
+ const tx = new Transaction5();
1329
+ return tx;
1330
+ }
1331
+ };
1332
+
1333
+ // src/gasless/index.ts
1334
+ import { PublicKey as PublicKey7, Transaction as Transaction6 } from "@solana/web3.js";
1335
+ import { BN as BN6 } from "@coral-xyz/anchor";
1336
+ var GaslessClient = class {
1337
+ constructor(client) {
1338
+ this.client = client;
1339
+ }
1340
+ /**
1341
+ * Get gasless configuration
1342
+ */
1343
+ async getConfig() {
1344
+ try {
1345
+ const configPda = this.client.pdas.getGaslessConfig();
1346
+ const accountInfo = await this.client.connection.connection.getAccountInfo(configPda);
1347
+ if (!accountInfo) {
1348
+ return null;
1349
+ }
1350
+ const data = accountInfo.data;
1351
+ return {
1352
+ authority: new PublicKey7(data.slice(8, 40)),
1353
+ feePayer: new PublicKey7(data.slice(40, 72)),
1354
+ vcoinMint: new PublicKey7(data.slice(72, 104)),
1355
+ dailySubsidyBudget: new BN6(data.slice(136, 144), "le"),
1356
+ solFeePerTx: new BN6(data.slice(144, 152), "le"),
1357
+ vcoinFeeMultiplier: new BN6(data.slice(152, 160), "le"),
1358
+ totalSubsidizedTx: new BN6(data.slice(168, 176), "le"),
1359
+ totalVcoinCollected: new BN6(data.slice(184, 192), "le"),
1360
+ paused: data[192] !== 0
1361
+ };
1362
+ } catch {
1363
+ return null;
1364
+ }
1365
+ }
1366
+ /**
1367
+ * Get session key details
1368
+ */
1369
+ async getSessionKey(user, sessionPubkey) {
1370
+ try {
1371
+ const sessionPda = this.client.pdas.getSessionKey(user, sessionPubkey);
1372
+ const accountInfo = await this.client.connection.connection.getAccountInfo(sessionPda);
1373
+ if (!accountInfo) {
1374
+ return null;
1375
+ }
1376
+ const data = accountInfo.data;
1377
+ return {
1378
+ user: new PublicKey7(data.slice(8, 40)),
1379
+ sessionPubkey: new PublicKey7(data.slice(40, 72)),
1380
+ scope: data.readUInt16LE(72),
1381
+ createdAt: new BN6(data.slice(74, 82), "le"),
1382
+ expiresAt: new BN6(data.slice(82, 90), "le"),
1383
+ actionsUsed: data.readUInt32LE(90),
1384
+ maxActions: data.readUInt32LE(94),
1385
+ vcoinSpent: new BN6(data.slice(98, 106), "le"),
1386
+ maxSpend: new BN6(data.slice(106, 114), "le"),
1387
+ isRevoked: data[114] !== 0,
1388
+ feeMethod: data[123]
1389
+ };
1390
+ } catch {
1391
+ return null;
1392
+ }
1393
+ }
1394
+ /**
1395
+ * Get user gasless statistics
1396
+ */
1397
+ async getUserStats(user) {
1398
+ const target = user || this.client.publicKey;
1399
+ if (!target) {
1400
+ throw new Error("No user specified and wallet not connected");
1401
+ }
1402
+ try {
1403
+ const statsPda = this.client.pdas.getUserGaslessStats(target);
1404
+ const accountInfo = await this.client.connection.connection.getAccountInfo(statsPda);
1405
+ if (!accountInfo) {
1406
+ return null;
1407
+ }
1408
+ const data = accountInfo.data;
1409
+ return {
1410
+ user: new PublicKey7(data.slice(8, 40)),
1411
+ totalGaslessTx: new BN6(data.slice(40, 48), "le"),
1412
+ totalSubsidized: new BN6(data.slice(48, 56), "le"),
1413
+ totalVcoinFees: new BN6(data.slice(56, 64), "le"),
1414
+ sessionsCreated: data.readUInt32LE(72),
1415
+ activeSession: new PublicKey7(data.slice(76, 108))
1416
+ };
1417
+ } catch {
1418
+ return null;
1419
+ }
1420
+ }
1421
+ /**
1422
+ * Check if session is valid
1423
+ */
1424
+ async isSessionValid(user, sessionPubkey) {
1425
+ const session = await this.getSessionKey(user, sessionPubkey);
1426
+ if (!session) {
1427
+ return { valid: false, reason: "Session not found" };
1428
+ }
1429
+ if (session.isRevoked) {
1430
+ return { valid: false, reason: "Session has been revoked" };
1431
+ }
1432
+ const now = getCurrentTimestamp();
1433
+ if (now > session.expiresAt.toNumber()) {
1434
+ return { valid: false, reason: "Session has expired" };
1435
+ }
1436
+ if (session.actionsUsed >= session.maxActions) {
1437
+ return { valid: false, reason: "Session action limit reached" };
1438
+ }
1439
+ return { valid: true };
1440
+ }
1441
+ /**
1442
+ * Check if action is in session scope
1443
+ */
1444
+ isActionInScope(session, actionScope) {
1445
+ return (session.scope & actionScope) !== 0;
1446
+ }
1447
+ /**
1448
+ * Get remaining session actions
1449
+ */
1450
+ getRemainingActions(session) {
1451
+ return session.maxActions - session.actionsUsed;
1452
+ }
1453
+ /**
1454
+ * Get remaining session spend
1455
+ */
1456
+ getRemainingSpend(session) {
1457
+ return session.maxSpend.sub(session.vcoinSpent);
1458
+ }
1459
+ /**
1460
+ * Get remaining session time
1461
+ */
1462
+ getRemainingTime(session) {
1463
+ const now = getCurrentTimestamp();
1464
+ return Math.max(0, session.expiresAt.toNumber() - now);
1465
+ }
1466
+ /**
1467
+ * Calculate VCoin fee equivalent
1468
+ */
1469
+ async calculateVCoinFee() {
1470
+ const config = await this.getConfig();
1471
+ if (!config) {
1472
+ return new BN6(GASLESS_CONSTANTS.defaultSolFee * GASLESS_CONSTANTS.vcoinFeeMultiplier);
1473
+ }
1474
+ return config.solFeePerTx.mul(config.vcoinFeeMultiplier);
1475
+ }
1476
+ /**
1477
+ * Check if user is eligible for subsidized transactions
1478
+ */
1479
+ async isEligibleForSubsidy(user) {
1480
+ const target = user || this.client.publicKey;
1481
+ if (!target) {
1482
+ throw new Error("No user specified and wallet not connected");
1483
+ }
1484
+ const [config, userStats] = await Promise.all([
1485
+ this.getConfig(),
1486
+ this.getUserStats(target)
1487
+ ]);
1488
+ if (!config) {
1489
+ return { eligible: false, remainingToday: 0, reason: "Config not found" };
1490
+ }
1491
+ const maxPerUser = GASLESS_CONSTANTS.maxSubsidizedPerUser;
1492
+ const usedToday = 0;
1493
+ const remaining = maxPerUser - usedToday;
1494
+ if (remaining <= 0) {
1495
+ return {
1496
+ eligible: false,
1497
+ remainingToday: 0,
1498
+ reason: "Daily limit reached"
1499
+ };
1500
+ }
1501
+ return { eligible: true, remainingToday: remaining };
1502
+ }
1503
+ /**
1504
+ * Get scope names from scope bitmap
1505
+ */
1506
+ getScopeNames(scope) {
1507
+ const names = [];
1508
+ const scopeMap = [
1509
+ { bit: ACTION_SCOPES.tip, name: "Tip" },
1510
+ { bit: ACTION_SCOPES.vouch, name: "Vouch" },
1511
+ { bit: ACTION_SCOPES.content, name: "Content" },
1512
+ { bit: ACTION_SCOPES.governance, name: "Governance" },
1513
+ { bit: ACTION_SCOPES.transfer, name: "Transfer" },
1514
+ { bit: ACTION_SCOPES.stake, name: "Stake" },
1515
+ { bit: ACTION_SCOPES.claim, name: "Claim" },
1516
+ { bit: ACTION_SCOPES.follow, name: "Follow" }
1517
+ ];
1518
+ for (const { bit, name } of scopeMap) {
1519
+ if (scope & bit) {
1520
+ names.push(name);
1521
+ }
1522
+ }
1523
+ return names;
1524
+ }
1525
+ /**
1526
+ * Create scope from action names
1527
+ */
1528
+ createScope(actions) {
1529
+ let scope = 0;
1530
+ const scopeMap = {
1531
+ tip: ACTION_SCOPES.tip,
1532
+ vouch: ACTION_SCOPES.vouch,
1533
+ content: ACTION_SCOPES.content,
1534
+ governance: ACTION_SCOPES.governance,
1535
+ transfer: ACTION_SCOPES.transfer,
1536
+ stake: ACTION_SCOPES.stake,
1537
+ claim: ACTION_SCOPES.claim,
1538
+ follow: ACTION_SCOPES.follow
1539
+ };
1540
+ for (const action of actions) {
1541
+ const bit = scopeMap[action.toLowerCase()];
1542
+ if (bit) {
1543
+ scope |= bit;
1544
+ }
1545
+ }
1546
+ return scope;
1547
+ }
1548
+ // ============ Transaction Building ============
1549
+ /**
1550
+ * Build create session key transaction
1551
+ */
1552
+ async buildCreateSessionTransaction(params) {
1553
+ if (!this.client.publicKey) {
1554
+ throw new Error("Wallet not connected");
1555
+ }
1556
+ if (!params.sessionPubkey) {
1557
+ throw new Error("Session public key required");
1558
+ }
1559
+ if (!params.scope || params.scope === 0) {
1560
+ throw new Error("At least one scope required");
1561
+ }
1562
+ const duration = params.durationSeconds || GASLESS_CONSTANTS.sessionDuration;
1563
+ const maxActions = params.maxActions || GASLESS_CONSTANTS.maxSessionActions;
1564
+ const maxSpend = params.maxSpend || new BN6(GASLESS_CONSTANTS.maxSessionSpend * 1e9);
1565
+ const feeMethod = params.feeMethod ?? 1;
1566
+ const tx = new Transaction6();
1567
+ return tx;
1568
+ }
1569
+ /**
1570
+ * Build revoke session key transaction
1571
+ */
1572
+ async buildRevokeSessionTransaction(sessionPubkey) {
1573
+ if (!this.client.publicKey) {
1574
+ throw new Error("Wallet not connected");
1575
+ }
1576
+ const session = await this.getSessionKey(this.client.publicKey, sessionPubkey);
1577
+ if (!session) {
1578
+ throw new Error("Session not found");
1579
+ }
1580
+ if (session.isRevoked) {
1581
+ throw new Error("Session already revoked");
1582
+ }
1583
+ const tx = new Transaction6();
1584
+ return tx;
1585
+ }
1586
+ /**
1587
+ * Build VCoin fee deduction transaction
1588
+ */
1589
+ async buildDeductFeeTransaction(amount) {
1590
+ if (!this.client.publicKey) {
1591
+ throw new Error("Wallet not connected");
1592
+ }
1593
+ const tx = new Transaction6();
1594
+ return tx;
1595
+ }
1596
+ };
1597
+
1598
+ // src/identity/index.ts
1599
+ import { PublicKey as PublicKey8, Transaction as Transaction7 } from "@solana/web3.js";
1600
+ import { BN as BN7 } from "@coral-xyz/anchor";
1601
+ var IdentityClient = class {
1602
+ constructor(client) {
1603
+ this.client = client;
1604
+ }
1605
+ /**
1606
+ * Get user identity
1607
+ */
1608
+ async getIdentity(user) {
1609
+ const target = user || this.client.publicKey;
1610
+ if (!target) {
1611
+ throw new Error("No user specified and wallet not connected");
1612
+ }
1613
+ try {
1614
+ const identityPda = this.client.pdas.getUserIdentity(target);
1615
+ const accountInfo = await this.client.connection.connection.getAccountInfo(identityPda);
1616
+ if (!accountInfo) {
1617
+ return null;
1618
+ }
1619
+ const data = accountInfo.data;
1620
+ return {
1621
+ user: new PublicKey8(data.slice(8, 40)),
1622
+ didHash: new Uint8Array(data.slice(40, 72)),
1623
+ verificationLevel: data[72],
1624
+ createdAt: new BN7(data.slice(73, 81), "le"),
1625
+ updatedAt: new BN7(data.slice(81, 89), "le")
1626
+ };
1627
+ } catch {
1628
+ return null;
1629
+ }
1630
+ }
1631
+ /**
1632
+ * Check if user has identity
1633
+ */
1634
+ async hasIdentity(user) {
1635
+ const identity = await this.getIdentity(user);
1636
+ return identity !== null;
1637
+ }
1638
+ /**
1639
+ * Get verification level name
1640
+ */
1641
+ getVerificationLevelName(level) {
1642
+ const levels = ["None", "Basic", "Standard", "Enhanced", "Premium"];
1643
+ return levels[level] || "Unknown";
1644
+ }
1645
+ /**
1646
+ * Get verification level requirements
1647
+ */
1648
+ getVerificationRequirements(level) {
1649
+ const requirements = {
1650
+ 0: [],
1651
+ 1: ["Email verification", "Phone verification"],
1652
+ 2: ["Basic requirements", "Social account linking"],
1653
+ 3: ["Standard requirements", "ID verification"],
1654
+ 4: ["Enhanced requirements", "Face verification", "Address verification"]
1655
+ };
1656
+ return requirements[level] || [];
1657
+ }
1658
+ /**
1659
+ * Get verification level benefits
1660
+ */
1661
+ getVerificationBenefits(level) {
1662
+ const benefits = {
1663
+ 0: ["Basic platform access"],
1664
+ 1: ["Higher withdrawal limits", "Basic rewards eligibility"],
1665
+ 2: ["Full rewards eligibility", "Vouch capabilities"],
1666
+ 3: ["Priority support", "Enhanced trust score"],
1667
+ 4: ["VIP status", "Governance proposal creation", "Maximum limits"]
1668
+ };
1669
+ return benefits[level] || [];
1670
+ }
1671
+ // ============ Transaction Building ============
1672
+ /**
1673
+ * Build create identity transaction
1674
+ */
1675
+ async buildCreateIdentityTransaction(didHash) {
1676
+ if (!this.client.publicKey) {
1677
+ throw new Error("Wallet not connected");
1678
+ }
1679
+ const existing = await this.getIdentity();
1680
+ if (existing) {
1681
+ throw new Error("Identity already exists");
1682
+ }
1683
+ if (didHash.length !== 32) {
1684
+ throw new Error("DID hash must be 32 bytes");
1685
+ }
1686
+ const tx = new Transaction7();
1687
+ return tx;
1688
+ }
1689
+ /**
1690
+ * Build update DID hash transaction
1691
+ */
1692
+ async buildUpdateDidHashTransaction(newDidHash) {
1693
+ if (!this.client.publicKey) {
1694
+ throw new Error("Wallet not connected");
1695
+ }
1696
+ if (newDidHash.length !== 32) {
1697
+ throw new Error("DID hash must be 32 bytes");
1698
+ }
1699
+ const tx = new Transaction7();
1700
+ return tx;
1701
+ }
1702
+ };
1703
+
1704
+ // src/fivea/index.ts
1705
+ import { PublicKey as PublicKey9, Transaction as Transaction8 } from "@solana/web3.js";
1706
+ import { BN as BN8 } from "@coral-xyz/anchor";
1707
+ var FiveAClient = class {
1708
+ constructor(client) {
1709
+ this.client = client;
1710
+ }
1711
+ /**
1712
+ * Get user's 5A score
1713
+ */
1714
+ async getScore(user) {
1715
+ const target = user || this.client.publicKey;
1716
+ if (!target) {
1717
+ throw new Error("No user specified and wallet not connected");
1718
+ }
1719
+ try {
1720
+ const scorePda = this.client.pdas.getUserScore(target);
1721
+ const accountInfo = await this.client.connection.connection.getAccountInfo(scorePda);
1722
+ if (!accountInfo) {
1723
+ return null;
1724
+ }
1725
+ const data = accountInfo.data;
1726
+ return {
1727
+ user: new PublicKey9(data.slice(8, 40)),
1728
+ authenticity: data.readUInt16LE(40),
1729
+ accuracy: data.readUInt16LE(42),
1730
+ agility: data.readUInt16LE(44),
1731
+ activity: data.readUInt16LE(46),
1732
+ approved: data.readUInt16LE(48),
1733
+ compositeScore: data.readUInt16LE(50),
1734
+ lastUpdated: new BN8(data.slice(52, 60), "le"),
1735
+ isPrivate: data[60] !== 0
1736
+ };
1737
+ } catch {
1738
+ return null;
1739
+ }
1740
+ }
1741
+ /**
1742
+ * Format score as percentage
1743
+ */
1744
+ formatScore(score) {
1745
+ return `${(score / 100).toFixed(2)}%`;
1746
+ }
1747
+ /**
1748
+ * Get score tier
1749
+ */
1750
+ getScoreTier(composite) {
1751
+ if (composite >= 8e3) return "Excellent";
1752
+ if (composite >= 6e3) return "Good";
1753
+ if (composite >= 4e3) return "Average";
1754
+ if (composite >= 2e3) return "Below Average";
1755
+ return "Low";
1756
+ }
1757
+ /**
1758
+ * Get reward multiplier for score
1759
+ */
1760
+ getRewardMultiplier(composite) {
1761
+ if (composite >= 8e3) return FIVE_A_CONSTANTS.scoreMultipliers["80-100"];
1762
+ if (composite >= 6e3) return FIVE_A_CONSTANTS.scoreMultipliers["60-80"];
1763
+ if (composite >= 4e3) return FIVE_A_CONSTANTS.scoreMultipliers["40-60"];
1764
+ if (composite >= 2e3) return FIVE_A_CONSTANTS.scoreMultipliers["20-40"];
1765
+ return FIVE_A_CONSTANTS.scoreMultipliers["0-20"];
1766
+ }
1767
+ /**
1768
+ * Get score breakdown
1769
+ */
1770
+ getScoreBreakdown(score) {
1771
+ const weights = FIVE_A_CONSTANTS.scoreWeights;
1772
+ return [
1773
+ {
1774
+ component: "A1 - Authenticity",
1775
+ description: "Are you a real person?",
1776
+ score: this.formatScore(score.authenticity),
1777
+ weight: weights.authenticity,
1778
+ contribution: this.formatScore(score.authenticity * weights.authenticity / 100)
1779
+ },
1780
+ {
1781
+ component: "A2 - Accuracy",
1782
+ description: "Is your content quality?",
1783
+ score: this.formatScore(score.accuracy),
1784
+ weight: weights.accuracy,
1785
+ contribution: this.formatScore(score.accuracy * weights.accuracy / 100)
1786
+ },
1787
+ {
1788
+ component: "A3 - Agility",
1789
+ description: "Are you fast?",
1790
+ score: this.formatScore(score.agility),
1791
+ weight: weights.agility,
1792
+ contribution: this.formatScore(score.agility * weights.agility / 100)
1793
+ },
1794
+ {
1795
+ component: "A4 - Activity",
1796
+ description: "Do you show up daily?",
1797
+ score: this.formatScore(score.activity),
1798
+ weight: weights.activity,
1799
+ contribution: this.formatScore(score.activity * weights.activity / 100)
1800
+ },
1801
+ {
1802
+ component: "A5 - Approved",
1803
+ description: "Does the community like you?",
1804
+ score: this.formatScore(score.approved),
1805
+ weight: weights.approved,
1806
+ contribution: this.formatScore(score.approved * weights.approved / 100)
1807
+ }
1808
+ ];
1809
+ }
1810
+ /**
1811
+ * Calculate max vouches for score
1812
+ */
1813
+ getMaxVouches(composite) {
1814
+ if (composite >= 9e3) return 20;
1815
+ if (composite >= 8e3) return 15;
1816
+ if (composite >= 7e3) return 10;
1817
+ if (composite >= 6e3) return 7;
1818
+ if (composite >= 5e3) return 5;
1819
+ if (composite >= 4e3) return 3;
1820
+ return 2;
1821
+ }
1822
+ /**
1823
+ * Check if user can vouch for another
1824
+ */
1825
+ async canVouchFor(target) {
1826
+ if (!this.client.publicKey) {
1827
+ return { canVouch: false, reason: "Wallet not connected" };
1828
+ }
1829
+ if (this.client.publicKey.equals(target)) {
1830
+ return { canVouch: false, reason: "Cannot vouch for yourself" };
1831
+ }
1832
+ const myScore = await this.getScore();
1833
+ if (!myScore) {
1834
+ return { canVouch: false, reason: "No 5A score found" };
1835
+ }
1836
+ if (myScore.compositeScore < 6e3) {
1837
+ return { canVouch: false, reason: "Score too low to vouch (min 60%)" };
1838
+ }
1839
+ return { canVouch: true };
1840
+ }
1841
+ /**
1842
+ * Get score improvement suggestions
1843
+ */
1844
+ getImprovementSuggestions(score) {
1845
+ const suggestions = [];
1846
+ if (score.authenticity < 6e3) {
1847
+ suggestions.push("Complete identity verification to improve Authenticity (A1)");
1848
+ }
1849
+ if (score.accuracy < 6e3) {
1850
+ suggestions.push("Create quality content to improve Accuracy (A2)");
1851
+ }
1852
+ if (score.agility < 6e3) {
1853
+ suggestions.push("Respond faster to improve Agility (A3)");
1854
+ }
1855
+ if (score.activity < 6e3) {
1856
+ suggestions.push("Engage daily with content to improve Activity (A4)");
1857
+ }
1858
+ if (score.approved < 6e3) {
1859
+ suggestions.push("Get vouched by high-score users to improve Approved (A5)");
1860
+ }
1861
+ return suggestions;
1862
+ }
1863
+ // ============ Transaction Building ============
1864
+ /**
1865
+ * Build vouch transaction
1866
+ */
1867
+ async buildVouchTransaction(target) {
1868
+ if (!this.client.publicKey) {
1869
+ throw new Error("Wallet not connected");
1870
+ }
1871
+ const { canVouch, reason } = await this.canVouchFor(target);
1872
+ if (!canVouch) {
1873
+ throw new Error(reason);
1874
+ }
1875
+ const tx = new Transaction8();
1876
+ return tx;
1877
+ }
1878
+ };
1879
+
1880
+ // src/content/index.ts
1881
+ import { PublicKey as PublicKey10, Transaction as Transaction9 } from "@solana/web3.js";
1882
+ import { BN as BN9 } from "@coral-xyz/anchor";
1883
+ var ContentClient = class {
1884
+ constructor(client) {
1885
+ this.client = client;
1886
+ }
1887
+ /**
1888
+ * Get content record
1889
+ */
1890
+ async getContent(contentId) {
1891
+ try {
1892
+ const contentPda = this.client.pdas.getContentRecord(contentId);
1893
+ const accountInfo = await this.client.connection.connection.getAccountInfo(contentPda);
1894
+ if (!accountInfo) {
1895
+ return null;
1896
+ }
1897
+ const data = accountInfo.data;
1898
+ return {
1899
+ contentId: new Uint8Array(data.slice(8, 40)),
1900
+ creator: new PublicKey10(data.slice(40, 72)),
1901
+ contentHash: new Uint8Array(data.slice(72, 104)),
1902
+ state: data[104],
1903
+ createdAt: new BN9(data.slice(105, 113), "le"),
1904
+ editCount: data.readUInt16LE(113),
1905
+ tips: new BN9(data.slice(115, 123), "le"),
1906
+ engagementScore: new BN9(data.slice(123, 131), "le")
1907
+ };
1908
+ } catch {
1909
+ return null;
1910
+ }
1911
+ }
1912
+ /**
1913
+ * Get user's energy
1914
+ */
1915
+ async getEnergy(user) {
1916
+ const target = user || this.client.publicKey;
1917
+ if (!target) {
1918
+ throw new Error("No user specified and wallet not connected");
1919
+ }
1920
+ try {
1921
+ const energyPda = this.client.pdas.getUserEnergy(target);
1922
+ const accountInfo = await this.client.connection.connection.getAccountInfo(energyPda);
1923
+ if (!accountInfo) {
1924
+ return null;
1925
+ }
1926
+ const data = accountInfo.data;
1927
+ return {
1928
+ user: new PublicKey10(data.slice(8, 40)),
1929
+ currentEnergy: data.readUInt16LE(40),
1930
+ maxEnergy: data.readUInt16LE(42),
1931
+ lastRegenTime: new BN9(data.slice(44, 52), "le"),
1932
+ tier: data[52]
1933
+ };
1934
+ } catch {
1935
+ return null;
1936
+ }
1937
+ }
1938
+ /**
1939
+ * Get content state name
1940
+ */
1941
+ getStateName(state) {
1942
+ const states = ["Active", "Hidden", "Deleted", "Flagged"];
1943
+ return states[state] || "Unknown";
1944
+ }
1945
+ /**
1946
+ * Calculate regenerated energy
1947
+ */
1948
+ calculateRegenEnergy(energy) {
1949
+ const now = getCurrentTimestamp();
1950
+ const secondsSinceRegen = now - energy.lastRegenTime.toNumber();
1951
+ const hoursSinceRegen = secondsSinceRegen / 3600;
1952
+ const regenAmount = Math.floor(hoursSinceRegen * CONTENT_CONSTANTS.energyRegenRate);
1953
+ const newEnergy = Math.min(
1954
+ energy.maxEnergy,
1955
+ energy.currentEnergy + regenAmount
1956
+ );
1957
+ return newEnergy;
1958
+ }
1959
+ /**
1960
+ * Get time until next energy
1961
+ */
1962
+ getTimeUntilNextEnergy(energy) {
1963
+ if (energy.currentEnergy >= energy.maxEnergy) {
1964
+ return 0;
1965
+ }
1966
+ const now = getCurrentTimestamp();
1967
+ const secondsSinceRegen = now - energy.lastRegenTime.toNumber();
1968
+ const secondsPerEnergy = 3600 / CONTENT_CONSTANTS.energyRegenRate;
1969
+ const nextRegenIn = secondsPerEnergy - secondsSinceRegen % secondsPerEnergy;
1970
+ return Math.max(0, Math.ceil(nextRegenIn));
1971
+ }
1972
+ /**
1973
+ * Get time until full energy
1974
+ */
1975
+ getTimeUntilFull(energy) {
1976
+ const currentEnergy = this.calculateRegenEnergy(energy);
1977
+ if (currentEnergy >= energy.maxEnergy) {
1978
+ return 0;
1979
+ }
1980
+ const energyNeeded = energy.maxEnergy - currentEnergy;
1981
+ const secondsPerEnergy = 3600 / CONTENT_CONSTANTS.energyRegenRate;
1982
+ return Math.ceil(energyNeeded * secondsPerEnergy);
1983
+ }
1984
+ /**
1985
+ * Check if user can create content
1986
+ */
1987
+ async canCreateContent(user) {
1988
+ const energy = await this.getEnergy(user);
1989
+ if (!energy) {
1990
+ return { canCreate: false, reason: "Energy account not found" };
1991
+ }
1992
+ const currentEnergy = this.calculateRegenEnergy(energy);
1993
+ const energyNeeded = CONTENT_CONSTANTS.createCost;
1994
+ if (currentEnergy < energyNeeded) {
1995
+ return {
1996
+ canCreate: false,
1997
+ reason: `Insufficient energy (${currentEnergy}/${energyNeeded})`,
1998
+ energyNeeded,
1999
+ energyAvailable: currentEnergy
2000
+ };
2001
+ }
2002
+ return { canCreate: true, energyNeeded, energyAvailable: currentEnergy };
2003
+ }
2004
+ /**
2005
+ * Check if user can edit content
2006
+ */
2007
+ async canEditContent(contentId, user) {
2008
+ const target = user || this.client.publicKey;
2009
+ if (!target) {
2010
+ return { canEdit: false, reason: "Wallet not connected" };
2011
+ }
2012
+ const content = await this.getContent(contentId);
2013
+ if (!content) {
2014
+ return { canEdit: false, reason: "Content not found" };
2015
+ }
2016
+ if (!content.creator.equals(target)) {
2017
+ return { canEdit: false, reason: "Not content creator" };
2018
+ }
2019
+ if (content.state === 2) {
2020
+ return { canEdit: false, reason: "Content is deleted" };
2021
+ }
2022
+ const energy = await this.getEnergy(target);
2023
+ if (!energy) {
2024
+ return { canEdit: false, reason: "Energy account not found" };
2025
+ }
2026
+ const currentEnergy = this.calculateRegenEnergy(energy);
2027
+ if (currentEnergy < CONTENT_CONSTANTS.editCost) {
2028
+ return { canEdit: false, reason: "Insufficient energy" };
2029
+ }
2030
+ return { canEdit: true };
2031
+ }
2032
+ /**
2033
+ * Get content stats
2034
+ */
2035
+ async getContentStats(contentId) {
2036
+ const content = await this.getContent(contentId);
2037
+ if (!content) {
2038
+ throw new Error("Content not found");
2039
+ }
2040
+ const now = getCurrentTimestamp();
2041
+ const age = now - content.createdAt.toNumber();
2042
+ return {
2043
+ tips: formatVCoin(content.tips),
2044
+ engagementScore: content.engagementScore.toString(),
2045
+ editCount: content.editCount,
2046
+ state: this.getStateName(content.state),
2047
+ age
2048
+ };
2049
+ }
2050
+ // ============ Transaction Building ============
2051
+ /**
2052
+ * Build create content transaction
2053
+ */
2054
+ async buildCreateContentTransaction(contentHash) {
2055
+ if (!this.client.publicKey) {
2056
+ throw new Error("Wallet not connected");
2057
+ }
2058
+ const { canCreate, reason } = await this.canCreateContent();
2059
+ if (!canCreate) {
2060
+ throw new Error(reason);
2061
+ }
2062
+ if (contentHash.length !== 32) {
2063
+ throw new Error("Content hash must be 32 bytes");
2064
+ }
2065
+ const tx = new Transaction9();
2066
+ return tx;
2067
+ }
2068
+ /**
2069
+ * Build edit content transaction
2070
+ */
2071
+ async buildEditContentTransaction(contentId, newContentHash) {
2072
+ if (!this.client.publicKey) {
2073
+ throw new Error("Wallet not connected");
2074
+ }
2075
+ const { canEdit, reason } = await this.canEditContent(contentId);
2076
+ if (!canEdit) {
2077
+ throw new Error(reason);
2078
+ }
2079
+ const tx = new Transaction9();
2080
+ return tx;
2081
+ }
2082
+ /**
2083
+ * Build delete content transaction
2084
+ */
2085
+ async buildDeleteContentTransaction(contentId) {
2086
+ if (!this.client.publicKey) {
2087
+ throw new Error("Wallet not connected");
2088
+ }
2089
+ const content = await this.getContent(contentId);
2090
+ if (!content) {
2091
+ throw new Error("Content not found");
2092
+ }
2093
+ if (!content.creator.equals(this.client.publicKey)) {
2094
+ throw new Error("Not content creator");
2095
+ }
2096
+ const tx = new Transaction9();
2097
+ return tx;
2098
+ }
2099
+ };
2100
+
2101
+ // src/client.ts
2102
+ import { Connection as Connection2 } from "@solana/web3.js";
2103
+ import { AnchorProvider as AnchorProvider2, BN as BN10 } from "@coral-xyz/anchor";
2104
+ import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
2105
+ var ViWoClient = class {
2106
+ constructor(config) {
2107
+ if (config.connection instanceof Connection2) {
2108
+ this.connection = new ViWoConnection({
2109
+ endpoint: config.connection.rpcEndpoint,
2110
+ commitment: "confirmed"
2111
+ });
2112
+ } else {
2113
+ this.connection = new ViWoConnection(config.connection);
2114
+ }
2115
+ this.wallet = config.wallet || null;
2116
+ this.programIds = {
2117
+ ...PROGRAM_IDS,
2118
+ ...config.programIds
2119
+ };
2120
+ this.pdas = new PDAs(this.programIds);
2121
+ this.staking = new StakingClient(this);
2122
+ this.governance = new GovernanceClient(this);
2123
+ this.rewards = new RewardsClient(this);
2124
+ this.vilink = new ViLinkClient(this);
2125
+ this.gasless = new GaslessClient(this);
2126
+ this.identity = new IdentityClient(this);
2127
+ this.fivea = new FiveAClient(this);
2128
+ this.content = new ContentClient(this);
2129
+ }
2130
+ /**
2131
+ * Get the wallet public key
2132
+ */
2133
+ get publicKey() {
2134
+ return this.wallet?.publicKey || null;
2135
+ }
2136
+ /**
2137
+ * Check if wallet is connected
2138
+ */
2139
+ get isConnected() {
2140
+ return this.wallet !== null && this.wallet.publicKey !== null;
2141
+ }
2142
+ /**
2143
+ * Set wallet adapter
2144
+ */
2145
+ setWallet(wallet) {
2146
+ this.wallet = wallet;
2147
+ this.staking = new StakingClient(this);
2148
+ this.governance = new GovernanceClient(this);
2149
+ this.rewards = new RewardsClient(this);
2150
+ this.vilink = new ViLinkClient(this);
2151
+ this.gasless = new GaslessClient(this);
2152
+ this.identity = new IdentityClient(this);
2153
+ this.fivea = new FiveAClient(this);
2154
+ this.content = new ContentClient(this);
2155
+ }
2156
+ /**
2157
+ * Get Anchor provider
2158
+ */
2159
+ getProvider() {
2160
+ if (!this.wallet || !this.wallet.publicKey) {
2161
+ return null;
2162
+ }
2163
+ return new AnchorProvider2(
2164
+ this.connection.connection,
2165
+ this.wallet,
2166
+ { commitment: this.connection.commitment }
2167
+ );
2168
+ }
2169
+ /**
2170
+ * Send and confirm transaction
2171
+ */
2172
+ async sendTransaction(tx) {
2173
+ if (!this.wallet) {
2174
+ throw new Error("Wallet not connected");
2175
+ }
2176
+ const { blockhash, lastValidBlockHeight } = await this.connection.connection.getLatestBlockhash();
2177
+ tx.recentBlockhash = blockhash;
2178
+ tx.feePayer = this.wallet.publicKey;
2179
+ const signedTx = await this.wallet.signTransaction(tx);
2180
+ const signature = await this.connection.connection.sendRawTransaction(
2181
+ signedTx.serialize()
2182
+ );
2183
+ await this.connection.connection.confirmTransaction({
2184
+ signature,
2185
+ blockhash,
2186
+ lastValidBlockHeight
2187
+ });
2188
+ return signature;
2189
+ }
2190
+ /**
2191
+ * Get VCoin balance
2192
+ */
2193
+ async getVCoinBalance(user) {
2194
+ const target = user || this.publicKey;
2195
+ if (!target) {
2196
+ throw new Error("No user specified and wallet not connected");
2197
+ }
2198
+ try {
2199
+ const tokenAccounts = await this.connection.connection.getTokenAccountsByOwner(
2200
+ target,
2201
+ { programId: TOKEN_2022_PROGRAM_ID }
2202
+ );
2203
+ let balance = new BN10(0);
2204
+ for (const { account } of tokenAccounts.value) {
2205
+ const data = account.data;
2206
+ const amount = data.slice(64, 72);
2207
+ balance = balance.add(new BN10(amount, "le"));
2208
+ }
2209
+ return balance;
2210
+ } catch {
2211
+ return new BN10(0);
2212
+ }
2213
+ }
2214
+ /**
2215
+ * Get veVCoin balance
2216
+ */
2217
+ async getVeVCoinBalance(user) {
2218
+ const target = user || this.publicKey;
2219
+ if (!target) {
2220
+ throw new Error("No user specified and wallet not connected");
2221
+ }
2222
+ try {
2223
+ const stakeData = await this.staking.getUserStake(target);
2224
+ return stakeData?.vevcoinBalance || new BN10(0);
2225
+ } catch {
2226
+ return new BN10(0);
2227
+ }
2228
+ }
2229
+ /**
2230
+ * Check connection health
2231
+ */
2232
+ async healthCheck() {
2233
+ try {
2234
+ const [connected, slot] = await Promise.all([
2235
+ this.connection.isHealthy(),
2236
+ this.connection.getSlot()
2237
+ ]);
2238
+ const blockTime = await this.connection.getBlockTime();
2239
+ return { connected, slot, blockTime };
2240
+ } catch {
2241
+ return { connected: false, slot: null, blockTime: null };
2242
+ }
2243
+ }
2244
+ };
2245
+ export {
2246
+ ACTION_SCOPES,
2247
+ ActionType,
2248
+ BN,
2249
+ CONTENT_CONSTANTS,
2250
+ ContentClient,
2251
+ ContentState,
2252
+ FIVE_A_CONSTANTS,
2253
+ FeeMethod,
2254
+ FiveAClient,
2255
+ GASLESS_CONSTANTS,
2256
+ GOVERNANCE_CONSTANTS,
2257
+ GaslessClient,
2258
+ GovernanceClient,
2259
+ IdentityClient,
2260
+ LOCK_DURATIONS,
2261
+ PDAs,
2262
+ PROGRAM_IDS,
2263
+ ProposalStatus,
2264
+ RewardsClient,
2265
+ SEEDS,
2266
+ SSCRE_CONSTANTS,
2267
+ STAKING_TIERS,
2268
+ StakingClient,
2269
+ StakingTier,
2270
+ TransactionBuilder,
2271
+ VCOIN_DECIMALS,
2272
+ VCOIN_INITIAL_CIRCULATING,
2273
+ VCOIN_TOTAL_SUPPLY,
2274
+ VEVCOIN_DECIMALS,
2275
+ VILINK_CONSTANTS,
2276
+ VerificationLevel,
2277
+ ViLinkClient,
2278
+ ViWoClient,
2279
+ ViWoConnection,
2280
+ dateToTimestamp,
2281
+ formatVCoin,
2282
+ getCurrentTimestamp,
2283
+ parseVCoin,
2284
+ timestampToDate
2285
+ };