-
Notifications
You must be signed in to change notification settings - Fork 5
/
drops.ts
823 lines (770 loc) · 30.5 KB
/
drops.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
import BN from 'bn.js';
//import * as nearAPI from "near-api-js";
import { FinalExecutionOutcome } from '@near-wallet-selector/core';
import {
BrowserWalletBehaviour,
Wallet
} from '@near-wallet-selector/core/lib/wallet/wallet.types';
//import { Account } from "near-api-js";
import { Account } from '@near-js/accounts';
import { Transaction, stringifyJsonOrBytes } from '@near-js/transactions';
import { parseNearAmount } from '@near-js/utils';
import {
assert,
assertDropIdUnique,
assertValidDropConfig,
assertValidFCData,
isSupportedKeypomContract,
isValidAccountObj
} from './checks';
import { getEnv } from './keypom';
import {
convertBasicTransaction,
estimateRequiredDeposit,
ftTransferCall,
generateKeys,
generatePerUsePasswords,
getStorageBase,
key2str,
keypomView,
nearArgsToYocto,
nftTransferCall,
parseFTAmount
} from './keypom-utils';
import { DropConfig } from './types/drops';
import { FCData } from './types/fc';
import { FTData } from './types/ft';
import { BasicTransaction } from './types/general';
import { NFTData } from './types/nft';
import { CreateDropProtocolArgs, CreateOrAddReturn } from './types/params';
import {
ProtocolReturnedDrop,
ProtocolReturnedDropConfig,
ProtocolReturnedMethod
} from './types/protocol';
import { SimpleData } from './types/simple';
import { getDropInformation, getUserBalance } from './views';
type AnyWallet = BrowserWalletBehaviour | Wallet;
export const KEY_LIMIT = 50;
/**
* Creates a new drop based on parameters passed in. This drop can have keys that are manually generated and passed in, or automatically generated. If they're
* automatically generated, they can be based off a set of entropy. For NFT and FT drops, assets can automatically be sent to Keypom to register keys as part of the payload.
* The deposit is estimated based on parameters that are passed in and the transaction can be returned instead of signed and sent to the network. This can allow you to get the
* required deposit from the return value and use that to fund the account's Keypom balance to avoid multiple transactions being signed in the case of a drop with many keys.
*
* @return {Promise<CreateOrAddReturn>} Object containing: the drop ID, the responses of the execution, as well as any auto generated keys (if any).
*
* @example
* Create a basic simple drop containing 10 keys each with 1 $NEAR. Each key is completely random:
* ```js
* // Initialize the SDK for the given network and NEAR connection. No entropy passed in so any auto generated keys will
* // be completely random unless otherwise overwritten.
* await initKeypom({
* network: "testnet",
* funder: {
* accountId: "benji_demo.testnet",
* secretKey: "ed25519:5yARProkcALbxaSQ66aYZMSBPWL9uPBmkoQGjV3oi2ddQDMh1teMAbz7jqNV9oVyMy7kZNREjYvWPqjcA6LW9Jb1"
* }
* });
*
* // Create a drop with 10 completely random keys. The return value `keys` contains information about the generated keys
* const {keys} = await createDrop({
* numKeys: 10,
* depositPerUseNEAR: 1,
* });
*
* console.log('public keys: ', keys.publicKeys);
* console.log('private keys: ', keys.secretKeys);
* ```
*
* @example
* Init funder with root entropy and generate deterministic keys for a drop. Compare with manually generated keys:
* ```js
* // Initialize the SDK for the given network and NEAR connection. Root entropy is passed into the funder account so any generated keys
* // Will be based off that entropy.
* await initKeypom({
* network: "testnet",
* funder: {
* accountId: "benji_demo.testnet",
* secretKey: "ed25519:5yARProkcALbxaSQ66aYZMSBPWL9uPBmkoQGjV3oi2ddQDMh1teMAbz7jqNV9oVyMy7kZNREjYvWPqjcA6LW9Jb1",
* rootEntropy: "my-global-secret-password"
* }
* });
*
* // Create a simple drop with 5 keys. Each key will be derived based on the rootEntropy of the funder, the drop ID, and key nonce.
* const { keys: keysFromDrop, dropId } = await createDrop({
* numKeys: 5,
* depositPerUseNEAR: 1,
* });
*
* // Deterministically Generate the Private Keys:
* const nonceDropIdMeta = Array.from({length: 5}, (_, i) => `${dropId}_${i}`);
* const manualKeys = await generateKeys({
* numKeys: 5,
* rootEntropy: "my-global-secret-password",
* metaEntropy: nonceDropIdMeta
* })
*
* // Get the public and private keys from the keys generated by the drop
* const {publicKeys, secretKeys} = keysFromDrop;
* // Get the public and private keys from the keys that were manually generated
* const {publicKeys: pubKeysGenerated, secretKeys: secretKeysGenerated} = manualKeys;
* // These should match!
* console.log('secretKeys: ', secretKeys)
* console.log('secretKeysGenerated: ', secretKeysGenerated)
*
* // These should match!
* console.log('publicKeys: ', publicKeys)
* console.log('pubKeysGenerated: ', pubKeysGenerated)
* ```
*
* @example
* Use manually generated keys to create a drop:
* ```js
* // Initialize the SDK for the given network and NEAR connection. No entropy passed in so any auto generated keys will
* // be completely random unless otherwise overwritten.
* await initKeypom({
* network: "testnet",
* funder: {
* accountId: "benji_demo.testnet",
* secretKey: "ed25519:5yARProkcALbxaSQ66aYZMSBPWL9uPBmkoQGjV3oi2ddQDMh1teMAbz7jqNV9oVyMy7kZNREjYvWPqjcA6LW9Jb1"
* }
* });
*
* // Generate 10 random keys
* const {publicKeys} = await generateKeys({
* numKeys: 10
* });
*
* // Create a drop using the keys that were generated. Since keys are passed in, the return value won't contain information about the keys.
* await createDrop({
* publicKeys,
* depositPerUseNEAR: 1,
* });
* ```
*
* @example
* Create a simple drop with 1 key and 1 use per key. This 1 use-key should be password protected based on a base-password:
* ```js
* // Initialize the SDK for the given network and NEAR connection
* await initKeypom({
* network: "testnet",
* funder: {
* accountId: "benji_demo.testnet",
* secretKey: "ed25519:5yARProkcALbxaSQ66aYZMSBPWL9uPBmkoQGjV3oi2ddQDMh1teMAbz7jqNV9oVyMy7kZNREjYvWPqjcA6LW9Jb1"
* }
* });
*
*
* const basePassword = "my-cool-password123";
* // Create a simple drop with 1 $NEAR and pass in a base password to create a unique password for each use of each key
* const {keys} = await createDrop({
* numKeys: 1,
* depositPerUseNEAR: 1,
* basePassword
* });
*
* // Create the password to pass into claim which is a hash of the basePassword, public key and whichever use we are on
* let currentUse = 1;
* let passwordForClaim = await hashPassword(basePassword + keys.publicKeys[0] + currentUse.toString());
* ```
* @group Creating, And Claiming Drops
*/
export const createDrop = async ({
account,
wallet,
dropId,
numKeys = 0,
publicKeys,
rootEntropy,
depositPerUseNEAR,
depositPerUseYocto,
metadata,
requiredGas,
config = {},
ftData,
nftData,
simpleData = {},
fcData,
basePassword,
passwordProtectedUses,
useBalance = false,
returnTransactions = false,
successUrl,
}: {
/** Account object that if passed in, will be used to sign the txn instead of the funder account. */
account?: Account;
/** If using a browser wallet through wallet selector and that wallet should sign the transaction, pass in the object. */
wallet?: AnyWallet;
/**
* Specify how many keys should be generated for the drop. If the funder has rootEntropy set OR rootEntropy is passed in, the keys will be
* deterministically generated using the drop ID, key nonce, and entropy. Otherwise, each key will be generated randomly.
* If this is not passed in, the publicKeys parameter *must* be passed in.
*/
numKeys?: number;
/** Pass in a custom set of publicKeys to add to the drop. If this is not passed in, keys will be generated based on the numKeys parameter. */
publicKeys?: string[];
/** How much $NEAR should be contained in each link. Unit in $NEAR (i.e `1` = 1 $NEAR) */
depositPerUseNEAR?: number | string;
/** How much $yoctoNEAR should be contained in each link. Unit in yoctoNEAR (1 yoctoNEAR = 1e-24 $NEAR) */
depositPerUseYocto?: string;
/** Specify a custom drop ID rather than using the incrementing nonce on the contract. */
dropId?: string;
/** Allows specific drop behaviors to be configured such as the number of uses each key / link will have. */
config?: DropConfig;
/** String of metadata to attach to the drop. This can be whatever you would like and is optional. Often this is stringified JSON. */
metadata?: string;
/** Allows you to overload how much gas should be attached to the transaction when the key is claimed. This should be in Gas units (1 TGas = 1000000000000). By default, 100 TGas is attached. */
requiredGas?: string;
/** For creating a simple drop, this contains necessary configurable information about the drop. */
simpleData?: SimpleData;
/** For creating a fungible token drop, this contains necessary configurable information about the drop. */
ftData?: FTData;
/** For creating a non-fungible token drop, this contains necessary configurable information about the drop. */
nftData?: NFTData;
/** For creating a function call drop, this contains necessary configurable information about the drop. */
fcData?: FCData;
/** Specify an entropy to use for generating keys (will overload the funder's rootEntropy if applicable). This parameter only matters if the publicKeys variable is not passed in. */
rootEntropy?: string;
/** For doing password protected drops, this is the base password that will be used to generate all the passwords. It will be double hashed with the public keys. If specified, by default, all key uses will have their own unique password unless passwordProtectedUses is passed in. */
basePassword?: string;
/** For doing password protected drops, specifies exactly which uses will be password protected. The uses are NOT zero indexed (i.e 1st use = 1). Each use will have a different, unique password generated via double hashing the base password + public key + key use. */
passwordProtectedUses?: number[];
/** If the account has a balance within the Keypom contract, set this to true to avoid the need to attach a deposit. If the account doesn't have enough balance, an error will throw. */
useBalance?: boolean;
/** If true, the transaction will be returned instead of being signed and sent. This is useful for getting the requiredDeposit from the return value without actually signing the transaction. */
returnTransactions?: boolean;
/** When signing with a wallet, a success URl can be included that the user will be redirected to once the transaction has been successfully signed. */
successUrl?: string;
}): Promise<CreateOrAddReturn> => {
const {
near,
viewCall,
gas,
attachedGas,
contractId,
receiverId,
getAccount,
execute,
fundingAccountDetails,
} = getEnv();
assert(
isValidAccountObj(account),
'Passed in account is not a valid account object.'
);
account = await getAccount({ account, wallet });
assert(
isSupportedKeypomContract(contractId!) === true,
'Only the latest Keypom contract can be used to call this methods. Please update the contract.'
);
assert(
publicKeys != undefined || numKeys != undefined,
'Must pass in either publicKeys or numKeys to create a drop.'
);
/// parse args
depositPerUseYocto = nearArgsToYocto(depositPerUseNEAR, depositPerUseYocto);
// Ensure that if the dropID is passed in, it's greater than 1 billion
assert(
parseInt(dropId || '1000000000') >= 1000000000,
'All custom drop IDs must be greater than 1_000_000_000'
);
if (!dropId) dropId = Date.now().toString();
await assertDropIdUnique(dropId);
const finalConfig: ProtocolReturnedDropConfig = {
uses_per_key: config?.usesPerKey || 1,
time: config?.time,
usage: {
auto_delete_drop: config?.usage?.autoDeleteDrop || false,
auto_withdraw: (config?.usage?.autoWithdraw === true) || false,
permissions: config?.usage?.permissions,
refund_deposit: config?.usage?.refundDeposit,
account_creation_fields: {
account_id_field:
config?.usage?.accountCreationFields?.accountIdField,
drop_id_field:
config?.usage?.accountCreationFields?.dropIdField,
key_id_field: config?.usage?.accountCreationFields?.keyIdField,
funder_id_field:
config?.usage?.accountCreationFields?.funderIdField,
},
},
sale: config?.sale
? {
max_num_keys: config?.sale?.maxNumKeys,
price_per_key:
config?.sale?.pricePerKeyYocto ||
config?.sale?.pricePerKeyNEAR
? parseNearAmount(
config?.sale?.pricePerKeyNEAR?.toString()
)!
: undefined,
allowlist: config?.sale?.allowlist,
blocklist: config?.sale?.blocklist,
auto_withdraw_funds: config?.sale?.autoWithdrawFunds,
start: config?.sale?.start,
end: config?.sale?.end,
}
: undefined,
root_account_id: config?.dropRoot,
};
assertValidDropConfig(finalConfig);
// If there are no publicKeys being passed in, we should generate our own based on the number of keys
let keys;
if (!publicKeys) {
// Default root entropy is what is passed in. If there wasn't any, we should check if the funding account contains some.
const rootEntropyUsed =
rootEntropy || fundingAccountDetails?.rootEntropy;
// If either root entropy was passed into the function or the funder has some set, we should use that.
if (rootEntropyUsed) {
// Create an array of size numKeys with increasing strings from 0 -> numKeys - 1. Each element should also contain the dropId infront of the string
const nonceDropIdMeta = Array.from(
{ length: numKeys },
(_, i) => `${dropId}_${i}`
);
keys = await generateKeys({
numKeys,
rootEntropy: rootEntropyUsed,
metaEntropy: nonceDropIdMeta,
});
} else {
// No entropy is provided so all keys should be fully random
keys = await generateKeys({
numKeys,
});
}
publicKeys = keys.publicKeys;
}
numKeys = publicKeys!.length;
assert(numKeys <= 100, 'Cannot add more than 100 keys at once');
let passwords;
if (basePassword) {
assert(numKeys <= 50, 'Cannot add more than 50 keys at once with passwords');
// Generate the passwords with the base password and public keys. By default, each key will have a unique password for all of its uses unless passwordProtectedUses is passed in
passwords = await generatePerUsePasswords({
publicKeys: publicKeys!,
basePassword,
uses:
passwordProtectedUses ||
Array.from(
{ length: config?.usesPerKey || 1 },
(_, i) => i + 1
),
});
}
let ftBalancePerUse;
if (ftData) {
ftBalancePerUse = ftData?.absoluteAmount || '0';
if (ftData.amount) {
const metadata = viewCall({
contractId: ftData.contractId,
methodName: 'ft_metadata',
});
ftBalancePerUse = parseFTAmount(
ftData.amount.toString(),
metadata.decimals
);
}
}
assertValidFCData(
fcData,
finalConfig.uses_per_key || 1
);
const createDropArgs: CreateDropProtocolArgs = {
drop_id: dropId,
public_keys: publicKeys || [],
deposit_per_use: depositPerUseYocto,
config: finalConfig,
metadata,
required_gas: requiredGas,
ft: ftData?.contractId
? {
contract_id: ftData.contractId,
sender_id: ftData.senderId,
balance_per_use: ftBalancePerUse!,
}
: undefined,
nft: nftData?.contractId
? {
contract_id: nftData.contractId,
sender_id: nftData.senderId,
}
: undefined,
fc: fcData?.methods
? {
methods: fcData.methods.map((useMethods) =>
useMethods
? useMethods.map((method) => {
const ret: ProtocolReturnedMethod = {
receiver_id: method.receiverId,
method_name: method.methodName,
args: method.args,
attached_deposit: method.attachedDeposit,
attached_gas: method.attachedGas,
account_id_field: method.accountIdField,
drop_id_field: method.dropIdField,
key_id_field: method.keyIdField,
funder_id_field: method.funderIdField,
receiver_to_claimer:
method.receiverToClaimer,
user_args_rule: method.userArgsRule,
};
return ret;
})
: undefined
),
}
: undefined,
simple: simpleData?.lazyRegister
? {
lazy_register: simpleData.lazyRegister,
}
: undefined,
passwords_per_use: passwords,
};
// If there is no ft data, nft data, or fc data, ensure the deposit per use is greater than 0
if (
createDropArgs.fc === undefined &&
createDropArgs.ft === undefined &&
createDropArgs.nft === undefined
) {
assert(
depositPerUseYocto != '0',
'Deposit per use must be greater than 0 for simple drops'
);
}
/// estimate required deposit
const storageCalculated = getStorageBase(createDropArgs);
const requiredDeposit = await estimateRequiredDeposit({
near: near!,
depositPerUse: depositPerUseYocto,
numKeys,
usesPerKey: finalConfig.uses_per_key || 1,
attachedGas: parseInt(requiredGas || attachedGas!),
storage: storageCalculated,
ftData,
fcData,
});
let hasBalance = false;
if (useBalance) {
const userBal = new BN(
await getUserBalance({ accountId: account!.accountId })
);
if (userBal.lt(new BN(requiredDeposit))) {
throw new Error(
'Insufficient balance on Keypom to create drop. Use attached deposit instead.'
);
}
hasBalance = true;
}
const deposit = !hasBalance ? requiredDeposit : '0';
let transactions: Transaction[] = [];
const pk = await account.connection.signer.getPublicKey(
account.accountId,
account.connection.networkId
);
assert(pk !== null, 'Could not get public key from signer. Ensure you have the key in the key store.')
const txnInfo: BasicTransaction = {
receiverId: receiverId!,
signerId: account!.accountId, // We know this is not undefined since getAccount throws
actions: [
{
enum: 'FunctionCall',
functionCall: {
methodName: 'create_drop',
args: stringifyJsonOrBytes(createDropArgs),
gas: gas!,
deposit,
}
},
],
};
transactions.push(await convertBasicTransaction({txnInfo, signerId: account!.accountId, signerPk: pk}));
if (ftData?.contractId && publicKeys?.length) {
transactions.push(
(await ftTransferCall({
account: account!,
contractId: ftData.contractId,
absoluteAmount: new BN(ftBalancePerUse!)
.mul(new BN(numKeys))
.mul(new BN(finalConfig.uses_per_key))
.toString(),
dropId,
returnTransaction: true,
})) as Transaction
);
}
const tokenIds = nftData?.tokenIds;
if (nftData && tokenIds && tokenIds?.length > 0) {
if (tokenIds.length > 2) {
throw new Error(
'You can only automatically register 2 NFTs with \'createDrop\'. If you need to register more NFTs you can use the method \'nftTransferCall\' after you create the drop.'
);
}
const nftTXs = (await nftTransferCall({
account: account!,
contractId: nftData.contractId as string,
tokenIds,
dropId: dropId.toString(),
returnTransactions: true,
})) as Transaction[];
transactions = transactions.concat(nftTXs);
}
if (returnTransactions) {
return { keys, dropId, transactions, requiredDeposit };
}
const responses = await execute({
transactions,
account,
wallet,
successUrl,
});
return { responses, keys, dropId, requiredDeposit };
};
/**
* Delete a set of drops and optionally withdraw any remaining balance you have on the Keypom contract.
*
* @example
* Create 5 drops and delete each of them:
* ```js
* // Initialize the SDK for the given network and NEAR connection
* await initKeypom({
* network: "testnet",
* funder: {
* accountId: "benji_demo.testnet",
* secretKey: "ed25519:5yARProkcALbxaSQ66aYZMSBPWL9uPBmkoQGjV3oi2ddQDMh1teMAbz7jqNV9oVyMy7kZNREjYvWPqjcA6LW9Jb1"
* }
* });
*
* // loop to create 5 simple drops each with 5 more keys than the next
* for(var i = 0; i < 5; i++) {
* // create 10 keys with no entropy (all random)
* const {publicKeys} = await generateKeys({
* numKeys: 5 * (i+1) // First drop will have 5, then 10, then 15 etc..
* });
*
* // Create the simple
* await createDrop({
* publicKeys,
* depositPerUseNEAR: 1,
* });
* }
*
* let drops = await getDrops({accountId: "benji_demo.testnet"});
* console.log('drops: ', drops)
*
* await deleteDrops({
* drops
* })
*
* // Get the number of drops the account has after deletion (should be zero)
* const numDrops = await getDropSupply({
* accountId: "benjiman.testnet"
* });
* console.log('numDrops: ', numDrops)
* ```
* @group Deleting State
*/
export const deleteDrops = async ({
account,
wallet,
drops,
dropIds,
withdrawBalance = true,
}: {
/** Account object that if passed in, will be used to sign the txn instead of the funder account. */
account?: Account;
/** If using a browser wallet through wallet selector and that wallet should sign the transaction, pass in the object. */
wallet?: AnyWallet;
/** If the set of drop information for the drops you want to delete (from `getDropInformation` or `getDrops`) is already known to the client, it can be passed in instead of the drop IDs to reduce computation. */
drops?: ProtocolReturnedDrop[];
/** Specify a set of drop IDs to delete. */
dropIds?: string[];
/** Whether or not to withdraw any remaining balance on the Keypom contract. */
withdrawBalance?: boolean;
}) => {
const { gas300, receiverId, execute, getAccount, contractId } =
getEnv();
assert(
isSupportedKeypomContract(contractId!) === true,
'Only the latest Keypom contract can be used to call this methods. Please update the contract.'
);
assert(
isValidAccountObj(account),
'Passed in account is not a valid account object.'
);
account = await getAccount({ account, wallet });
const pubKey = await account.connection.signer.getPublicKey(account.accountId, account.connection.networkId);
// If the drop information isn't passed in, we should get it from the drop IDs
if (!drops) {
if (!dropIds) {
throw new Error('Must pass in either drops or dropIds');
}
// For each drop ID in drop IDs, get the drop information
drops = [];
await Promise.all(
await dropIds.map(async (dropId) => {
drops?.push(await getDropInformation({ dropId }));
})
);
}
const responses = await Promise.all(
drops!.map(async ({ owner_id, drop_id, registered_uses, ft, nft }) => {
assert(
owner_id == account!.accountId,
'Only the owner of the drop can delete drops.'
);
let keySupply;
let keys;
const updateKeys = async () => {
const keyPromises = [
(async () => {
keySupply = await keypomView({
methodName: 'get_key_supply_for_drop',
args: {
drop_id: drop_id.toString(),
},
});
})(),
];
keyPromises.push(
(async () => {
keys = await keypomView({
methodName: 'get_keys_for_drop',
args: {
drop_id: drop_id.toString(),
from_index: '0',
limit: KEY_LIMIT,
},
});
})()
);
await Promise.all(keyPromises);
};
await updateKeys();
const responses: Array<void | FinalExecutionOutcome[]> = [];
if (
registered_uses !== 0 &&
(ft !== undefined || nft !== undefined)
) {
const txn = await convertBasicTransaction({
txnInfo: {
receiverId,
signerId: account!.accountId,
actions: [
{
enum: 'FunctionCall',
functionCall: {
methodName: 'refund_assets',
args: stringifyJsonOrBytes({
drop_id,
}),
gas: gas300,
deposit: '0'
},
},
],
},
signerId: account!.accountId,
signerPk: pubKey
});
responses.push(
...(await execute({
account,
wallet,
transactions: [txn],
}))
);
}
const deleteKeys = async () => {
const txn = await convertBasicTransaction({
txnInfo: {
receiverId,
signerId: account!.accountId,
actions: [
{
enum: 'FunctionCall',
functionCall: {
methodName: 'delete_keys',
args: stringifyJsonOrBytes({
drop_id,
public_keys: keys!.map(key2str),
}),
gas: gas300,
deposit: '0'
},
},
],
},
signerId: account!.accountId,
signerPk: pubKey
});
responses.push(
...(await execute({
account,
wallet,
transactions: [txn],
}))
);
if (keySupply > (keys?.length || 0)) {
await updateKeys();
await deleteKeys();
}
};
await deleteKeys();
if (withdrawBalance) {
const txn = await convertBasicTransaction({
txnInfo: {
receiverId,
signerId: account!.accountId,
actions: [
{
enum: 'FunctionCall',
functionCall: {
methodName: 'withdraw_from_balance',
args: stringifyJsonOrBytes({}),
gas: '50000000000000',
deposit: '0'
},
},
],
},
signerId: account!.accountId,
signerPk: pubKey
});
responses.push(
...(await execute({
account,
wallet,
transactions: [txn],
}))
);
}
return responses;
})
);
return responses;
};
// This should be done later. Very small number of drops will have lazy registrations enabled.
// /**
// * Allows a user to register uses for a simple drop that has lazy registrations enabled. This drop can be over-registered.
// *
// * @param {Account=} account (OPTIONAL) If specified, the passed in account will be used to sign the txn instead of the funder account.
// * @param {BrowserWalletBehaviour=} wallet (OPTIONAL) If using a browser wallet through wallet selector and that wallet should sign the transaction, pass it in.
// * @param {string[]=} dropId Specify the drop ID of the drop you want to register uses on
// *
// * @example <caption>Create 5 drops and delete each of them</caption>
// * ```js
// * ```
// */
// export const registerUses = async ({
// account,
// wallet,
// dropId,
// numUses,
// useBalance = false,
// }: RegisterUsesParams) => {
// const {
// gas300, receiverId, execute, getAccount
// } = getEnv()
// account = await getAccount({ account, wallet });
// }