@sign-global/tokentable-core 1.0.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/CHANGELOG.md +24 -0
- package/README.md +72 -0
- package/dist/index.d.mts +314 -224
- package/dist/index.d.ts +314 -224
- package/dist/index.js +489 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +486 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -3
- package/src/constants/chain-config.ts +20 -3
- package/src/constants/network.ts +53 -0
- package/src/contracts/evm/AirdropService.ts +18 -13
- package/src/contracts/index.ts +3 -0
- package/src/contracts/solana/AirdropSignatureClient.ts +2 -13
- package/src/contracts/solana/SignatureAirdropService.ts +2 -14
- package/src/contracts/solana/SolanaContractClient.ts +2 -2
- package/src/contracts/sui/BaseDistributorClient.ts +162 -0
- package/src/contracts/sui/DistributorWithFeeClient.ts +102 -0
- package/src/contracts/sui/SignatureAirdropService.ts +59 -0
- package/src/contracts/sui/SuiContractClient.ts +181 -0
- package/src/contracts/sui/__tests__/SignatureAirdropService.test.ts +71 -0
- package/src/services/airdrop/index.ts +4 -1
- package/src/types/index.ts +34 -6
- package/src/utils/api-client.ts +2 -2
- package/src/utils/utils.ts +8 -3
- package/vitest.config.ts +14 -0
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";
|
|
@@ -198,8 +249,8 @@ var ChainConfig = {
|
|
|
198
249
|
rpcUrl: "https://zetachain-mainnet.g.alchemy.com/v2/1tmk0FrhwTJHXh0XIyae7_SXw1lDCjgs"
|
|
199
250
|
},
|
|
200
251
|
[arbitrum.id]: {
|
|
201
|
-
contractAddress: "
|
|
202
|
-
rpcUrl:
|
|
252
|
+
contractAddress: "0xeD09c23677D02f4f486E55f0f081cfc114EEa53b",
|
|
253
|
+
rpcUrl: "https://arb-mainnet.g.alchemy.com/v2/udrqNPSB6i5n5L6QSM31Ng72h_hFOrVT"
|
|
203
254
|
},
|
|
204
255
|
[berachainTestnet.id]: {
|
|
205
256
|
contractAddress: "",
|
|
@@ -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
|
|
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(
|
|
@@ -3835,11 +3895,16 @@ var getCustomNaNoId = () => {
|
|
|
3835
3895
|
const nanoid = customAlphabet("1234567890abcdefghijklmnopqrstuvwxyz", 10);
|
|
3836
3896
|
return nanoid();
|
|
3837
3897
|
};
|
|
3838
|
-
var safeParseJSON = (str) => {
|
|
3898
|
+
var safeParseJSON = (str, defaultValue = {}) => {
|
|
3899
|
+
if (!str || typeof str !== "string") {
|
|
3900
|
+
return defaultValue;
|
|
3901
|
+
}
|
|
3839
3902
|
try {
|
|
3840
|
-
|
|
3903
|
+
const parsed = JSON.parse(str);
|
|
3904
|
+
return parsed !== null && parsed !== void 0 ? parsed : defaultValue;
|
|
3841
3905
|
} catch (error) {
|
|
3842
|
-
|
|
3906
|
+
console.warn("Failed to parse JSON:", error);
|
|
3907
|
+
return defaultValue;
|
|
3843
3908
|
}
|
|
3844
3909
|
};
|
|
3845
3910
|
var isBeforeByUnix = (unix) => {
|
|
@@ -3907,7 +3972,7 @@ var ApiClient = class _ApiClient {
|
|
|
3907
3972
|
}
|
|
3908
3973
|
const res = await fetch(finalUrl, init);
|
|
3909
3974
|
const [status, resData] = await Promise.all([res.status, res.json()]);
|
|
3910
|
-
if (status < 200
|
|
3975
|
+
if (status < 200 || status >= 300 || resData?.success !== true) {
|
|
3911
3976
|
return Promise.reject(resData);
|
|
3912
3977
|
}
|
|
3913
3978
|
return resData.data;
|
|
@@ -4086,22 +4151,27 @@ var AirdropService = class {
|
|
|
4086
4151
|
return this.airdropV3FeeClient.getFeelessThreshold();
|
|
4087
4152
|
}
|
|
4088
4153
|
async getClaimFee(address, amount) {
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
}
|
|
4093
|
-
const feeClient = this.getFeeClient(feeCollector);
|
|
4094
|
-
const fee = await feeClient.getFee(address, amount);
|
|
4095
|
-
const version = await this.getVersion();
|
|
4096
|
-
if (version === "0.3.0" /* V3 */) {
|
|
4097
|
-
const threshold = await this.getFeelessThreshold();
|
|
4098
|
-
if (isGreaterThan(Number(amount), Number(threshold))) {
|
|
4099
|
-
return fee;
|
|
4100
|
-
} else {
|
|
4154
|
+
try {
|
|
4155
|
+
const feeCollector = await this.feeCollectors(this.options.contractAddress);
|
|
4156
|
+
if (!feeCollector || feeCollector === ZERO_ADDRESS) {
|
|
4101
4157
|
return void 0;
|
|
4102
4158
|
}
|
|
4159
|
+
const feeClient = this.getFeeClient(feeCollector);
|
|
4160
|
+
const fee = await feeClient.getFee(address, amount);
|
|
4161
|
+
const version = await this.getVersion();
|
|
4162
|
+
if (version === "0.3.0" /* V3 */) {
|
|
4163
|
+
const threshold = await this.getFeelessThreshold();
|
|
4164
|
+
if (isGreaterThan(Number(amount), Number(threshold))) {
|
|
4165
|
+
return fee;
|
|
4166
|
+
} else {
|
|
4167
|
+
return void 0;
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
return fee;
|
|
4171
|
+
} catch (error) {
|
|
4172
|
+
console.error("Failed to get claim fee:", error);
|
|
4173
|
+
throw new Error(`Failed to calculate claim fee: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4103
4174
|
}
|
|
4104
|
-
return fee;
|
|
4105
4175
|
}
|
|
4106
4176
|
async getKycThreshold() {
|
|
4107
4177
|
const kycClient = await this.getKycClient();
|
|
@@ -10416,7 +10486,9 @@ var createApiClient = (baseURL) => {
|
|
|
10416
10486
|
var getAirdropProject = async (projectId, baseURL) => {
|
|
10417
10487
|
const client = createApiClient(baseURL);
|
|
10418
10488
|
const res = await client.get(`/airdrop-open/projects/${projectId}`);
|
|
10419
|
-
if (!res)
|
|
10489
|
+
if (!res) {
|
|
10490
|
+
throw new Error(`Project not found: ${projectId}`);
|
|
10491
|
+
}
|
|
10420
10492
|
const projectConfig = res?.themeConf;
|
|
10421
10493
|
const blockCountries = safeParseJSON(projectConfig?.blockCountries);
|
|
10422
10494
|
return {
|
|
@@ -12206,6 +12278,395 @@ var AirdropService3 = class {
|
|
|
12206
12278
|
return fromNano2(feeInfo.claimFee);
|
|
12207
12279
|
}
|
|
12208
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
|
+
};
|
|
12209
12670
|
export {
|
|
12210
12671
|
AirdropSigIdentityTypeEnum,
|
|
12211
12672
|
ApiClient,
|
|
@@ -12220,6 +12681,7 @@ export {
|
|
|
12220
12681
|
AirdropService2 as SolanaAirdropService,
|
|
12221
12682
|
SignatureAirdropService2 as SolanaSignatureAirdropService,
|
|
12222
12683
|
SplTokenVersion,
|
|
12684
|
+
SignatureAirdropService3 as SuiSignatureAirdropService,
|
|
12223
12685
|
SupportedChains,
|
|
12224
12686
|
AirdropService3 as TonAirdropService,
|
|
12225
12687
|
TonAirdropVersionEnum,
|
|
@@ -12262,6 +12724,8 @@ export {
|
|
|
12262
12724
|
signBrBTestnet,
|
|
12263
12725
|
solanaDevNet,
|
|
12264
12726
|
solanaMainNet,
|
|
12727
|
+
suiMainNet,
|
|
12728
|
+
suiTestNet,
|
|
12265
12729
|
tonMainNet,
|
|
12266
12730
|
tonTestNet
|
|
12267
12731
|
};
|