@silvana-one/token 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.
Files changed (68) hide show
  1. package/README.md +1 -0
  2. package/dist/node/BondingCurveAdmin.d.ts +690 -0
  3. package/dist/node/BondingCurveAdmin.js +504 -0
  4. package/dist/node/BondingCurveAdmin.js.map +1 -0
  5. package/dist/node/FungibleToken.d.ts +414 -0
  6. package/dist/node/FungibleToken.js +7 -0
  7. package/dist/node/FungibleToken.js.map +1 -0
  8. package/dist/node/FungibleTokenAdvancedAdmin.d.ts +124 -0
  9. package/dist/node/FungibleTokenAdvancedAdmin.js +226 -0
  10. package/dist/node/FungibleTokenAdvancedAdmin.js.map +1 -0
  11. package/dist/node/FungibleTokenContract.d.ts +526 -0
  12. package/dist/node/FungibleTokenContract.js +295 -0
  13. package/dist/node/FungibleTokenContract.js.map +1 -0
  14. package/dist/node/FungibleTokenStandardAdmin.d.ts +27 -0
  15. package/dist/node/FungibleTokenStandardAdmin.js +101 -0
  16. package/dist/node/FungibleTokenStandardAdmin.js.map +1 -0
  17. package/dist/node/bid.d.ts +86 -0
  18. package/dist/node/bid.js +168 -0
  19. package/dist/node/bid.js.map +1 -0
  20. package/dist/node/claim.d.ts +89 -0
  21. package/dist/node/claim.js +156 -0
  22. package/dist/node/claim.js.map +1 -0
  23. package/dist/node/index.cjs +1576 -0
  24. package/dist/node/index.d.ts +8 -0
  25. package/dist/node/index.js +9 -0
  26. package/dist/node/index.js.map +1 -0
  27. package/dist/node/offer.d.ts +87 -0
  28. package/dist/node/offer.js +169 -0
  29. package/dist/node/offer.js.map +1 -0
  30. package/dist/tsconfig.tsbuildinfo +1 -0
  31. package/dist/tsconfig.web.tsbuildinfo +1 -0
  32. package/dist/web/BondingCurveAdmin.d.ts +690 -0
  33. package/dist/web/BondingCurveAdmin.js +504 -0
  34. package/dist/web/BondingCurveAdmin.js.map +1 -0
  35. package/dist/web/FungibleToken.d.ts +414 -0
  36. package/dist/web/FungibleToken.js +7 -0
  37. package/dist/web/FungibleToken.js.map +1 -0
  38. package/dist/web/FungibleTokenAdvancedAdmin.d.ts +124 -0
  39. package/dist/web/FungibleTokenAdvancedAdmin.js +226 -0
  40. package/dist/web/FungibleTokenAdvancedAdmin.js.map +1 -0
  41. package/dist/web/FungibleTokenContract.d.ts +526 -0
  42. package/dist/web/FungibleTokenContract.js +295 -0
  43. package/dist/web/FungibleTokenContract.js.map +1 -0
  44. package/dist/web/FungibleTokenStandardAdmin.d.ts +27 -0
  45. package/dist/web/FungibleTokenStandardAdmin.js +101 -0
  46. package/dist/web/FungibleTokenStandardAdmin.js.map +1 -0
  47. package/dist/web/bid.d.ts +86 -0
  48. package/dist/web/bid.js +168 -0
  49. package/dist/web/bid.js.map +1 -0
  50. package/dist/web/claim.d.ts +89 -0
  51. package/dist/web/claim.js +156 -0
  52. package/dist/web/claim.js.map +1 -0
  53. package/dist/web/index.d.ts +8 -0
  54. package/dist/web/index.js +9 -0
  55. package/dist/web/index.js.map +1 -0
  56. package/dist/web/offer.d.ts +87 -0
  57. package/dist/web/offer.js +169 -0
  58. package/dist/web/offer.js.map +1 -0
  59. package/package.json +64 -0
  60. package/src/BondingCurveAdmin.ts +590 -0
  61. package/src/FungibleToken.ts +11 -0
  62. package/src/FungibleTokenAdvancedAdmin.ts +260 -0
  63. package/src/FungibleTokenContract.ts +337 -0
  64. package/src/FungibleTokenStandardAdmin.ts +95 -0
  65. package/src/bid.ts +170 -0
  66. package/src/claim.ts +151 -0
  67. package/src/index.ts +8 -0
  68. package/src/offer.ts +164 -0
@@ -0,0 +1,590 @@
1
+ import {
2
+ AccountUpdate,
3
+ Bool,
4
+ DeployArgs,
5
+ method,
6
+ Permissions,
7
+ Provable,
8
+ PublicKey,
9
+ TokenContract,
10
+ State,
11
+ state,
12
+ UInt64,
13
+ VerificationKey,
14
+ Field,
15
+ Struct,
16
+ AccountUpdateForest,
17
+ Int64,
18
+ UInt32,
19
+ fetchAccount,
20
+ Mina,
21
+ } from "o1js";
22
+ import {
23
+ FungibleTokenAdminBase,
24
+ FungibleTokenContract,
25
+ } from "@silvana-one/token";
26
+
27
+ /**
28
+ * Default bonding curve:
29
+ * Initial price: 1 MINA per 100,000 tokens (0.00001 MINA per token), or 10_000 / 10 ** 9
30
+ * Curve K: 1 MINA per 100,000 tokens (0.00001 MINA per token), or 10_000 / 10 ** 9 MINA
31
+ * Owner fee: 10% in 0.001 % units(10 * 1000 = 10_000)
32
+ * Price formula: price = startPrice + curveK * totalSupply
33
+ * Example:
34
+ * If supply is 200,000 tokens, price = 1 MINA + 1 MINA * 200_000 / 100_000 = 3 MINA per 100,000 tokens
35
+ * or per token in MINA/1e9
36
+ * 10000 + 10000 * 200_000 / 100_000 = 30000 per token,
37
+ * or 30_000 * 100_000 = 3_000_000_000, or 3 MINA per 100,000 tokens
38
+ *
39
+ * To calculate the max supply for the given price for 100,000 tokens:
40
+ * price = startPrice + curveK * totalSupply
41
+ * price - startPrice = curveK * totalSupply
42
+ * (price - startPrice) / curveK = totalSupply
43
+ * (3 MINA - 1 MINA) / 1 MINA = 2 * 100_000 = 200_000 tokens
44
+ * (30_000 - 10_000) / 10_000 = 2 * 100_000 = 200_000 tokens
45
+ * or, in 1e9 units:
46
+ * (30_000 - 10_000) * 1e9 * 100_000 / 10_000 = 200_000_000_000_000, or to avoid overflow,
47
+ * (30_000 - 10_000) * 1e9 / 10_000 * 100_000 = 200_000_000_000_000
48
+ */
49
+
50
+ export class BondingCurveParams extends Struct({
51
+ startPrice: UInt64,
52
+ curveK: UInt64,
53
+ fee: UInt32, // 1000 = 1%
54
+ mintingIsAllowed: Bool,
55
+ }) {
56
+ pack() {
57
+ return Field.fromBits([
58
+ ...this.startPrice.value.toBits(64),
59
+ ...this.curveK.value.toBits(64),
60
+ ...this.fee.value.toBits(32),
61
+ this.mintingIsAllowed,
62
+ ]);
63
+ }
64
+ static unpack(field: Field): BondingCurveParams {
65
+ const bits = field.toBits(64 + 64 + 32 + 1);
66
+ const startPrice = UInt64.Unsafe.fromField(
67
+ Field.fromBits(bits.slice(0, 64))
68
+ );
69
+ const curveK = UInt64.Unsafe.fromField(
70
+ Field.fromBits(bits.slice(64, 64 + 64))
71
+ );
72
+ const fee = UInt32.Unsafe.fromField(
73
+ Field.fromBits(bits.slice(64 + 64, 64 + 64 + 32))
74
+ );
75
+ const mintingIsAllowed = bits[64 + 64 + 32];
76
+ return new BondingCurveParams({
77
+ startPrice,
78
+ curveK,
79
+ fee,
80
+ mintingIsAllowed,
81
+ });
82
+ }
83
+ }
84
+
85
+ export class BondingMintEvent extends Struct({
86
+ to: PublicKey,
87
+ amount: UInt64,
88
+ price: UInt64,
89
+ payment: UInt64,
90
+ fee: UInt64,
91
+ }) {}
92
+
93
+ export class BondingRedeemEvent extends Struct({
94
+ seller: PublicKey,
95
+ amount: UInt64,
96
+ payment: UInt64,
97
+ minBalance: UInt64,
98
+ maxSupply: UInt64,
99
+ fee: UInt64,
100
+ }) {}
101
+
102
+ export class BondingCurveAdminInitializeProps extends Struct({
103
+ tokenAddress: PublicKey,
104
+ startPrice: UInt64,
105
+ curveK: UInt64,
106
+ feeMaster: PublicKey,
107
+ fee: UInt32, // 1000 = 1%
108
+ launchFee: UInt64,
109
+ numberOfNewAccounts: UInt64,
110
+ }) {}
111
+
112
+ export class FungibleTokenBondingCurveAdmin
113
+ extends TokenContract
114
+ implements FungibleTokenAdminBase
115
+ {
116
+ @state(PublicKey) owner = State<PublicKey>(PublicKey.empty());
117
+ @state(PublicKey) token = State<PublicKey>(PublicKey.empty());
118
+ @state(PublicKey) feeMaster = State<PublicKey>(PublicKey.empty());
119
+ @state(Field) curve = State<Field>();
120
+ @state(Bool) insideMint = State<Bool>(Bool(false));
121
+
122
+ events = {
123
+ mint: BondingMintEvent,
124
+ redeem: BondingRedeemEvent,
125
+ };
126
+
127
+ async deploy(props: DeployArgs) {
128
+ await super.deploy(props);
129
+ this.curve.set(
130
+ new BondingCurveParams({
131
+ startPrice: UInt64.from(10_000),
132
+ curveK: UInt64.from(10_000),
133
+ fee: UInt32.from(1000),
134
+ mintingIsAllowed: Bool(false),
135
+ }).pack()
136
+ );
137
+ this.account.permissions.set({
138
+ ...Permissions.default(),
139
+ setVerificationKey:
140
+ Permissions.VerificationKey.impossibleDuringCurrentVersion(),
141
+ setPermissions: Permissions.impossible(),
142
+ send: Permissions.proof(),
143
+ });
144
+ }
145
+
146
+ async approveBase(forest: AccountUpdateForest) {
147
+ throw Error("Transfer not allowed");
148
+ }
149
+
150
+ @method
151
+ async initialize(props: BondingCurveAdminInitializeProps) {
152
+ const {
153
+ tokenAddress,
154
+ feeMaster,
155
+ startPrice,
156
+ curveK,
157
+ fee,
158
+ launchFee,
159
+ numberOfNewAccounts,
160
+ } = props;
161
+ this.account.provedState.requireEquals(Bool(false));
162
+ this.token.set(tokenAddress);
163
+ this.feeMaster.set(feeMaster);
164
+ this.curve.set(
165
+ new BondingCurveParams({
166
+ startPrice,
167
+ curveK,
168
+ fee,
169
+ mintingIsAllowed: Bool(false),
170
+ }).pack()
171
+ );
172
+
173
+ /*
174
+ We cannot constrain the circulating supply of the token,
175
+ as the error Can't transfer to/from the circulation account
176
+ is thrown for ANY AccountUpdate for the circulation tracking account
177
+ so we keep the copy if the circulation amount here and sync sometimes
178
+ in the case the user burns tokens without calling the redeem method
179
+ */
180
+ const supplyUpdate = AccountUpdate.createSigned(
181
+ this.address,
182
+ this.deriveTokenId()
183
+ );
184
+ let permissions = Permissions.default();
185
+ permissions.send = Permissions.none();
186
+ permissions.setPermissions = Permissions.impossible();
187
+ supplyUpdate.account.permissions.set(permissions);
188
+
189
+ const payment = launchFee.add(
190
+ numberOfNewAccounts.mul(UInt64.from(1_000_000_000))
191
+ );
192
+ const owner = this.sender.getUnconstrained();
193
+ const ownerUpdate = AccountUpdate.create(owner);
194
+ ownerUpdate.requireSignature();
195
+ this.owner.set(owner);
196
+ ownerUpdate.body.useFullCommitment = Bool(true);
197
+ ownerUpdate.account.balance.requireBetween(payment, UInt64.MAXINT());
198
+ ownerUpdate.balance.subInPlace(payment);
199
+ const feeUpdate = AccountUpdate.create(feeMaster);
200
+ feeUpdate.balance.addInPlace(launchFee);
201
+ }
202
+
203
+ @method async mint(to: PublicKey, amount: UInt64, price: UInt64) {
204
+ this.insideMint.getAndRequireEquals().assertEquals(Bool(false));
205
+ this.insideMint.set(Bool(true));
206
+ const tokenAddress = this.token.getAndRequireEquals();
207
+ const token = new BondingCurveFungibleToken(tokenAddress);
208
+ const { startPrice, curveK, fee, mintingIsAllowed } =
209
+ BondingCurveParams.unpack(this.curve.getAndRequireEquals());
210
+ const buyer = this.sender.getUnconstrained();
211
+ const buyerUpdate = AccountUpdate.create(buyer);
212
+ buyerUpdate.requireSignature();
213
+ buyerUpdate.body.useFullCommitment = Bool(true);
214
+ const owner = this.owner.getAndRequireEquals();
215
+ const isOwner = owner.equals(buyer);
216
+ const canMint = isOwner.or(mintingIsAllowed);
217
+ canMint.assertTrue("Minting is disabled for this token");
218
+ this.curve.set(
219
+ new BondingCurveParams({
220
+ startPrice,
221
+ curveK,
222
+ fee,
223
+ mintingIsAllowed: Bool(true),
224
+ }).pack()
225
+ );
226
+
227
+ /*
228
+ * (price - startPrice) / curveK = totalSupply
229
+ * (3 MINA - 1 MINA) / 1 MINA = 2 * 100_000 = 200_000 tokens
230
+ * (30_000 - 10_000) / 10_000 = 2 * 100_000 = 200_000 tokens
231
+ * or, in 1e9 units:
232
+ * (30_000 - 10_000) * 1e9 * 100_000 / 10_000 = 200_000_000_000_000, or to avoid overflow,
233
+ * (30_000 - 10_000) * 1e9 / 10_000 * 100_000 = 200_000_000_000_000
234
+ * (price - startPrice) * 1e9 / curveK * 100_000 = maximumSupply
235
+ * maximumSupply * curveK = (price - startPrice) * 1e14
236
+ * maximumSupply * curveK + startPrice * 1e14 = price * 1e14
237
+ *
238
+ */
239
+
240
+ const maximumSupplyField = Provable.witness(Field, () => {
241
+ if (price.toBigInt() < startPrice.toBigInt()) {
242
+ throw Error("Price must be greater than or equal to the start price");
243
+ }
244
+ return Field.from(
245
+ ((price.toBigInt() - startPrice.toBigInt()) * 10n ** 14n) /
246
+ curveK.toBigInt()
247
+ );
248
+ });
249
+
250
+ maximumSupplyField
251
+ .mul(curveK.value)
252
+ .add(startPrice.value.mul(Field.from(10 ** 14)))
253
+ .assertLessThanOrEqual(price.value.mul(Field.from(10 ** 14)));
254
+
255
+ maximumSupplyField.assertLessThan(UInt64.MAXINT().value);
256
+ const maximumSupply = UInt64.Unsafe.fromField(maximumSupplyField);
257
+ const supplyUpdate = AccountUpdate.create(
258
+ this.address,
259
+ this.deriveTokenId()
260
+ );
261
+ supplyUpdate.account.balance.requireBetween(UInt64.zero, maximumSupply);
262
+ amount.assertLessThanOrEqual(UInt64.from(2n ** 62n));
263
+ supplyUpdate.balanceChange = Int64.fromUnsigned(amount);
264
+ const paymentField = Provable.witness(Field, () => {
265
+ let payment = (price.toBigInt() * amount.toBigInt()) / 10n ** 9n;
266
+ if (payment * 10n ** 9n !== price.toBigInt() * amount.toBigInt()) {
267
+ payment++;
268
+ }
269
+ if (payment * 10n ** 9n < price.toBigInt() * amount.toBigInt()) {
270
+ throw Error("Payment calculation failed");
271
+ }
272
+ return Field.from(payment);
273
+ });
274
+ paymentField
275
+ .mul(Field.from(10 ** 9))
276
+ .assertGreaterThanOrEqual(price.value.mul(amount.value));
277
+ paymentField.assertLessThan(UInt64.MAXINT().value);
278
+ const payment = UInt64.Unsafe.fromField(paymentField);
279
+
280
+ const feePaymentField = Provable.witness(Field, () => {
281
+ let feePayment = (payment.toBigInt() * fee.toBigint()) / 100_000n;
282
+ if (feePayment * 100_000n !== payment.toBigInt() * fee.toBigint()) {
283
+ feePayment++;
284
+ }
285
+ if (feePayment * 100_000n < payment.toBigInt() * fee.toBigint()) {
286
+ throw Error("Fee calculation failed");
287
+ }
288
+ return Field.from(feePayment);
289
+ });
290
+ feePaymentField
291
+ .mul(Field.from(100_000))
292
+ .assertGreaterThanOrEqual(payment.value.mul(fee.value));
293
+ feePaymentField.assertLessThan(UInt64.MAXINT().value);
294
+ let feePayment = UInt64.Unsafe.fromField(feePaymentField);
295
+ feePayment = Provable.if(
296
+ feePayment.lessThan(UInt64.from(100_000_000)),
297
+ UInt64.from(100_000_000),
298
+ feePayment
299
+ );
300
+
301
+ const tokenUpdate = await token.mint(to, amount);
302
+ const isNew = tokenUpdate.account.isNew.get();
303
+ const totalPayment = payment
304
+ .add(feePayment)
305
+ .add(Provable.if(isNew, UInt64.from(1_000_000_000), UInt64.zero));
306
+ buyerUpdate.account.balance.requireBetween(totalPayment, UInt64.MAXINT());
307
+ buyerUpdate.balance.subInPlace(totalPayment);
308
+
309
+ this.balance.addInPlace(payment);
310
+ const feeUpdate = AccountUpdate.create(
311
+ this.feeMaster.getAndRequireEquals()
312
+ );
313
+ feeUpdate.body.useFullCommitment = Bool(true);
314
+ feeUpdate.balance.addInPlace(feePayment);
315
+ this.emitEvent(
316
+ "mint",
317
+ new BondingMintEvent({
318
+ to,
319
+ amount,
320
+ price,
321
+ payment,
322
+ fee: feePayment,
323
+ })
324
+ );
325
+ }
326
+
327
+ /*
328
+ In case of other txs being included in the same block, the balance and supply may change.
329
+ We need to ensure that the balance is at least minBalance and the supply is at most maxSupply.
330
+ It is recommended to put 5% buffer for minBalance and 5% buffer for maxSupply for tx to succeed.
331
+ */
332
+ @method async redeem(amount: UInt64, minPrice: UInt64, slippage: UInt32) {
333
+ const tokenAddress = this.token.getAndRequireEquals();
334
+ const token = new BondingCurveFungibleToken(tokenAddress);
335
+
336
+ const balance = await Provable.witnessAsync(UInt64, async () => {
337
+ await fetchAccount({
338
+ publicKey: this.address,
339
+ tokenId: this.tokenId,
340
+ });
341
+ const balance = Mina.getAccount(this.address, this.tokenId).balance;
342
+ console.log("redeem: balance", balance.toBigInt());
343
+ return balance;
344
+ });
345
+ slippage.assertLessThan(UInt32.from(1000));
346
+ const minBalanceField = Provable.witness(Field, () => {
347
+ let minBalance =
348
+ (balance.toBigInt() * (1000n - slippage.toBigint())) / 1000n;
349
+ if (
350
+ minBalance * 1000n !==
351
+ balance.toBigInt() * (1000n - slippage.toBigint())
352
+ ) {
353
+ minBalance++;
354
+ }
355
+ if (
356
+ minBalance * 1000n <
357
+ balance.toBigInt() * (1000n - slippage.toBigint())
358
+ ) {
359
+ throw Error("Min balance calculation failed");
360
+ }
361
+ return Field.from(minBalance);
362
+ });
363
+ minBalanceField
364
+ .mul(Field.from(1000))
365
+ .add(balance.value.mul(slippage.value))
366
+ .assertGreaterThanOrEqual(balance.value.mul(Field.from(1000)));
367
+
368
+ minBalanceField.assertLessThan(UInt64.MAXINT().value);
369
+ const minBalance = UInt64.Unsafe.fromField(minBalanceField);
370
+ this.account.balance.requireBetween(minBalance, UInt64.MAXINT());
371
+
372
+ const supply = await Provable.witnessAsync(UInt64, async () => {
373
+ await fetchAccount({
374
+ publicKey: this.address,
375
+ tokenId: this.deriveTokenId(),
376
+ });
377
+ const balance = Mina.getAccount(
378
+ this.address,
379
+ this.deriveTokenId()
380
+ ).balance;
381
+ console.log("redeem: supply", balance.toBigInt());
382
+ return balance;
383
+ });
384
+ supply.assertGreaterThanOrEqual(amount);
385
+ supply.assertGreaterThan(UInt64.zero);
386
+ const maxSupplyField = Provable.witness(Field, () =>
387
+ Field.from((supply.toBigInt() * (1000n + slippage.toBigint())) / 1000n)
388
+ );
389
+ maxSupplyField
390
+ .mul(Field.from(1000))
391
+ .assertLessThanOrEqual(
392
+ supply.value.mul(Field.from(1000).add(slippage.value))
393
+ );
394
+ maxSupplyField.assertLessThan(UInt64.MAXINT().value);
395
+ const maxSupply = UInt64.Unsafe.fromField(maxSupplyField);
396
+ const supplyUpdate = AccountUpdate.create(
397
+ this.address,
398
+ this.deriveTokenId()
399
+ );
400
+ supplyUpdate.account.balance.requireBetween(UInt64.zero, maxSupply);
401
+ supplyUpdate.balanceChange = Int64.fromUnsigned(amount).neg();
402
+
403
+ // We avoid modular division that is not working in case there is remainder
404
+ const paymentField = Provable.witness(Field, () =>
405
+ Field.from(
406
+ (minBalance.toBigInt() * amount.toBigInt()) / maxSupply.toBigInt()
407
+ )
408
+ );
409
+ // We use assertLessThanOrEqual to handle the case when there is remainder
410
+ paymentField
411
+ .mul(maxSupply.value)
412
+ .assertLessThanOrEqual(minBalance.value.mul(amount.value));
413
+ paymentField.assertLessThan(Field.from(2n ** 62n));
414
+ amount.value
415
+ .mul(minPrice.value)
416
+ .assertLessThanOrEqual(paymentField.mul(Field.from(10 ** 9)));
417
+ const payment = UInt64.Unsafe.fromField(paymentField);
418
+ const { fee, mintingIsAllowed } = BondingCurveParams.unpack(
419
+ this.curve.getAndRequireEquals()
420
+ );
421
+ mintingIsAllowed.assertTrue(
422
+ "Minting is disabled for this token, nothing to redeem"
423
+ );
424
+ let feePayment = Provable.witness(UInt64, () => {
425
+ let feePayment = (payment.toBigInt() * fee.toBigint()) / 100_000n;
426
+ if (feePayment * 100_000n !== payment.toBigInt() * fee.toBigint()) {
427
+ feePayment++;
428
+ }
429
+ if (feePayment * 100_000n < payment.toBigInt() * fee.toBigint()) {
430
+ throw Error("Fee calculation failed");
431
+ }
432
+ return UInt64.from(feePayment);
433
+ });
434
+ feePayment = Provable.if(
435
+ feePayment.lessThan(UInt64.from(100_000_000)),
436
+ UInt64.from(100_000_000),
437
+ feePayment
438
+ );
439
+ const seller = this.sender.getUnconstrained();
440
+ const sellerUpdate = AccountUpdate.create(seller);
441
+ const isNew = await Provable.witnessAsync(Bool, async () => {
442
+ const sellerAccount = await fetchAccount({
443
+ publicKey: seller,
444
+ });
445
+ return Bool(sellerAccount.account === undefined);
446
+ });
447
+
448
+ sellerUpdate.account.isNew.requireEquals(isNew);
449
+ const accountCreationFee = Provable.if(
450
+ isNew,
451
+ UInt64.from(1_000_000_000),
452
+ UInt64.zero
453
+ );
454
+ payment.assertGreaterThan(feePayment.add(accountCreationFee));
455
+ sellerUpdate.requireSignature();
456
+ sellerUpdate.body.useFullCommitment = Bool(true);
457
+ const totalPayment = Provable.witness(UInt64, () => {
458
+ if (
459
+ payment.toBigInt() <
460
+ feePayment.toBigInt() + accountCreationFee.toBigInt()
461
+ ) {
462
+ console.error(
463
+ `The redeem amount ${
464
+ Number(payment.toBigInt() / 10n ** 6n) / 1000
465
+ } MINA is too low to cover the fee ${
466
+ Number(feePayment.toBigInt() / 10n ** 6n) / 1000
467
+ } MINA and account creation fee ${
468
+ Number(accountCreationFee.toBigInt() / 10n ** 6n) / 1000
469
+ } MINA for the account ${seller.toBase58()}`,
470
+ {
471
+ payment: payment.toBigInt(),
472
+ feePayment: feePayment.toBigInt(),
473
+ accountCreationFee: accountCreationFee.toBigInt(),
474
+ minBalance: minBalance.toBigInt(),
475
+ maxSupply: maxSupply.toBigInt(),
476
+ supply: supply.toBigInt(),
477
+ amount: amount.toBigInt(),
478
+ balance: balance.toBigInt(),
479
+ tokenAddress: tokenAddress.toBase58(),
480
+ seller: seller.toBase58(),
481
+ isNew: isNew.toBoolean(),
482
+ }
483
+ );
484
+ throw Error(
485
+ `The redeem amount ${
486
+ Number(payment.toBigInt() / 10n ** 6n) / 1000
487
+ } MINA is too low to cover the fee ${
488
+ Number(feePayment.toBigInt() / 10n ** 6n) / 1000
489
+ } MINA and account creation fee ${
490
+ Number(accountCreationFee.toBigInt() / 10n ** 6n) / 1000
491
+ } MINA for the account ${seller.toBase58()}`
492
+ );
493
+ }
494
+ return UInt64.from(
495
+ payment.toBigInt() -
496
+ feePayment.toBigInt() -
497
+ accountCreationFee.toBigInt()
498
+ );
499
+ });
500
+ totalPayment.add(feePayment).add(accountCreationFee).assertEquals(payment);
501
+ sellerUpdate.balance.addInPlace(totalPayment);
502
+ const feeUpdate = AccountUpdate.create(
503
+ this.feeMaster.getAndRequireEquals()
504
+ );
505
+ feeUpdate.body.useFullCommitment = Bool(true);
506
+ feeUpdate.balance.addInPlace(feePayment);
507
+ this.balance.subInPlace(payment);
508
+ await token.burn(seller, amount);
509
+ this.emitEvent(
510
+ "redeem",
511
+ new BondingRedeemEvent({
512
+ seller,
513
+ amount,
514
+ payment,
515
+ minBalance,
516
+ maxSupply,
517
+ fee: feePayment,
518
+ })
519
+ );
520
+ }
521
+
522
+ /**
523
+ * In case the user burned tokens without calling the redeem method,
524
+ * we need to sync the supply to the actual circulated supply
525
+ */
526
+
527
+ @method async sync() {
528
+ const tokenAddress = this.token.getAndRequireEquals();
529
+ const token = new BondingCurveFungibleToken(tokenAddress);
530
+ const supplyUpdate = AccountUpdate.create(
531
+ this.address,
532
+ this.deriveTokenId()
533
+ );
534
+ const circulatingSupply = await token.getBalanceOf(tokenAddress);
535
+ const totalSupply = supplyUpdate.account.balance.getAndRequireEquals();
536
+ supplyUpdate.balanceChange = Int64.fromUnsigned(
537
+ totalSupply.sub(circulatingSupply)
538
+ ).neg();
539
+ }
540
+
541
+ /** Update the verification key.
542
+ * Note that because we have set the permissions for setting the verification key to `impossibleDuringCurrentVersion()`, this will only be possible in case of a protocol update that requires an update.
543
+ */
544
+ @method
545
+ async updateVerificationKey(vk: VerificationKey) {
546
+ this.account.verificationKey.set(vk);
547
+ }
548
+
549
+ ensureOwnerSignature() {
550
+ const owner = this.owner.getAndRequireEquals();
551
+ const update = AccountUpdate.createSigned(owner);
552
+ update.body.useFullCommitment = Bool(true);
553
+ return update;
554
+ }
555
+
556
+ @method.returns(Bool)
557
+ public async canMint(_accountUpdate: AccountUpdate) {
558
+ this.insideMint.requireEquals(Bool(true));
559
+ this.insideMint.set(Bool(false));
560
+ return Bool(true);
561
+ }
562
+
563
+ @method.returns(Bool)
564
+ public async canChangeAdmin(_admin: PublicKey) {
565
+ this.ensureOwnerSignature();
566
+ return Bool(true);
567
+ }
568
+
569
+ @method.returns(Bool)
570
+ public async canPause(): Promise<Bool> {
571
+ this.ensureOwnerSignature();
572
+ return Bool(true);
573
+ }
574
+
575
+ @method.returns(Bool)
576
+ public async canResume(): Promise<Bool> {
577
+ this.ensureOwnerSignature();
578
+ return Bool(true);
579
+ }
580
+
581
+ @method.returns(Bool)
582
+ public async canChangeVerificationKey(_vk: VerificationKey): Promise<Bool> {
583
+ this.ensureOwnerSignature();
584
+ return Bool(true);
585
+ }
586
+ }
587
+
588
+ export const BondingCurveFungibleToken = FungibleTokenContract(
589
+ FungibleTokenBondingCurveAdmin
590
+ );
@@ -0,0 +1,11 @@
1
+ import {
2
+ FungibleTokenContract,
3
+ FungibleTokenAdminBase,
4
+ } from "./FungibleTokenContract.js";
5
+ import { FungibleTokenAdmin } from "./FungibleTokenStandardAdmin.js";
6
+ import { FungibleTokenAdvancedAdmin } from "./FungibleTokenAdvancedAdmin.js";
7
+
8
+ export { FungibleToken, AdvancedFungibleToken };
9
+
10
+ const FungibleToken = FungibleTokenContract(FungibleTokenAdmin);
11
+ const AdvancedFungibleToken = FungibleTokenContract(FungibleTokenAdvancedAdmin);