@triadxyz/triad-protocol 0.0.1-beta

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/stake.js ADDED
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const web3_js_1 = require("@solana/web3.js");
13
+ const helpers_1 = require("./utils/helpers");
14
+ const constants_1 = require("./utils/constants");
15
+ class Stake {
16
+ constructor(program, provider) {
17
+ this.provider = provider;
18
+ this.program = program;
19
+ }
20
+ /**
21
+ * Get all Stake Vaults
22
+ */
23
+ getStakeVaults() {
24
+ return __awaiter(this, void 0, void 0, function* () {
25
+ const response = yield this.program.account.stakeVault.all();
26
+ return response.map((stakeVault) => ({
27
+ name: stakeVault.account.name,
28
+ collection: stakeVault.account.collection,
29
+ slots: stakeVault.account.slots.toNumber(),
30
+ amount: stakeVault.account.amount.toNumber(),
31
+ isLocked: stakeVault.account.isLocked,
32
+ usersPaid: stakeVault.account.usersPaid,
33
+ amountPaid: stakeVault.account.amountPaid.toNumber(),
34
+ amountUsers: stakeVault.account.amountUsers.toNumber(),
35
+ apr: stakeVault.account.apr,
36
+ initTs: stakeVault.account.initTs.toNumber(),
37
+ endTs: stakeVault.account.endTs.toNumber()
38
+ }));
39
+ });
40
+ }
41
+ /**
42
+ * Get Stake Vault by name
43
+ * @param stakeVault - Stake Vault name
44
+ */
45
+ getStakeVaultByName(stakeVault) {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ const StakeVault = (0, helpers_1.getStakeVaultAddressSync)(this.program.programId, stakeVault);
48
+ const response = yield this.program.account.stakeVault.fetch(StakeVault);
49
+ return {
50
+ name: response.name,
51
+ collection: response.collection,
52
+ slots: response.slots.toNumber(),
53
+ amount: response.amount.toNumber(),
54
+ isLocked: response.isLocked,
55
+ usersPaid: response.usersPaid,
56
+ amountPaid: response.amountPaid.toNumber(),
57
+ amountUsers: response.amountUsers.toNumber(),
58
+ apr: response.apr,
59
+ initTs: response.initTs.toNumber(),
60
+ endTs: response.endTs.toNumber()
61
+ };
62
+ });
63
+ }
64
+ /**
65
+ * Get all stakes
66
+ */
67
+ getStakes() {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ const response = yield this.program.account.stake.all();
70
+ return response.map((stake) => ({
71
+ name: stake.account.name,
72
+ collections: stake.account.collections,
73
+ rarity: Object.keys(stake.account.rarity)[0],
74
+ stakeVault: stake.account.stakeVault.toBase58(),
75
+ authority: stake.account.authority.toBase58(),
76
+ initTs: stake.account.initTs.toNumber(),
77
+ isLocked: stake.account.isLocked,
78
+ withdrawTs: stake.account.withdrawTs.toNumber(),
79
+ mint: stake.account.mint.toBase58(),
80
+ stakeRewards: stake.account.stakeRewards.toBase58()
81
+ }));
82
+ });
83
+ }
84
+ /**
85
+ * Get Stake by wallet
86
+ * @param wallet - User wallet
87
+ */
88
+ getStakeByWallet(wallet) {
89
+ return __awaiter(this, void 0, void 0, function* () {
90
+ const response = yield this.program.account.stake.all();
91
+ return response
92
+ .filter((stake) => stake.account.authority.equals(wallet))
93
+ .map((stake) => ({
94
+ name: stake.account.name,
95
+ collections: stake.account.collections,
96
+ rarity: Object.keys(stake.account.rarity)[0],
97
+ stakeVault: stake.account.stakeVault.toBase58(),
98
+ authority: stake.account.authority.toBase58(),
99
+ initTs: stake.account.initTs.toNumber(),
100
+ isLocked: stake.account.isLocked,
101
+ withdrawTs: stake.account.withdrawTs.toNumber(),
102
+ mint: stake.account.mint.toBase58(),
103
+ stakeRewards: stake.account.stakeRewards.toBase58()
104
+ }));
105
+ });
106
+ }
107
+ getStakeVaultRewards(stakeVault) {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ const StakeVault = (0, helpers_1.getStakeVaultAddressSync)(this.program.programId, stakeVault);
110
+ const response = yield this.program.account.stakeVault.fetch(StakeVault);
111
+ const data = {
112
+ amount: 0,
113
+ perDay: 0,
114
+ perWeek: 0,
115
+ perMonth: 0,
116
+ period: 0,
117
+ days: []
118
+ };
119
+ const amount = response.amount.toNumber() / Math.pow(10, constants_1.TTRIAD_DECIMALS);
120
+ data.period =
121
+ (response.endTs.toNumber() * 1000 - response.initTs.toNumber() * 1000) /
122
+ (1000 * 60 * 60 * 24);
123
+ data.amount = amount - (amount * constants_1.TTRIAD_FEE) / 100;
124
+ data.perDay = data.amount / data.period;
125
+ data.perWeek = data.perDay * 7;
126
+ data.perMonth = data.perDay * 30;
127
+ const endTsInMs = response.endTs.toNumber() * 1000;
128
+ const initTsInMs = response.initTs.toNumber() * 1000;
129
+ let currentTs = initTsInMs;
130
+ while (currentTs <= endTsInMs) {
131
+ data.days.push(currentTs);
132
+ currentTs = currentTs + 1000 * 60 * 60 * 24;
133
+ }
134
+ return data;
135
+ });
136
+ }
137
+ getStakeRewardsByWallet(wallet, stakeVaultRewards) {
138
+ return __awaiter(this, void 0, void 0, function* () {
139
+ const stakes = yield this.getStakeByWallet(wallet);
140
+ const rewards = {};
141
+ for (const day of stakeVaultRewards.days) {
142
+ stakes.forEach((stake) => {
143
+ const date = stake.initTs * 1000;
144
+ const currentDate = new Date().getTime();
145
+ if (date <= day && day <= currentDate) {
146
+ const key = new Date(day).toISOString().split('T')[0];
147
+ if (!rewards[key]) {
148
+ rewards[key] = [];
149
+ }
150
+ rewards[key].push(stake);
151
+ }
152
+ });
153
+ }
154
+ return rewards;
155
+ });
156
+ }
157
+ /**
158
+ * Stake NFT
159
+ * @param name - NFT name
160
+ * @param wallet - User wallet
161
+ * @param mint - NFT mint
162
+ * @param collections - NFT collections
163
+ * @param rarity - NFT rarity
164
+ *
165
+ */
166
+ stake({ name, wallet, mint, collections, rarity, stakeVault }, options) {
167
+ return __awaiter(this, void 0, void 0, function* () {
168
+ const FromAta = (0, helpers_1.getATASync)(wallet, mint);
169
+ const StakeVault = (0, helpers_1.getStakeVaultAddressSync)(this.program.programId, stakeVault);
170
+ const ToAta = (0, helpers_1.getATASync)(StakeVault, mint);
171
+ let items = [];
172
+ Object.keys(collections).forEach((key) => {
173
+ if (collections[key]) {
174
+ items.push({ [key]: {} });
175
+ }
176
+ });
177
+ const method = this.program.methods
178
+ .stake({
179
+ name,
180
+ collections: items,
181
+ rarity,
182
+ stakeVault
183
+ })
184
+ .accounts({
185
+ signer: wallet,
186
+ fromAta: FromAta,
187
+ toAta: ToAta,
188
+ mint: mint
189
+ });
190
+ if (options === null || options === void 0 ? void 0 : options.microLamports) {
191
+ method.postInstructions([
192
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
193
+ microLamports: options.microLamports
194
+ })
195
+ ]);
196
+ }
197
+ return method.rpc({ skipPreflight: options === null || options === void 0 ? void 0 : options.skipPreflight });
198
+ });
199
+ }
200
+ /**
201
+ * Initialize Stake Vault
202
+ * @param name - The ticker's name
203
+ * @param slots - Amount available to users joining the vault
204
+ * @param collection - The Collection name
205
+ *
206
+ */
207
+ initializeStakeVault({ name, slots, collection, amount }, options) {
208
+ return __awaiter(this, void 0, void 0, function* () {
209
+ const method = this.program.methods
210
+ .initializeStakeVault({
211
+ name,
212
+ slots,
213
+ collection,
214
+ amount
215
+ })
216
+ .accounts({
217
+ signer: this.provider.wallet.publicKey
218
+ });
219
+ if (options === null || options === void 0 ? void 0 : options.microLamports) {
220
+ method.postInstructions([
221
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
222
+ microLamports: options.microLamports
223
+ })
224
+ ]);
225
+ }
226
+ return method.rpc({ skipPreflight: options === null || options === void 0 ? void 0 : options.skipPreflight });
227
+ });
228
+ }
229
+ /**
230
+ * Deposit Stake Rewards
231
+ * @param wallet - User wallet
232
+ * @param mint - NFT mint
233
+ * @param amount - Reward amount
234
+ *
235
+ */
236
+ depositStakeRewards({ wallet, mint, amount, stakeVault }, options) {
237
+ return __awaiter(this, void 0, void 0, function* () {
238
+ const FromAta = (0, helpers_1.getATASync)(wallet, mint);
239
+ const StakeVault = (0, helpers_1.getStakeVaultAddressSync)(this.program.programId, stakeVault);
240
+ const ToAta = (0, helpers_1.getATASync)(StakeVault, mint);
241
+ const method = this.program.methods
242
+ .depositStakeRewards({
243
+ amount,
244
+ stakeVault
245
+ })
246
+ .accounts({
247
+ signer: wallet,
248
+ fromAta: FromAta,
249
+ toAta: ToAta,
250
+ mint: mint
251
+ });
252
+ if (options === null || options === void 0 ? void 0 : options.microLamports) {
253
+ method.postInstructions([
254
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
255
+ microLamports: options.microLamports
256
+ })
257
+ ]);
258
+ }
259
+ return method.rpc({ skipPreflight: options === null || options === void 0 ? void 0 : options.skipPreflight });
260
+ });
261
+ }
262
+ /**
263
+ * Request Withdraw
264
+ * @param wallet - User wallet
265
+ * @param nftName - NFT name
266
+ * @param stakeVault - Name of the stake vault
267
+ *
268
+ */
269
+ requestWithdraw({ wallet, nftName, mint, stakeVault }, options) {
270
+ return __awaiter(this, void 0, void 0, function* () {
271
+ const method = this.program.methods
272
+ .requestWithdrawNft({
273
+ nftName,
274
+ stakeVault
275
+ })
276
+ .accounts({
277
+ signer: wallet,
278
+ mint: mint
279
+ });
280
+ if (options === null || options === void 0 ? void 0 : options.microLamports) {
281
+ method.postInstructions([
282
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
283
+ microLamports: options.microLamports
284
+ })
285
+ ]);
286
+ }
287
+ return method.rpc({ skipPreflight: options === null || options === void 0 ? void 0 : options.skipPreflight });
288
+ });
289
+ }
290
+ /**
291
+ * Withdraw NFT
292
+ * @param wallet - User wallet
293
+ * @param nftName - NFT name
294
+ * @param mint - NFT mint
295
+ * @param stakeVault - Name of the stake vault
296
+ *
297
+ */
298
+ withdraw({ wallet, nftName, mint, stakeVault }, options) {
299
+ return __awaiter(this, void 0, void 0, function* () {
300
+ const StakeVault = (0, helpers_1.getStakeVaultAddressSync)(this.program.programId, stakeVault);
301
+ const FromAta = (0, helpers_1.getATASync)(StakeVault, mint);
302
+ const ToAta = (0, helpers_1.getATASync)(wallet, mint);
303
+ const method = this.program.methods
304
+ .withdrawNft({
305
+ nftName,
306
+ stakeVault
307
+ })
308
+ .accounts({
309
+ signer: wallet,
310
+ fromAta: FromAta,
311
+ toAta: ToAta,
312
+ mint: mint
313
+ });
314
+ if (options === null || options === void 0 ? void 0 : options.microLamports) {
315
+ method.postInstructions([
316
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
317
+ microLamports: options.microLamports
318
+ })
319
+ ]);
320
+ }
321
+ return method.rpc({ skipPreflight: options === null || options === void 0 ? void 0 : options.skipPreflight });
322
+ });
323
+ }
324
+ }
325
+ exports.default = Stake;
package/dist/test.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/test.js ADDED
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const web3_js_1 = require("@solana/web3.js");
17
+ const index_1 = __importDefault(require("./index"));
18
+ const anchor_1 = require("@coral-xyz/anchor");
19
+ const constants_1 = require("./utils/constants");
20
+ const stakes_json_1 = __importDefault(require("./utils/stakes.json"));
21
+ const file = fs_1.default.readFileSync('/Users/dannpl/.config/solana/triad-man.json');
22
+ const rpc_file = fs_1.default.readFileSync('/Users/dannpl/.config/solana/rpc.txt');
23
+ const keypair = web3_js_1.Keypair.fromSecretKey(new Uint8Array(JSON.parse(file.toString())));
24
+ const connection = new web3_js_1.Connection(rpc_file.toString(), 'confirmed');
25
+ const wallet = new anchor_1.Wallet(keypair);
26
+ const triadProtocol = new index_1.default(connection, wallet);
27
+ const requestWithdraw = () => __awaiter(void 0, void 0, void 0, function* () {
28
+ const response = yield triadProtocol.stake.requestWithdraw({
29
+ wallet: wallet.publicKey,
30
+ nftName: 'Triad 0',
31
+ mint: new web3_js_1.PublicKey('FXRhaGeYue7bMCwcksNw4hJRY7jZ1YMwgmCu1Y8fyUNd'),
32
+ stakeVault: 'Rev 1'
33
+ }, {
34
+ microLamports: 10000,
35
+ skipPreflight: true
36
+ });
37
+ console.log(response);
38
+ });
39
+ const withdraw = () => __awaiter(void 0, void 0, void 0, function* () {
40
+ const response = yield triadProtocol.stake.withdraw({
41
+ wallet: wallet.publicKey,
42
+ nftName: 'Triad 0',
43
+ mint: new web3_js_1.PublicKey('FXRhaGeYue7bMCwcksNw4hJRY7jZ1YMwgmCu1Y8fyUNd'),
44
+ stakeVault: 'Rev 1'
45
+ }, {
46
+ microLamports: 10000,
47
+ skipPreflight: true
48
+ });
49
+ console.log(response);
50
+ });
51
+ const getStake = () => __awaiter(void 0, void 0, void 0, function* () {
52
+ const response = yield triadProtocol.stake.getStakeByWallet(wallet.publicKey);
53
+ const stakeVaults = yield triadProtocol.stake.getStakeVaults();
54
+ console.log(response);
55
+ console.log(stakeVaults);
56
+ });
57
+ const stake = () => __awaiter(void 0, void 0, void 0, function* () {
58
+ const response = yield triadProtocol.stake.stake({
59
+ name: 'Triad 0',
60
+ wallet: wallet.publicKey,
61
+ stakeVault: 'Rev 1',
62
+ rarity: { mythic: {} },
63
+ collections: {
64
+ coleta: false,
65
+ undead: false,
66
+ alligators: false,
67
+ pyth: false
68
+ },
69
+ mint: new web3_js_1.PublicKey('FXRhaGeYue7bMCwcksNw4hJRY7jZ1YMwgmCu1Y8fyUNd')
70
+ }, {
71
+ skipPreflight: true,
72
+ microLamports: 20000
73
+ });
74
+ console.log(response);
75
+ });
76
+ stake();
77
+ const getDailyBaseRewards = () => __awaiter(void 0, void 0, void 0, function* () {
78
+ const stakeVaultRewards = yield triadProtocol.stake.getStakeVaultRewards(constants_1.STAKE_SEASON);
79
+ const days = {};
80
+ stakes_json_1.default.forEach((stake) => {
81
+ const keys = Object.keys(stake.rewards);
82
+ keys.forEach((key) => {
83
+ if (!days[key]) {
84
+ days[key] = 0;
85
+ }
86
+ days[key] += Object.values(stake.rewards[key]).length;
87
+ });
88
+ });
89
+ const values = {};
90
+ Object.keys(days).forEach((key) => {
91
+ values[key] = stakeVaultRewards.perDay / days[key];
92
+ });
93
+ console.log('Daily Rewards:', values);
94
+ });
95
+ const getStakesByWallet = () => __awaiter(void 0, void 0, void 0, function* () {
96
+ const response = yield triadProtocol.stake.getStakes();
97
+ const stakeVaultRewards = yield triadProtocol.stake.getStakeVaultRewards(constants_1.STAKE_SEASON);
98
+ const users = response
99
+ .map((stake) => stake.authority)
100
+ .filter((value, index, self) => self.indexOf(value) === index);
101
+ const data = [];
102
+ for (const user of users) {
103
+ const rewardsByWallet = yield triadProtocol.stake.getStakeRewardsByWallet(new web3_js_1.PublicKey(user), stakeVaultRewards);
104
+ data.push({
105
+ wallet: user,
106
+ rewards: rewardsByWallet
107
+ });
108
+ }
109
+ fs_1.default.writeFileSync('./src/utils/stakes.json', JSON.stringify(data, null, 2));
110
+ });
@@ -0,0 +1,44 @@
1
+ /// <reference types="bn.js" />
2
+ import { AnchorProvider, Program } from '@coral-xyz/anchor';
3
+ import { PublicKey } from '@solana/web3.js';
4
+ import { TriadProtocol } from './types/triad_protocol';
5
+ export default class Ticker {
6
+ program: Program<TriadProtocol>;
7
+ provider: AnchorProvider;
8
+ constructor(program: Program<TriadProtocol>, provider: AnchorProvider);
9
+ /**
10
+ * Get all tickers
11
+ */
12
+ getTickers(): Promise<import("@coral-xyz/anchor").ProgramAccount<{
13
+ initTs: import("bn.js");
14
+ updatedTs: import("bn.js");
15
+ bump: number;
16
+ authority: PublicKey;
17
+ name: string;
18
+ protocolProgramId: PublicKey;
19
+ price: import("bn.js");
20
+ vault: PublicKey;
21
+ }>[]>;
22
+ /**
23
+ * Create a new ticker
24
+ * @param name - The ticker's name
25
+ * @param protocolProgramId - The program ID of the protocol
26
+ * @param token mint - Token mint for the ticker (e.g. USDC)
27
+ *
28
+ */
29
+ createTicker({ name, protocolProgramId, tokenMint }: {
30
+ name: string;
31
+ protocolProgramId: PublicKey;
32
+ tokenMint: PublicKey;
33
+ }): Promise<string>;
34
+ /**
35
+ * Update a ticker's price
36
+ * @param name - The ticker's name
37
+ * @param price - The ticker's price
38
+ *
39
+ */
40
+ updateTickerPrice({ name, price }: {
41
+ name: string;
42
+ price: string;
43
+ }): Promise<string>;
44
+ }
package/dist/ticker.js ADDED
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const web3_js_1 = require("@solana/web3.js");
13
+ const helpers_1 = require("./utils/helpers");
14
+ const bn_js_1 = require("bn.js");
15
+ class Ticker {
16
+ constructor(program, provider) {
17
+ this.provider = provider;
18
+ this.program = program;
19
+ }
20
+ /**
21
+ * Get all tickers
22
+ */
23
+ getTickers() {
24
+ return __awaiter(this, void 0, void 0, function* () {
25
+ return this.program.account.ticker.all();
26
+ });
27
+ }
28
+ /**
29
+ * Create a new ticker
30
+ * @param name - The ticker's name
31
+ * @param protocolProgramId - The program ID of the protocol
32
+ * @param token mint - Token mint for the ticker (e.g. USDC)
33
+ *
34
+ */
35
+ createTicker({ name, protocolProgramId, tokenMint }) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ return this.program.methods
38
+ .createTicker({ name, protocolProgramId })
39
+ .accounts({
40
+ signer: this.provider.wallet.publicKey,
41
+ payerTokenMint: tokenMint
42
+ })
43
+ .rpc();
44
+ });
45
+ }
46
+ /**
47
+ * Update a ticker's price
48
+ * @param name - The ticker's name
49
+ * @param price - The ticker's price
50
+ *
51
+ */
52
+ updateTickerPrice({ name, price }) {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ const TickerPDA = (0, helpers_1.getTickerAddressSync)(this.program.programId, name);
55
+ const instructions = [
56
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
57
+ microLamports: 12000
58
+ })
59
+ ];
60
+ instructions.push(yield this.program.methods
61
+ .updateTickerPrice({ price: new bn_js_1.BN(price) })
62
+ .accounts({
63
+ signer: this.provider.wallet.publicKey,
64
+ ticker: TickerPDA
65
+ })
66
+ .instruction());
67
+ const { blockhash } = yield this.provider.connection.getLatestBlockhash();
68
+ const message = new web3_js_1.TransactionMessage({
69
+ payerKey: this.provider.wallet.publicKey,
70
+ recentBlockhash: blockhash,
71
+ instructions
72
+ }).compileToV0Message();
73
+ return this.provider.sendAndConfirm(new web3_js_1.VersionedTransaction(message));
74
+ });
75
+ }
76
+ }
77
+ exports.default = Ticker;