@sign-global/tokentable-core 1.1.0 → 1.2.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 CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  } from "viem/chains";
16
16
 
17
17
  // src/constants/network.ts
18
+ import { getFullnodeUrl } from "@mysten/sui/client";
18
19
  import { CHAIN } from "@tonconnect/sdk";
19
20
  import { defineChain } from "viem";
20
21
  var mevmDevNet = defineChain({
@@ -161,6 +162,56 @@ var signBrBTestnet = /* @__PURE__ */ defineChain({
161
162
  },
162
163
  testnet: true
163
164
  });
165
+ var suiTestNet = {
166
+ id: "testnet",
167
+ name: "Sui Testnet",
168
+ network: "Sui Testnet",
169
+ nativeCurrency: {
170
+ decimals: 9,
171
+ name: "Sui",
172
+ symbol: "SUI"
173
+ },
174
+ rpcUrls: {
175
+ public: {
176
+ http: [getFullnodeUrl("testnet")]
177
+ },
178
+ default: {
179
+ http: [getFullnodeUrl("testnet")]
180
+ }
181
+ },
182
+ blockExplorers: {
183
+ default: {
184
+ name: "sui scan",
185
+ url: "https://testnet.suivision.xyz/"
186
+ }
187
+ },
188
+ testnet: true
189
+ };
190
+ var suiMainNet = {
191
+ id: "mainnet",
192
+ name: "Sui Mainnet",
193
+ network: "Sui Mainnet",
194
+ nativeCurrency: {
195
+ decimals: 9,
196
+ name: "Sui",
197
+ symbol: "SUI"
198
+ },
199
+ rpcUrls: {
200
+ public: {
201
+ http: [getFullnodeUrl("mainnet")]
202
+ },
203
+ default: {
204
+ http: [getFullnodeUrl("mainnet")]
205
+ }
206
+ },
207
+ blockExplorers: {
208
+ default: {
209
+ name: "sui scan",
210
+ url: "https://suivision.xyz/"
211
+ }
212
+ },
213
+ testnet: false
214
+ };
164
215
 
165
216
  // src/constants/chain-config.ts
166
217
  import { CHAIN as CHAIN2 } from "@tonconnect/sdk";
@@ -247,6 +298,14 @@ var ChainConfig = {
247
298
  [solanaMainNet.id]: {
248
299
  contractAddress: "",
249
300
  rpcUrl: solanaMainNet.rpcUrls.default.http[0]
301
+ },
302
+ [suiTestNet.id]: {
303
+ contractAddress: "0xb99be918b901c472b9839871b0341bf53a4d94a19312008a2e6c98908c92b778",
304
+ rpcUrl: suiTestNet.rpcUrls.default.http[0]
305
+ },
306
+ [suiMainNet.id]: {
307
+ contractAddress: "",
308
+ rpcUrl: suiMainNet.rpcUrls.default.http[0]
250
309
  }
251
310
  };
252
311
  var SupportedChains = [
@@ -3660,6 +3719,7 @@ var ChainType = /* @__PURE__ */ ((ChainType2) => {
3660
3719
  ChainType2["Evm"] = "evm";
3661
3720
  ChainType2["Ton"] = "ton";
3662
3721
  ChainType2["Solana"] = "solana";
3722
+ ChainType2["Sui"] = "sui";
3663
3723
  return ChainType2;
3664
3724
  })(ChainType || {});
3665
3725
  var AirdropSigIdentityTypeEnum = /* @__PURE__ */ ((AirdropSigIdentityTypeEnum2) => {
@@ -3726,7 +3786,7 @@ async function getOrCreateAssociatedTokenAccount({
3726
3786
  payer,
3727
3787
  mint,
3728
3788
  owner,
3729
- signTransaction,
3789
+ signTransaction: signTransaction2,
3730
3790
  allowOwnerOffCurve = false,
3731
3791
  commitment,
3732
3792
  programId = TOKEN_PROGRAM_ID,
@@ -3759,7 +3819,7 @@ async function getOrCreateAssociatedTokenAccount({
3759
3819
  console.log(blockhash, "blockhash");
3760
3820
  transaction.feePayer = await payer;
3761
3821
  transaction.recentBlockhash = blockhash;
3762
- const signed = await signTransaction(transaction);
3822
+ const signed = await signTransaction2(transaction);
3763
3823
  const signature = await connection.sendRawTransaction(signed.serialize());
3764
3824
  const latestBlockhash = await connection.getLatestBlockhash();
3765
3825
  await connection.confirmTransaction(
@@ -12218,6 +12278,395 @@ var AirdropService3 = class {
12218
12278
  return fromNano2(feeInfo.claimFee);
12219
12279
  }
12220
12280
  };
12281
+
12282
+ // src/contracts/sui/SuiContractClient.ts
12283
+ import { SuiClient, getFullnodeUrl as getFullnodeUrl2 } from "@mysten/sui/client";
12284
+ import { bcs } from "@mysten/sui/bcs";
12285
+ import { Transaction as Transaction2 } from "@mysten/sui/transactions";
12286
+ import { signTransaction } from "@mysten/wallet-standard";
12287
+ var SuiContractClientBase = class {
12288
+ packageId;
12289
+ chainId;
12290
+ moduleName;
12291
+ distributorId;
12292
+ wallet;
12293
+ client;
12294
+ constructor(contractInfo) {
12295
+ this.packageId = contractInfo.packageId;
12296
+ this.chainId = contractInfo.chainId ? contractInfo.chainId : "mainnet";
12297
+ this.moduleName = contractInfo.moduleName;
12298
+ this.distributorId = contractInfo.distributorId;
12299
+ this.wallet = contractInfo.walletClient;
12300
+ this.client = new SuiClient({ network: this.chainId, url: getFullnodeUrl2(this.chainId) });
12301
+ }
12302
+ async signAndExecute(tx) {
12303
+ if (!this.wallet) {
12304
+ throw new Error("Wallet not connected");
12305
+ }
12306
+ if (!this.wallet.features?.["sui:signTransaction"] && !this.wallet.features?.["sui:signTransactionBlock"]) {
12307
+ throw new Error("Wallet doesn't support transaction signing");
12308
+ }
12309
+ const accounts = await this.wallet.accounts;
12310
+ const client = this.client;
12311
+ if (!accounts || accounts.length === 0) {
12312
+ throw new Error("No wallet account connected");
12313
+ }
12314
+ const signerAccount = accounts[0];
12315
+ if ("setSenderIfNotSet" in tx) {
12316
+ tx.setSenderIfNotSet(signerAccount.address);
12317
+ }
12318
+ console.log(
12319
+ tx,
12320
+ await tx.toJSON({
12321
+ supportedIntents: [],
12322
+ client
12323
+ })
12324
+ );
12325
+ const chain = `sui:${this.chainId}`;
12326
+ const { signature, bytes } = await signTransaction(this.wallet, {
12327
+ transaction: {
12328
+ async toJSON() {
12329
+ return typeof tx === "string" ? tx : await tx.toJSON({
12330
+ supportedIntents: [],
12331
+ client
12332
+ });
12333
+ }
12334
+ },
12335
+ account: signerAccount,
12336
+ chain
12337
+ });
12338
+ const result = await this.client.executeTransactionBlock({
12339
+ transactionBlock: bytes,
12340
+ signature,
12341
+ options: {
12342
+ showEffects: true,
12343
+ showEvents: true
12344
+ }
12345
+ });
12346
+ await this.waitForTx(result.digest);
12347
+ return result;
12348
+ }
12349
+ async waitForTx(digest) {
12350
+ return this.client.waitForTransaction({
12351
+ digest,
12352
+ options: { showEffects: true, showEvents: true }
12353
+ });
12354
+ }
12355
+ /**
12356
+ * 读取合约返回值(只读调用)
12357
+ * @param func Move function 名
12358
+ * @param typeArgs 类型参数
12359
+ * @param args Move function 参数
12360
+ */
12361
+ async readMoveFunction(func, typeArgs = [], args = []) {
12362
+ const DEFAULT_SENDER = "0x0000000000000000000000000000000000000000000000000000000000000001";
12363
+ const sender = this.wallet?.accounts?.[0]?.address || DEFAULT_SENDER;
12364
+ if (!sender) throw new Error("No wallet account selected");
12365
+ const tx = new Transaction2();
12366
+ tx.moveCall({
12367
+ target: `${this.packageId}::${this.moduleName}::${func}`,
12368
+ typeArguments: typeArgs,
12369
+ arguments: args.map((arg) => {
12370
+ if (Array.isArray(arg)) return tx.pure(bcs.vector(bcs.vector(bcs.u8())).serialize(arg));
12371
+ if (typeof arg === "string" && arg.startsWith("0x")) return tx.object(arg);
12372
+ return tx.pure(arg);
12373
+ })
12374
+ });
12375
+ const res = await this.client.devInspectTransactionBlock({
12376
+ sender,
12377
+ transactionBlock: tx
12378
+ // ✅ 这里传 Transaction,不再报 TS 类型错误
12379
+ });
12380
+ console.log(res, "res");
12381
+ if (!res || !res.results?.[0]?.returnValues) {
12382
+ throw new Error(`Failed to read ${func}`);
12383
+ }
12384
+ return res.results[0].returnValues[0];
12385
+ }
12386
+ /**
12387
+ * 查询 Distributor MoveObject 的 fields
12388
+ */
12389
+ async getObjectFields(objectId) {
12390
+ const res = await this.client.getObject({
12391
+ id: objectId,
12392
+ options: { showContent: true }
12393
+ });
12394
+ if (res.data?.content?.dataType !== "moveObject") {
12395
+ throw new Error(`Object ${objectId} is not a moveObject`);
12396
+ }
12397
+ return res.data.content.fields;
12398
+ }
12399
+ async getSharedObjectRef(tx, objectId, mutable) {
12400
+ const obj = await this.client.getObject({
12401
+ id: objectId,
12402
+ options: { showOwner: true }
12403
+ });
12404
+ console.log(obj, "obj");
12405
+ if (!obj.data) {
12406
+ throw new Error(`Object ${objectId} not found`);
12407
+ }
12408
+ if (!obj.data.owner?.Shared) {
12409
+ throw new Error(`Object ${objectId} is not a shared object`);
12410
+ }
12411
+ const initialSharedVersion = obj.data.owner.Shared.initial_shared_version;
12412
+ return tx.sharedObjectRef({
12413
+ objectId,
12414
+ initialSharedVersion: Number(initialSharedVersion),
12415
+ mutable
12416
+ });
12417
+ }
12418
+ };
12419
+
12420
+ // src/contracts/sui/BaseDistributorClient.ts
12421
+ import { Transaction as Transaction3 } from "@mysten/sui/transactions";
12422
+ import { bcs as bcs2 } from "@mysten/sui/bcs";
12423
+ var SuiBaseDistributorClient = class extends SuiContractClientBase {
12424
+ constructor(info) {
12425
+ super({ ...info, moduleName: "base_distributor" });
12426
+ }
12427
+ async createDistributor(coinType, projectId, projectRegistryId) {
12428
+ const tx = new Transaction3();
12429
+ const projectIdBytes = bcs2.vector(bcs2.u8()).serialize(Array.from(Buffer.from(projectId, "utf8")));
12430
+ tx.moveCall({
12431
+ target: `${this.packageId}::${this.moduleName}::create_and_share_distributor`,
12432
+ typeArguments: [coinType],
12433
+ arguments: [tx.pure(projectIdBytes), tx.object(projectRegistryId)]
12434
+ });
12435
+ const result = await this.signAndExecute(tx);
12436
+ console.log(result, "res");
12437
+ let distributorId;
12438
+ if (result.effects?.created) {
12439
+ const createdObj = result.effects.created.find(
12440
+ (obj) => obj.owner === "Shared" || obj.owner?.Shared
12441
+ // 某些版本字段是对象
12442
+ );
12443
+ if (createdObj) {
12444
+ distributorId = createdObj.reference.objectId;
12445
+ }
12446
+ }
12447
+ return {
12448
+ txResult: result,
12449
+ distributorId
12450
+ };
12451
+ }
12452
+ async setBaseParams(coinType, startTime, endTime, authorizedSigner, ownerCapId) {
12453
+ const tx = new Transaction3();
12454
+ tx.moveCall({
12455
+ target: `${this.packageId}::${this.moduleName}::set_base_params`,
12456
+ typeArguments: [coinType],
12457
+ arguments: [
12458
+ tx.object(this.distributorId),
12459
+ tx.pure.u64(startTime),
12460
+ tx.pure.u64(endTime),
12461
+ tx.pure.address(authorizedSigner),
12462
+ tx.object(ownerCapId)
12463
+ ]
12464
+ });
12465
+ return this.signAndExecute(tx);
12466
+ }
12467
+ async togglePause(coinType, ownerCapId) {
12468
+ const tx = new Transaction3();
12469
+ tx.moveCall({
12470
+ target: `${this.packageId}::${this.moduleName}::toggle_pause`,
12471
+ typeArguments: [coinType],
12472
+ arguments: [tx.object(this.distributorId), tx.object(ownerCapId)]
12473
+ });
12474
+ return this.signAndExecute(tx);
12475
+ }
12476
+ async getDistributorInfo() {
12477
+ console.log(this.client, this.distributorId, "distributorId");
12478
+ const res = await this.client.getObject({
12479
+ id: this.distributorId,
12480
+ options: { showContent: true }
12481
+ });
12482
+ const content = res.data?.content;
12483
+ const fields = content?.fields;
12484
+ const match = content?.type.match(/<(.+)>$/);
12485
+ const coinType = match ? match[1] : "";
12486
+ return {
12487
+ startTime: fields?.start_time,
12488
+ endTime: fields?.end_time,
12489
+ signer: fields?.authorized_signer,
12490
+ ownerCapId: fields?.owner_cap_id,
12491
+ version: fields?.version ? String.fromCharCode(...fields.version) : void 0,
12492
+ feeCollector: fields?.fee_collector,
12493
+ token: coinType,
12494
+ tokenBalance: fields?.token_balance,
12495
+ paused: fields?.paused
12496
+ };
12497
+ }
12498
+ async getVersion() {
12499
+ const { version } = await this.getDistributorInfo();
12500
+ return version;
12501
+ }
12502
+ async getPaused() {
12503
+ const { paused } = await this.getDistributorInfo();
12504
+ return paused;
12505
+ }
12506
+ async getClaimFee(amount) {
12507
+ return 0n;
12508
+ }
12509
+ async batchFetchClaimed(coinType, claimIds) {
12510
+ const claimIdsBytes = claimIds.map((id) => Array.from(new TextEncoder().encode(id)));
12511
+ const res = await this.readMoveFunction(
12512
+ "get_claim_status",
12513
+ [coinType],
12514
+ [this.distributorId, claimIdsBytes]
12515
+ );
12516
+ console.log(res, "claims");
12517
+ const [data, type] = res;
12518
+ if (type === "vector<bool>" && data) {
12519
+ const uint8Array = new Uint8Array(data);
12520
+ return Array.from(bcs2.vector(bcs2.bool()).parse(uint8Array));
12521
+ }
12522
+ return [];
12523
+ }
12524
+ async getDistributorId(projectId, projectRegistryId) {
12525
+ const distributorId = await this.readMoveFunction(
12526
+ "get_distributor_by_project_id",
12527
+ [],
12528
+ [projectRegistryId, Array.from(Buffer.from(projectId, "utf8"))]
12529
+ );
12530
+ console.log(distributorId, "id");
12531
+ return distributorId.Some;
12532
+ }
12533
+ async getOwnerCapId() {
12534
+ const objects = await this.client.getOwnedObjects({
12535
+ owner: this.wallet.accounts[0].address,
12536
+ filter: {
12537
+ StructType: `${this.packageId}::ownable::OwnerCap`
12538
+ }
12539
+ });
12540
+ console.log(objects, "objects");
12541
+ const ownerCapId = objects.data[0]?.data?.objectId;
12542
+ return ownerCapId;
12543
+ }
12544
+ };
12545
+
12546
+ // src/contracts/sui/DistributorWithFeeClient.ts
12547
+ import { Transaction as Transaction4 } from "@mysten/sui/transactions";
12548
+ import { bcs as bcs3 } from "@mysten/sui/bcs";
12549
+ var SuiDistributorWithFeeClient = class extends SuiContractClientBase {
12550
+ constructor(info) {
12551
+ super({ ...info, moduleName: "fungible_token_with_fees_distributor" });
12552
+ }
12553
+ async prepareFeePayment(totalFeesStr, feeTokenType, tx) {
12554
+ const totalFees = BigInt(totalFeesStr);
12555
+ const coins = await this.client.getCoins({
12556
+ owner: this.wallet.accounts[0].address,
12557
+ coinType: feeTokenType
12558
+ });
12559
+ const suitableCoin = coins.data.find((coin) => BigInt(coin.balance) >= totalFees);
12560
+ if (suitableCoin) {
12561
+ if (BigInt(suitableCoin.balance) === totalFees) {
12562
+ return tx.object(suitableCoin.coinObjectId);
12563
+ }
12564
+ const [feeCoin] = tx.splitCoins(tx.object(suitableCoin.coinObjectId), [tx.pure(bcs3.u64().serialize(totalFees))]);
12565
+ return feeCoin;
12566
+ }
12567
+ throw new Error(`Insufficient balance. Required: ${totalFeesStr}, Available coins: ${coins.data.length}`);
12568
+ }
12569
+ async claimWithFee(coinType, feeCollector, data) {
12570
+ console.log(data, feeCollector, this.distributorId, "data");
12571
+ const recipients = data.map((item) => item.recipient);
12572
+ const claimIds = data.map((item) => item.claimId);
12573
+ const signatures = data.map((item) => item.signature);
12574
+ function encodeClaimDataWithFees(timestamp, amount, fees) {
12575
+ const tsBytes = bcs3.u64().serialize(timestamp).toBytes();
12576
+ const amtBytes = bcs3.u64().serialize(amount).toBytes();
12577
+ const feeBytes = bcs3.u64().serialize(fees).toBytes();
12578
+ return Array.from(new Uint8Array([...tsBytes, ...amtBytes, ...feeBytes]));
12579
+ }
12580
+ const datas = data.map((item) => {
12581
+ const claimData = item.data;
12582
+ return encodeClaimDataWithFees(
12583
+ BigInt(claimData.claimableTimestamp),
12584
+ BigInt(claimData.claimableAmount),
12585
+ BigInt(item.fees || "0")
12586
+ );
12587
+ });
12588
+ const totalFees = data.map((item) => BigInt(item.fees || "0")).reduce((sum, fee) => sum + fee, BigInt(0));
12589
+ const tx = new Transaction4();
12590
+ const distributor = await this.getSharedObjectRef(tx, this.distributorId, true);
12591
+ const feeConfig = await this.getSharedObjectRef(tx, feeCollector, true);
12592
+ const clock = await this.getSharedObjectRef(tx, "0x6", false);
12593
+ const hexToBytes = (hex) => Array.from(Buffer.from(hex.replace(/^0x/, ""), "hex"));
12594
+ const feeTokenType = "0x2::sui::SUI";
12595
+ const [feeCoin] = tx.splitCoins(
12596
+ tx.gas,
12597
+ // 用 gas 对象作为源
12598
+ [tx.pure(bcs3.u64().serialize(totalFees))]
12599
+ );
12600
+ tx.moveCall({
12601
+ target: `${this.packageId}::${this.moduleName}::claim_with_fees`,
12602
+ arguments: [
12603
+ distributor,
12604
+ feeConfig,
12605
+ tx.pure(bcs3.vector(bcs3.Address).serialize(recipients)),
12606
+ // recipients: vector<address>
12607
+ tx.pure(
12608
+ bcs3.vector(bcs3.vector(bcs3.u8())).serialize(claimIds.map((id) => Array.from(new TextEncoder().encode(id))))
12609
+ ),
12610
+ // claim_ids: vector<vector<u8>>
12611
+ tx.pure(bcs3.vector(bcs3.vector(bcs3.u8())).serialize(datas)),
12612
+ // claim_datas: vector<vector<u8>>
12613
+ tx.pure(bcs3.vector(bcs3.vector(bcs3.u8())).serialize(signatures.map((sig) => hexToBytes(sig)))),
12614
+ // signatures: vector<vector<u8>>
12615
+ feeCoin,
12616
+ clock
12617
+ ],
12618
+ typeArguments: [coinType, feeTokenType]
12619
+ // Token类型, Fee类型
12620
+ });
12621
+ const res = await this.signAndExecute(tx);
12622
+ return res.digest;
12623
+ }
12624
+ };
12625
+
12626
+ // src/contracts/sui/SignatureAirdropService.ts
12627
+ var SignatureAirdropService3 = class {
12628
+ options;
12629
+ constructor(options) {
12630
+ const chainConfig = getChainConfig(options.chainId);
12631
+ this.options = {
12632
+ ...options,
12633
+ packageId: chainConfig.contractAddress
12634
+ };
12635
+ }
12636
+ get baseDistributorClient() {
12637
+ if (!this.options) {
12638
+ throw new Error("options is empty");
12639
+ }
12640
+ return new SuiBaseDistributorClient(this.options);
12641
+ }
12642
+ get feeDistributorClient() {
12643
+ if (!this.options) {
12644
+ throw new Error("options is empty");
12645
+ }
12646
+ return new SuiDistributorWithFeeClient(this.options);
12647
+ }
12648
+ async getVersion() {
12649
+ return this.baseDistributorClient.getVersion();
12650
+ }
12651
+ async getClaimFee(address, amount) {
12652
+ const fee = await this.baseDistributorClient.getClaimFee(amount);
12653
+ return fee;
12654
+ }
12655
+ async batchFetchClaimed(userClaimIds) {
12656
+ const res = await this.baseDistributorClient.batchFetchClaimed(this.options.coinType, userClaimIds);
12657
+ return res;
12658
+ }
12659
+ async getPaused() {
12660
+ return this.baseDistributorClient.getPaused();
12661
+ }
12662
+ async getContractInfo() {
12663
+ return this.baseDistributorClient.getDistributorInfo();
12664
+ }
12665
+ async claim(data, recipient) {
12666
+ const { feeCollector } = await this.getContractInfo();
12667
+ return this.feeDistributorClient.claimWithFee(this.options.coinType, feeCollector, data);
12668
+ }
12669
+ };
12221
12670
  export {
12222
12671
  AirdropSigIdentityTypeEnum,
12223
12672
  ApiClient,
@@ -12232,6 +12681,7 @@ export {
12232
12681
  AirdropService2 as SolanaAirdropService,
12233
12682
  SignatureAirdropService2 as SolanaSignatureAirdropService,
12234
12683
  SplTokenVersion,
12684
+ SignatureAirdropService3 as SuiSignatureAirdropService,
12235
12685
  SupportedChains,
12236
12686
  AirdropService3 as TonAirdropService,
12237
12687
  TonAirdropVersionEnum,
@@ -12274,6 +12724,8 @@ export {
12274
12724
  signBrBTestnet,
12275
12725
  solanaDevNet,
12276
12726
  solanaMainNet,
12727
+ suiMainNet,
12728
+ suiTestNet,
12277
12729
  tonMainNet,
12278
12730
  tonTestNet
12279
12731
  };