sui-easy-sdk 0.0.1
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/README.md +120 -0
- package/dist/client.d.ts +19 -0
- package/dist/client.js +67 -0
- package/dist/coin-utils.d.ts +35 -0
- package/dist/coin-utils.js +89 -0
- package/dist/data-walker.d.ts +37 -0
- package/dist/data-walker.js +57 -0
- package/dist/display.d.ts +17 -0
- package/dist/display.js +59 -0
- package/dist/event-monitor.d.ts +16 -0
- package/dist/event-monitor.js +62 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +25 -0
- package/dist/kiosk.d.ts +27 -0
- package/dist/kiosk.js +56 -0
- package/dist/names.d.ts +17 -0
- package/dist/names.js +53 -0
- package/dist/tx-builder.d.ts +40 -0
- package/dist/tx-builder.js +108 -0
- package/dist/utils.d.ts +35 -0
- package/dist/utils.js +58 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# SUI Easy SDK
|
|
2
|
+
|
|
3
|
+
The production-grade, all-in-one SDK for SUI blockchain development.
|
|
4
|
+
Designed to solve common developer pain points: pagination, name resolution, argument wrapping, and complex standard interactions (Kiosk, Display).
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
\`\`\`bash
|
|
9
|
+
npm install sui-easy-sdk @mysten/sui @mysten/bcs
|
|
10
|
+
\`\`\`
|
|
11
|
+
|
|
12
|
+
## Publishing to NPM
|
|
13
|
+
|
|
14
|
+
1. Build the project:
|
|
15
|
+
\`\`\`bash
|
|
16
|
+
npm run build
|
|
17
|
+
\`\`\`
|
|
18
|
+
2. Login to NPM:
|
|
19
|
+
\`\`\`bash
|
|
20
|
+
npm login
|
|
21
|
+
\`\`\`
|
|
22
|
+
3. Publish:
|
|
23
|
+
- **If you have 2FA enabled (likely):**
|
|
24
|
+
\`\`\`bash
|
|
25
|
+
npm publish --access public --otp=YOUR_OTP_CODE
|
|
26
|
+
\`\`\`
|
|
27
|
+
- Otherwise:
|
|
28
|
+
\`\`\`bash
|
|
29
|
+
npm publish --access public
|
|
30
|
+
\`\`\`
|
|
31
|
+
|
|
32
|
+
## Core Features
|
|
33
|
+
|
|
34
|
+
### 1. Universal Naming
|
|
35
|
+
**Every method** that accepts an address also accepts a `.sui` name.
|
|
36
|
+
\`\`\`typescript
|
|
37
|
+
const objects = await client.getAllOwnedObjects('demo.sui');
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
### 2. Auto-Pagination
|
|
41
|
+
Never write a `while(hasNextPage)` loop again.
|
|
42
|
+
\`\`\`typescript
|
|
43
|
+
const allCoins = await CoinUtils.selectCoins(client, 'demo.sui', 500n, '0x...::type', 'all');
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
### 3. Fluent Transaction Builder
|
|
47
|
+
Auto-wraps `number` -> `u64`, `string` -> `Pure` or `Object`, performs async name resolution.
|
|
48
|
+
\`\`\`typescript
|
|
49
|
+
await builder.moveCallWithResolving(client, 'pkg::mod::fn', ['friend.sui', 100]);
|
|
50
|
+
\`\`\`
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## API Reference
|
|
55
|
+
|
|
56
|
+
### `SuiEasyClient`
|
|
57
|
+
Extends `SuiClient` (official SDK).
|
|
58
|
+
|
|
59
|
+
- `constructor(options: SuiClientOptions)`: Standard initialization.
|
|
60
|
+
- `async resolveAddress(addressOrName: string): Promise<string>`:
|
|
61
|
+
- Resolves `.sui` name to 0x address. Returns input if already address.
|
|
62
|
+
- `async getAllOwnedObjects(addressOrName: string, typeFilter?: string)`:
|
|
63
|
+
- Fetches **ALL** pages of owned objects.
|
|
64
|
+
- Auto-resolves owner name.
|
|
65
|
+
- `parseError(error: any): string`:
|
|
66
|
+
- Extracts Move Abort codes from RPC errors.
|
|
67
|
+
|
|
68
|
+
### `AdvancedTxBuilder`
|
|
69
|
+
Wraps `Transaction`.
|
|
70
|
+
|
|
71
|
+
- `tx`: Access the underlying `Transaction` object.
|
|
72
|
+
- `moveCall(target, args, typeArgs)`:
|
|
73
|
+
- Adds a MoveCall command.
|
|
74
|
+
- Auto-wraps JS types (`number`->`u64`, `boolean`->`bool`).
|
|
75
|
+
- Treats hex-strings (`0x...`) as **Objects** by default.
|
|
76
|
+
- `async moveCallWithResolving(client, target, args, typeArgs)`:
|
|
77
|
+
- Resolves any string args ending in `.sui` to addresses before adding the command.
|
|
78
|
+
- `transferObjects(objects, recipient)`:
|
|
79
|
+
- Recipient can be a name or address (if resolved beforehand, otherwise use `moveCall` variants).
|
|
80
|
+
- `splitCoins(coin, amounts)`: Wrapper for `tx.splitCoins`.
|
|
81
|
+
- `mergeCoins(dest, sources)`: Wrapper for `tx.mergeCoins`.
|
|
82
|
+
|
|
83
|
+
### `CoinUtils`
|
|
84
|
+
- `static async selectCoins(client, ownerOrName, amount, coinType, strategy)`:
|
|
85
|
+
- `strategy`: `'biggest-first'` (gas opt), `'exact-match'` (payment), or `'all'`.
|
|
86
|
+
- Returns `{ selectedCoins, totalAmount }`.
|
|
87
|
+
- `static createCoinInput(tx, coins, amount, coinType)`:
|
|
88
|
+
- Returns a coin object ready for use (merges inputs if needed, then splits).
|
|
89
|
+
|
|
90
|
+
### `KioskUtils`
|
|
91
|
+
- `createKiosk(tx)`: Returns `[kiosk, kioskOwnerCap]`.
|
|
92
|
+
- `place(tx, kiosk, cap, item, itemType)`: Places item.
|
|
93
|
+
- `withdraw(tx, kiosk, cap, itemId, itemType)`: Withdraws item.
|
|
94
|
+
|
|
95
|
+
### `ObjectDisplayUtils`
|
|
96
|
+
- `getDisplays(objectIds)`:
|
|
97
|
+
- Returns map of `{ [id]: DisplayFields }`.
|
|
98
|
+
- Uses `multiGetObjects` for efficiency.
|
|
99
|
+
- `static resolveImageUrl(display)`:
|
|
100
|
+
- Sanitizes IPFS URLs.
|
|
101
|
+
|
|
102
|
+
### `DataUtils`
|
|
103
|
+
- `stringToVectorU8(str)`: Encodes string to `vector<u8>`.
|
|
104
|
+
- `vectorU8ToString(bytes)`: Decodes bytes.
|
|
105
|
+
- `toHex(bytes)`, `fromHex(str)`: Hex utils.
|
|
106
|
+
- `normalizeAddress(addr)`: 0x-padding.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Example Scenarios
|
|
111
|
+
|
|
112
|
+
### Buying an NFT with Kiosk and Names
|
|
113
|
+
See `examples/showcase.ts` for the full code.
|
|
114
|
+
|
|
115
|
+
\`\`\`typescript
|
|
116
|
+
const { selectedCoins } = await CoinUtils.selectCoins(client, 'buyer.sui', price);
|
|
117
|
+
const [payment] = CoinUtils.createCoinInput(builder.tx, selectedCoins, price, SUI_TYPE);
|
|
118
|
+
|
|
119
|
+
await builder.moveCallWithResolving(client, 'mkt::buy', ['kiosk.sui', payment]);
|
|
120
|
+
\`\`\`
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SuiClient, SuiClientOptions } from '@mysten/sui/client';
|
|
2
|
+
export declare class SuiEasyClient extends SuiClient {
|
|
3
|
+
constructor(options: SuiClientOptions);
|
|
4
|
+
/**
|
|
5
|
+
* Helper to resolve an address or name to an address.
|
|
6
|
+
* If input ends with .sui, it tries to resolve it.
|
|
7
|
+
*/
|
|
8
|
+
resolveAddress(addressOrName: string): Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Fetches all owned objects of a specific type, automatically handling pagination.
|
|
11
|
+
* Supports SUI names (e.g. "demo.sui") as owner.
|
|
12
|
+
*/
|
|
13
|
+
getAllOwnedObjects(addressOrName: string, typeFilter?: string): Promise<import("@mysten/sui/client").SuiObjectResponse[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Parses SUI error messages into a more human-readable format.
|
|
16
|
+
* Extracts Move abort codes if present.
|
|
17
|
+
*/
|
|
18
|
+
parseError(error: any): string;
|
|
19
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SuiEasyClient = void 0;
|
|
4
|
+
const client_1 = require("@mysten/sui/client");
|
|
5
|
+
class SuiEasyClient extends client_1.SuiClient {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
super(options);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Helper to resolve an address or name to an address.
|
|
11
|
+
* If input ends with .sui, it tries to resolve it.
|
|
12
|
+
*/
|
|
13
|
+
async resolveAddress(addressOrName) {
|
|
14
|
+
if (addressOrName.endsWith('.sui')) {
|
|
15
|
+
const address = await this.resolveNameServiceAddress({ name: addressOrName });
|
|
16
|
+
if (!address)
|
|
17
|
+
throw new Error(`Could not resolve name: ${addressOrName}`);
|
|
18
|
+
return address;
|
|
19
|
+
}
|
|
20
|
+
return addressOrName;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Fetches all owned objects of a specific type, automatically handling pagination.
|
|
24
|
+
* Supports SUI names (e.g. "demo.sui") as owner.
|
|
25
|
+
*/
|
|
26
|
+
async getAllOwnedObjects(addressOrName, typeFilter) {
|
|
27
|
+
const address = await this.resolveAddress(addressOrName);
|
|
28
|
+
let hasNextPage = true;
|
|
29
|
+
let nextCursor = null;
|
|
30
|
+
const allData = [];
|
|
31
|
+
while (hasNextPage) {
|
|
32
|
+
const response = await this.getOwnedObjects({
|
|
33
|
+
owner: address,
|
|
34
|
+
filter: typeFilter ? { StructType: typeFilter } : undefined,
|
|
35
|
+
cursor: nextCursor,
|
|
36
|
+
options: {
|
|
37
|
+
showContent: true,
|
|
38
|
+
showType: true,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
allData.push(...response.data);
|
|
42
|
+
hasNextPage = response.hasNextPage;
|
|
43
|
+
nextCursor = response.nextCursor;
|
|
44
|
+
}
|
|
45
|
+
return allData;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Parses SUI error messages into a more human-readable format.
|
|
49
|
+
* Extracts Move abort codes if present.
|
|
50
|
+
*/
|
|
51
|
+
parseError(error) {
|
|
52
|
+
if (typeof error === 'string')
|
|
53
|
+
return error;
|
|
54
|
+
if (error?.message) {
|
|
55
|
+
const message = error.message;
|
|
56
|
+
// Try to extract Move abort code
|
|
57
|
+
// Example: "MoveAbort(..., 1001)"
|
|
58
|
+
const abortMatch = message.match(/MoveAbort\(.*?, (\d+)\)/);
|
|
59
|
+
if (abortMatch) {
|
|
60
|
+
return `Transaction failed with Move Abort Code: ${abortMatch[1]}. Check container module error constants.`;
|
|
61
|
+
}
|
|
62
|
+
return message;
|
|
63
|
+
}
|
|
64
|
+
return 'Unknown error occurred';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.SuiEasyClient = SuiEasyClient;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SuiClient } from '@mysten/sui/client';
|
|
2
|
+
import { Transaction } from '@mysten/sui/transactions';
|
|
3
|
+
export type CoinSelectionStrategy = 'all' | 'biggest-first' | 'exact-match';
|
|
4
|
+
export declare class CoinUtils {
|
|
5
|
+
/**
|
|
6
|
+
* Selects coins to cover a specific amount with pagination and strategies.
|
|
7
|
+
*
|
|
8
|
+
* @param client SUI Client
|
|
9
|
+
* @param owner Address of the coin owner
|
|
10
|
+
* @param amount Amount required (in MIST)
|
|
11
|
+
* @param coinType Coin type (default SUI)
|
|
12
|
+
* @param strategy 'biggest-first' is usually best to minimize object count.
|
|
13
|
+
*/
|
|
14
|
+
static selectCoins(client: SuiClient, ownerOrName: string, amount: bigint, coinType?: string, strategy?: CoinSelectionStrategy): Promise<{
|
|
15
|
+
selectedCoins: any[];
|
|
16
|
+
totalAmount: bigint;
|
|
17
|
+
coinType: string;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Prepares inputs for a transaction.
|
|
21
|
+
* If multiple coins are needed, it sets the first as the primary payment coin
|
|
22
|
+
* and merges others into it (or uses them as input for PaySui/Pay).
|
|
23
|
+
*
|
|
24
|
+
* Note: Programmable Transactions handle multiple inputs natively for gas payment,
|
|
25
|
+
* but for transferring a specific amount of a custom coin, you often need to merge
|
|
26
|
+
* or use `splitCoins` from a specific input.
|
|
27
|
+
*/
|
|
28
|
+
static createCoinInput(tx: Transaction, coins: any[], amount: bigint, coinType: string): {
|
|
29
|
+
$kind: "Result";
|
|
30
|
+
Result: number;
|
|
31
|
+
} & [{
|
|
32
|
+
$kind: "NestedResult";
|
|
33
|
+
NestedResult: [number, number];
|
|
34
|
+
}];
|
|
35
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CoinUtils = void 0;
|
|
4
|
+
class CoinUtils {
|
|
5
|
+
/**
|
|
6
|
+
* Selects coins to cover a specific amount with pagination and strategies.
|
|
7
|
+
*
|
|
8
|
+
* @param client SUI Client
|
|
9
|
+
* @param owner Address of the coin owner
|
|
10
|
+
* @param amount Amount required (in MIST)
|
|
11
|
+
* @param coinType Coin type (default SUI)
|
|
12
|
+
* @param strategy 'biggest-first' is usually best to minimize object count.
|
|
13
|
+
*/
|
|
14
|
+
static async selectCoins(client, ownerOrName, amount, coinType = '0x2::sui::SUI', strategy = 'biggest-first') {
|
|
15
|
+
// Resolve owner if client has resolution capability (Basic Duck typing or check method)
|
|
16
|
+
let owner = ownerOrName;
|
|
17
|
+
if (ownerOrName.endsWith('.sui') && 'resolveNameServiceAddress' in client) {
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
const resolved = await client.resolveNameServiceAddress({ name: ownerOrName });
|
|
20
|
+
if (!resolved)
|
|
21
|
+
throw new Error(`Could not resolve name: ${ownerOrName}`);
|
|
22
|
+
owner = resolved;
|
|
23
|
+
}
|
|
24
|
+
let hasNextPage = true;
|
|
25
|
+
let nextCursor = null;
|
|
26
|
+
let allCoins = [];
|
|
27
|
+
// 1. Fetch ALL coins (or until safe limit)
|
|
28
|
+
// In production, might want to limit this loop if user has 10k coins
|
|
29
|
+
while (hasNextPage) {
|
|
30
|
+
const response = await client.getCoins({
|
|
31
|
+
owner,
|
|
32
|
+
coinType,
|
|
33
|
+
cursor: nextCursor
|
|
34
|
+
});
|
|
35
|
+
allCoins.push(...response.data);
|
|
36
|
+
hasNextPage = response.hasNextPage;
|
|
37
|
+
nextCursor = response.nextCursor;
|
|
38
|
+
}
|
|
39
|
+
const sortedCoins = allCoins.sort((a, b) => {
|
|
40
|
+
const balA = BigInt(a.balance);
|
|
41
|
+
const balB = BigInt(b.balance);
|
|
42
|
+
// Descending for biggest-first
|
|
43
|
+
return balA < balB ? 1 : -1;
|
|
44
|
+
});
|
|
45
|
+
const selected = [];
|
|
46
|
+
let total = 0n;
|
|
47
|
+
for (const coin of sortedCoins) {
|
|
48
|
+
if (total >= amount)
|
|
49
|
+
break;
|
|
50
|
+
selected.push(coin);
|
|
51
|
+
total += BigInt(coin.balance);
|
|
52
|
+
}
|
|
53
|
+
if (total < amount) {
|
|
54
|
+
throw new Error(`Insufficient balance. Needed ${amount}, found ${total}`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
selectedCoins: selected,
|
|
58
|
+
totalAmount: total,
|
|
59
|
+
coinType
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Prepares inputs for a transaction.
|
|
64
|
+
* If multiple coins are needed, it sets the first as the primary payment coin
|
|
65
|
+
* and merges others into it (or uses them as input for PaySui/Pay).
|
|
66
|
+
*
|
|
67
|
+
* Note: Programmable Transactions handle multiple inputs natively for gas payment,
|
|
68
|
+
* but for transferring a specific amount of a custom coin, you often need to merge
|
|
69
|
+
* or use `splitCoins` from a specific input.
|
|
70
|
+
*/
|
|
71
|
+
static createCoinInput(tx, coins, amount, coinType) {
|
|
72
|
+
// If it's SUI and we are using it for gas, tx.gas is usually enough if strictly for gas.
|
|
73
|
+
// But if we are transferring SUI, we might need to split from gas.
|
|
74
|
+
if (coinType === '0x2::sui::SUI') {
|
|
75
|
+
// For SUI, we can usually just use `tx.splitCoins(tx.gas, [amount])`
|
|
76
|
+
// assuming the gas object selected by the wallet/signer covers it.
|
|
77
|
+
// However, provided coins list allows explicit behavior.
|
|
78
|
+
return tx.splitCoins(tx.gas, [amount]);
|
|
79
|
+
}
|
|
80
|
+
// For Custom Tokens:
|
|
81
|
+
// We typically take the first coin, merge others into it, then split.
|
|
82
|
+
const primaryCoin = tx.object(coins[0].coinObjectId);
|
|
83
|
+
if (coins.length > 1) {
|
|
84
|
+
tx.mergeCoins(primaryCoin, coins.slice(1).map(c => tx.object(c.coinObjectId)));
|
|
85
|
+
}
|
|
86
|
+
return tx.splitCoins(primaryCoin, [amount]);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.CoinUtils = CoinUtils;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { SuiClient } from '@mysten/sui/client';
|
|
2
|
+
export declare class DataWalker {
|
|
3
|
+
private client;
|
|
4
|
+
constructor(client: SuiClient);
|
|
5
|
+
/**
|
|
6
|
+
* Recursively fetches all dynamic fields of an object.
|
|
7
|
+
* WARNING: This can be expensive for large tables. Use with caution/limits.
|
|
8
|
+
*/
|
|
9
|
+
getAllDynamicFields(parentId: string): Promise<({
|
|
10
|
+
digest: string;
|
|
11
|
+
name: import("@mysten/sui/client").DynamicFieldName;
|
|
12
|
+
objectId: string;
|
|
13
|
+
objectType: string;
|
|
14
|
+
type: import("@mysten/sui/client").DynamicFieldType;
|
|
15
|
+
version: string;
|
|
16
|
+
bcsEncoding: "base64";
|
|
17
|
+
bcsName: string;
|
|
18
|
+
} | {
|
|
19
|
+
digest: string;
|
|
20
|
+
name: import("@mysten/sui/client").DynamicFieldName;
|
|
21
|
+
objectId: string;
|
|
22
|
+
objectType: string;
|
|
23
|
+
type: import("@mysten/sui/client").DynamicFieldType;
|
|
24
|
+
version: string;
|
|
25
|
+
bcsEncoding: "base58";
|
|
26
|
+
bcsName: string;
|
|
27
|
+
})[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Walks a Table/Bag, calling a visitor function for each item.
|
|
30
|
+
* Efficiently paginates through keys.
|
|
31
|
+
*/
|
|
32
|
+
walkTable(tableId: string, visitor: (field: any) => Promise<void> | void): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Fetches the value of a specific Dynamic Field or Table entry.
|
|
35
|
+
*/
|
|
36
|
+
getPayload(parentId: string, name: any): Promise<import("@mysten/sui/client").SuiObjectResponse>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DataWalker = void 0;
|
|
4
|
+
class DataWalker {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Recursively fetches all dynamic fields of an object.
|
|
10
|
+
* WARNING: This can be expensive for large tables. Use with caution/limits.
|
|
11
|
+
*/
|
|
12
|
+
async getAllDynamicFields(parentId) {
|
|
13
|
+
let hasNextPage = true;
|
|
14
|
+
let nextCursor = null;
|
|
15
|
+
const allFields = [];
|
|
16
|
+
while (hasNextPage) {
|
|
17
|
+
const response = await this.client.getDynamicFields({
|
|
18
|
+
parentId,
|
|
19
|
+
cursor: nextCursor,
|
|
20
|
+
});
|
|
21
|
+
allFields.push(...response.data);
|
|
22
|
+
hasNextPage = response.hasNextPage;
|
|
23
|
+
nextCursor = response.nextCursor;
|
|
24
|
+
}
|
|
25
|
+
return allFields;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Walks a Table/Bag, calling a visitor function for each item.
|
|
29
|
+
* Efficiently paginates through keys.
|
|
30
|
+
*/
|
|
31
|
+
async walkTable(tableId, visitor) {
|
|
32
|
+
let hasNextPage = true;
|
|
33
|
+
let nextCursor = null;
|
|
34
|
+
while (hasNextPage) {
|
|
35
|
+
const response = await this.client.getDynamicFields({
|
|
36
|
+
parentId: tableId,
|
|
37
|
+
cursor: nextCursor,
|
|
38
|
+
});
|
|
39
|
+
for (const item of response.data) {
|
|
40
|
+
await visitor(item);
|
|
41
|
+
}
|
|
42
|
+
hasNextPage = response.hasNextPage;
|
|
43
|
+
nextCursor = response.nextCursor;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fetches the value of a specific Dynamic Field or Table entry.
|
|
48
|
+
*/
|
|
49
|
+
async getPayload(parentId, name) {
|
|
50
|
+
// "name" needs to be properly typed/structured (type, value)
|
|
51
|
+
return this.client.getDynamicFieldObject({
|
|
52
|
+
parentId,
|
|
53
|
+
name,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.DataWalker = DataWalker;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SuiClient } from '@mysten/sui/client';
|
|
2
|
+
export declare class ObjectDisplayUtils {
|
|
3
|
+
private client;
|
|
4
|
+
constructor(client: SuiClient);
|
|
5
|
+
/**
|
|
6
|
+
* Fetches the display metadata for a list of objects.
|
|
7
|
+
*
|
|
8
|
+
* @param objectIds List of object IDs
|
|
9
|
+
* @returns Map of ID -> Display Fields
|
|
10
|
+
*/
|
|
11
|
+
getDisplays(objectIds: string[]): Promise<Record<string, any>>;
|
|
12
|
+
/**
|
|
13
|
+
* Resolves an image URL from a display object.
|
|
14
|
+
* Handles IPFS, etc.
|
|
15
|
+
*/
|
|
16
|
+
static resolveImageUrl(display: any): string | null;
|
|
17
|
+
}
|
package/dist/display.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ObjectDisplayUtils = void 0;
|
|
4
|
+
class ObjectDisplayUtils {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Fetches the display metadata for a list of objects.
|
|
10
|
+
*
|
|
11
|
+
* @param objectIds List of object IDs
|
|
12
|
+
* @returns Map of ID -> Display Fields
|
|
13
|
+
*/
|
|
14
|
+
async getDisplays(objectIds) {
|
|
15
|
+
if (objectIds.length === 0)
|
|
16
|
+
return {};
|
|
17
|
+
// SUI RPC usually returns display in `getMultiGetObjects`
|
|
18
|
+
const responses = await this.client.multiGetObjects({
|
|
19
|
+
ids: objectIds,
|
|
20
|
+
options: {
|
|
21
|
+
showDisplay: true,
|
|
22
|
+
showType: true,
|
|
23
|
+
showContent: true // sometimes fallback needed
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const results = {};
|
|
27
|
+
for (const resp of responses) {
|
|
28
|
+
if (resp.data && resp.data.objectId) {
|
|
29
|
+
const display = resp.data.display?.data;
|
|
30
|
+
const id = resp.data.objectId;
|
|
31
|
+
if (display) {
|
|
32
|
+
results[id] = display;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Fallback heuristics could go here if using old standards (Url field)
|
|
36
|
+
// But for this SDK we focus on the Display standard.
|
|
37
|
+
results[id] = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolves an image URL from a display object.
|
|
45
|
+
* Handles IPFS, etc.
|
|
46
|
+
*/
|
|
47
|
+
static resolveImageUrl(display) {
|
|
48
|
+
if (!display)
|
|
49
|
+
return null;
|
|
50
|
+
let url = display.image_url || display.img_url || display.url;
|
|
51
|
+
if (!url)
|
|
52
|
+
return null;
|
|
53
|
+
if (url.startsWith('ipfs://')) {
|
|
54
|
+
return url.replace('ipfs://', 'https://ipfs.io/ipfs/');
|
|
55
|
+
}
|
|
56
|
+
return url;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.ObjectDisplayUtils = ObjectDisplayUtils;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SuiClient, SuiEvent } from '@mysten/sui/client';
|
|
2
|
+
export declare class EventMonitor {
|
|
3
|
+
private client;
|
|
4
|
+
pollIntervalMs: number;
|
|
5
|
+
private isPolling;
|
|
6
|
+
private intervalId;
|
|
7
|
+
private processedCursors;
|
|
8
|
+
constructor(client: SuiClient, pollIntervalMs?: number);
|
|
9
|
+
/**
|
|
10
|
+
* Subscribes to events via polling (more reliable than WebSocket for simple historical fetch).
|
|
11
|
+
* Note: The official simplified SDK often relies on RPC method "suix_queryEvents".
|
|
12
|
+
* This is a "Robust" monitor that polls if WSS is flaky or unavailable.
|
|
13
|
+
*/
|
|
14
|
+
startPolling(filter: any, onEvent: (event: SuiEvent) => void, startTime?: number): Promise<void>;
|
|
15
|
+
stop(): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventMonitor = void 0;
|
|
4
|
+
class EventMonitor {
|
|
5
|
+
constructor(client, pollIntervalMs = 2000) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.pollIntervalMs = pollIntervalMs;
|
|
8
|
+
this.isPolling = false;
|
|
9
|
+
this.intervalId = null;
|
|
10
|
+
this.processedCursors = new Set(); // Simple dedup mechanism
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Subscribes to events via polling (more reliable than WebSocket for simple historical fetch).
|
|
14
|
+
* Note: The official simplified SDK often relies on RPC method "suix_queryEvents".
|
|
15
|
+
* This is a "Robust" monitor that polls if WSS is flaky or unavailable.
|
|
16
|
+
*/
|
|
17
|
+
async startPolling(filter, onEvent, startTime = Date.now()) {
|
|
18
|
+
if (this.isPolling)
|
|
19
|
+
return;
|
|
20
|
+
this.isPolling = true;
|
|
21
|
+
// Start slightly in the past or now
|
|
22
|
+
// In a real implementation, we would track the last "txDigest" and "eventSeq".
|
|
23
|
+
let cursor = undefined; // Start from latest if undefined usually, but queryEvents behavior varies.
|
|
24
|
+
// Actually, for queryEvents, filtering by time range is not direct,
|
|
25
|
+
// we filter by MoveModule/Sender etc. and paginate backward/forward.
|
|
26
|
+
// This is a simplified poller.
|
|
27
|
+
this.intervalId = setInterval(async () => {
|
|
28
|
+
try {
|
|
29
|
+
const events = await this.client.queryEvents({
|
|
30
|
+
query: filter,
|
|
31
|
+
cursor,
|
|
32
|
+
limit: 10,
|
|
33
|
+
order: 'ascending' // We want new events
|
|
34
|
+
});
|
|
35
|
+
// If we have no cursor, we might get old events.
|
|
36
|
+
// A robust system needs to checkpoint the cursor.
|
|
37
|
+
for (const event of events.data) {
|
|
38
|
+
// crude dedup or time check
|
|
39
|
+
if (Number(event.timestampMs) >= startTime) {
|
|
40
|
+
const eventId = `${event.id.txDigest}_${event.id.eventSeq}`;
|
|
41
|
+
if (!this.processedCursors.has(eventId)) {
|
|
42
|
+
this.processedCursors.add(eventId);
|
|
43
|
+
onEvent(event);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (events.nextCursor) {
|
|
48
|
+
cursor = events.nextCursor;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
console.error('Error polling events:', e);
|
|
53
|
+
}
|
|
54
|
+
}, this.pollIntervalMs);
|
|
55
|
+
}
|
|
56
|
+
stop() {
|
|
57
|
+
this.isPolling = false;
|
|
58
|
+
if (this.intervalId)
|
|
59
|
+
clearInterval(this.intervalId);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.EventMonitor = EventMonitor;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './client';
|
|
2
|
+
export * from './tx-builder';
|
|
3
|
+
export * from './coin-utils';
|
|
4
|
+
export * from './data-walker';
|
|
5
|
+
export * from './event-monitor';
|
|
6
|
+
export * from './names';
|
|
7
|
+
export * from './display';
|
|
8
|
+
export * from './kiosk';
|
|
9
|
+
export * from './utils';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./client"), exports);
|
|
18
|
+
__exportStar(require("./tx-builder"), exports);
|
|
19
|
+
__exportStar(require("./coin-utils"), exports);
|
|
20
|
+
__exportStar(require("./data-walker"), exports);
|
|
21
|
+
__exportStar(require("./event-monitor"), exports);
|
|
22
|
+
__exportStar(require("./names"), exports);
|
|
23
|
+
__exportStar(require("./display"), exports);
|
|
24
|
+
__exportStar(require("./kiosk"), exports);
|
|
25
|
+
__exportStar(require("./utils"), exports);
|
package/dist/kiosk.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions';
|
|
2
|
+
export declare class KioskUtils {
|
|
3
|
+
static KIOSK_MODULE: string;
|
|
4
|
+
static KIOSK_TYPE: string;
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new Kiosk and shares it.
|
|
7
|
+
* Returns the [kiosk, kioskOwnerCap] transaction arguments.
|
|
8
|
+
*/
|
|
9
|
+
static createKiosk(tx: Transaction): {
|
|
10
|
+
kiosk: {
|
|
11
|
+
$kind: "NestedResult";
|
|
12
|
+
NestedResult: [number, number];
|
|
13
|
+
};
|
|
14
|
+
kioskOwnerCap: {
|
|
15
|
+
$kind: "NestedResult";
|
|
16
|
+
NestedResult: [number, number];
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Places an item into the Kiosk.
|
|
21
|
+
*/
|
|
22
|
+
static place(tx: Transaction, kiosk: TransactionObjectArgument, kioskCap: TransactionObjectArgument, item: TransactionObjectArgument, itemType: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Withdraws an item from the Kiosk.
|
|
25
|
+
*/
|
|
26
|
+
static withdraw(tx: Transaction, kiosk: TransactionObjectArgument, kioskCap: TransactionObjectArgument, itemId: string | TransactionObjectArgument, itemType: string): import("@mysten/sui/transactions").TransactionResult;
|
|
27
|
+
}
|
package/dist/kiosk.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KioskUtils = void 0;
|
|
4
|
+
// Note: @mysten/kiosk is a separate package.
|
|
5
|
+
// For this SDK we will wrap the transaction building logic manually
|
|
6
|
+
// OR assume the user has installed @mysten/kiosk.
|
|
7
|
+
// Given strict instructions not to install new things unless needed,
|
|
8
|
+
// I will implement a lightweight wrapper using plain PTBs for common tasks.
|
|
9
|
+
// BUT calling 0x2::kiosk functions directly is safer and we don't depend on extra libs.
|
|
10
|
+
class KioskUtils {
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new Kiosk and shares it.
|
|
13
|
+
* Returns the [kiosk, kioskOwnerCap] transaction arguments.
|
|
14
|
+
*/
|
|
15
|
+
static createKiosk(tx) {
|
|
16
|
+
const [kiosk, cap] = tx.moveCall({
|
|
17
|
+
target: `${KioskUtils.KIOSK_MODULE}::new`,
|
|
18
|
+
});
|
|
19
|
+
tx.moveCall({
|
|
20
|
+
target: '0x2::transfer::public_share_object',
|
|
21
|
+
typeArguments: [KioskUtils.KIOSK_TYPE],
|
|
22
|
+
arguments: [kiosk],
|
|
23
|
+
});
|
|
24
|
+
// Cap usually transferred to sender, but we return it for flexibility
|
|
25
|
+
return { kiosk, kioskOwnerCap: cap };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Places an item into the Kiosk.
|
|
29
|
+
*/
|
|
30
|
+
static place(tx, kiosk, kioskCap, item, itemType) {
|
|
31
|
+
tx.moveCall({
|
|
32
|
+
target: `${KioskUtils.KIOSK_MODULE}::place`,
|
|
33
|
+
typeArguments: [itemType],
|
|
34
|
+
arguments: [kiosk, kioskCap, item]
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Withdraws an item from the Kiosk.
|
|
39
|
+
*/
|
|
40
|
+
static withdraw(tx, kiosk, kioskCap, itemId, itemType) {
|
|
41
|
+
// If itemId is string, wrap resolve mechanism or assume pure ID for MoveCall?
|
|
42
|
+
// Kiosk::take takes ID.
|
|
43
|
+
// It generally takes the Object ID as ID.
|
|
44
|
+
// Handling ID wrapper for `take`.
|
|
45
|
+
const idArg = typeof itemId === 'string' ? tx.pure.id(itemId) : itemId;
|
|
46
|
+
return tx.moveCall({
|
|
47
|
+
target: `${KioskUtils.KIOSK_MODULE}::take`,
|
|
48
|
+
typeArguments: [itemType],
|
|
49
|
+
arguments: [kiosk, kioskCap, idArg]
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.KioskUtils = KioskUtils;
|
|
54
|
+
// 0x2::kiosk module ID
|
|
55
|
+
KioskUtils.KIOSK_MODULE = '0x2::kiosk';
|
|
56
|
+
KioskUtils.KIOSK_TYPE = '0x2::kiosk::Kiosk';
|
package/dist/names.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SuiClient } from '@mysten/sui/client';
|
|
2
|
+
export declare class NameService {
|
|
3
|
+
private client;
|
|
4
|
+
constructor(client: SuiClient);
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a SuiNS name to an address (e.g., "demo.sui" -> "0x...").
|
|
7
|
+
*/
|
|
8
|
+
resolveName(name: string): Promise<string | null>;
|
|
9
|
+
/**
|
|
10
|
+
* Resolves an address to a generic name (Reverse Lookup).
|
|
11
|
+
*/
|
|
12
|
+
resolveAddress(address: string): Promise<string | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Fetches detailed Name Object data.
|
|
15
|
+
*/
|
|
16
|
+
getNameObject(name: string): Promise<string | null>;
|
|
17
|
+
}
|
package/dist/names.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NameService = void 0;
|
|
4
|
+
class NameService {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolves a SuiNS name to an address (e.g., "demo.sui" -> "0x...").
|
|
10
|
+
*/
|
|
11
|
+
async resolveName(name) {
|
|
12
|
+
try {
|
|
13
|
+
const address = await this.client.resolveNameServiceAddress({
|
|
14
|
+
name,
|
|
15
|
+
});
|
|
16
|
+
return address;
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
// Return null if not found or error
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolves an address to a generic name (Reverse Lookup).
|
|
25
|
+
*/
|
|
26
|
+
async resolveAddress(address) {
|
|
27
|
+
try {
|
|
28
|
+
const names = await this.client.resolveNameServiceNames({
|
|
29
|
+
address,
|
|
30
|
+
limit: 1,
|
|
31
|
+
});
|
|
32
|
+
if (names.data.length > 0) {
|
|
33
|
+
return names.data[0];
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Fetches detailed Name Object data.
|
|
43
|
+
*/
|
|
44
|
+
async getNameObject(name) {
|
|
45
|
+
// This often requires knowing the registry or specific object ID logic,
|
|
46
|
+
// but the RPC provides a helper now.
|
|
47
|
+
// For deeper metadata, we might need to fetch the NFT object itself if we have the ID.
|
|
48
|
+
// The standard client doesn't expose "getNameObject" directly easily without knowing the ID.
|
|
49
|
+
// We will stick to resolution for now.
|
|
50
|
+
return this.resolveName(name);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.NameService = NameService;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions';
|
|
2
|
+
export declare class AdvancedTxBuilder {
|
|
3
|
+
tx: Transaction;
|
|
4
|
+
constructor();
|
|
5
|
+
/**
|
|
6
|
+
* Wrapper for moveCall that simplifies argument passing.
|
|
7
|
+
*/
|
|
8
|
+
moveCall(target: string, args?: any[], typeArgs?: string[]): this;
|
|
9
|
+
private isTransactionArgument;
|
|
10
|
+
/**
|
|
11
|
+
* Transfers objects to a recipient.
|
|
12
|
+
* Auto-converts single object to array.
|
|
13
|
+
*/
|
|
14
|
+
transferObjects(objects: TransactionObjectArgument | TransactionObjectArgument[], recipient: string): this;
|
|
15
|
+
/**
|
|
16
|
+
* splits a coin into multiple amounts
|
|
17
|
+
*/
|
|
18
|
+
splitCoins(coin: TransactionObjectArgument, amounts: (number | bigint)[]): {
|
|
19
|
+
$kind: "Result";
|
|
20
|
+
Result: number;
|
|
21
|
+
} & {
|
|
22
|
+
$kind: "NestedResult";
|
|
23
|
+
NestedResult: [number, number];
|
|
24
|
+
}[];
|
|
25
|
+
/**
|
|
26
|
+
* Merges multiple coins into a destination coin
|
|
27
|
+
*/
|
|
28
|
+
mergeCoins(destination: TransactionObjectArgument, sources: TransactionObjectArgument[]): this;
|
|
29
|
+
/**
|
|
30
|
+
* Scans all inputs for potential SuiNS names (strings ending in .sui)
|
|
31
|
+
* and auto-resolves them to addresses using the provided client.
|
|
32
|
+
* This MUST be called before building if you used names as arguments.
|
|
33
|
+
*/
|
|
34
|
+
resolveNameArgs(client: import('@mysten/sui/client').SuiClient): Promise<this>;
|
|
35
|
+
/**
|
|
36
|
+
* Async helper to add a move call while automatically resolving any .sui arguments.
|
|
37
|
+
*/
|
|
38
|
+
moveCallWithResolving(client: import('@mysten/sui/client').SuiClient, target: string, args?: any[], typeArgs?: string[]): Promise<this>;
|
|
39
|
+
build(): Transaction;
|
|
40
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AdvancedTxBuilder = void 0;
|
|
4
|
+
const transactions_1 = require("@mysten/sui/transactions");
|
|
5
|
+
class AdvancedTxBuilder {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.tx = new transactions_1.Transaction();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Wrapper for moveCall that simplifies argument passing.
|
|
11
|
+
*/
|
|
12
|
+
moveCall(target, args = [], typeArgs = []) {
|
|
13
|
+
// Auto-wrap pure values if they aren't already TransactionArguments
|
|
14
|
+
const processedArgs = args.map(arg => {
|
|
15
|
+
if (this.isTransactionArgument(arg)) {
|
|
16
|
+
return arg;
|
|
17
|
+
}
|
|
18
|
+
// Smarter inference
|
|
19
|
+
if (typeof arg === 'boolean')
|
|
20
|
+
return this.tx.pure.bool(arg);
|
|
21
|
+
if (typeof arg === 'number')
|
|
22
|
+
return this.tx.pure.u64(arg);
|
|
23
|
+
if (typeof arg === 'bigint')
|
|
24
|
+
return this.tx.pure.u64(arg);
|
|
25
|
+
if (typeof arg === 'string' && arg.startsWith('0x')) {
|
|
26
|
+
// Assume 0x strings are Objects by default for ergonomics
|
|
27
|
+
// If user wants pure address, they should pass tx.pure.address(arg)
|
|
28
|
+
return this.tx.object(arg);
|
|
29
|
+
}
|
|
30
|
+
if (typeof arg === 'string')
|
|
31
|
+
return this.tx.pure.string(arg);
|
|
32
|
+
// Fallback for complex types or byte arrays, might fail if not serializable
|
|
33
|
+
return this.tx.pure(arg);
|
|
34
|
+
});
|
|
35
|
+
this.tx.moveCall({
|
|
36
|
+
target: target,
|
|
37
|
+
arguments: processedArgs, // Typescript might complain about mixed types, but sdk handles it at runtime
|
|
38
|
+
typeArguments: typeArgs,
|
|
39
|
+
}); // Type cast might be needed if processedArgs logic implies mixed types not perfectly matching Strict definitions without casting
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
isTransactionArgument(arg) {
|
|
43
|
+
// Duck-typing check for TransactionArgument
|
|
44
|
+
return arg && (arg.kind === 'Input' ||
|
|
45
|
+
arg.kind === 'GasCoin' ||
|
|
46
|
+
arg.kind === 'Result' ||
|
|
47
|
+
arg.kind === 'NestedResult');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Transfers objects to a recipient.
|
|
51
|
+
* Auto-converts single object to array.
|
|
52
|
+
*/
|
|
53
|
+
transferObjects(objects, recipient) {
|
|
54
|
+
const objArray = Array.isArray(objects) ? objects : [objects];
|
|
55
|
+
this.tx.transferObjects(objArray, recipient);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* splits a coin into multiple amounts
|
|
60
|
+
*/
|
|
61
|
+
splitCoins(coin, amounts) {
|
|
62
|
+
return this.tx.splitCoins(coin, amounts);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Merges multiple coins into a destination coin
|
|
66
|
+
*/
|
|
67
|
+
mergeCoins(destination, sources) {
|
|
68
|
+
this.tx.mergeCoins(destination, sources);
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Scans all inputs for potential SuiNS names (strings ending in .sui)
|
|
73
|
+
* and auto-resolves them to addresses using the provided client.
|
|
74
|
+
* This MUST be called before building if you used names as arguments.
|
|
75
|
+
*/
|
|
76
|
+
async resolveNameArgs(client) {
|
|
77
|
+
// This requires deep inspection of the transaction block inputs.
|
|
78
|
+
// The official SDK doesn't make it easy to mutate inputs after adding.
|
|
79
|
+
// A better approach for the "Fluent" API is to resolve AT THE TIME OF CALL if possible,
|
|
80
|
+
// but that breaks the synchronous intuitive nature.
|
|
81
|
+
// Alternative: We can't really mutate the `this.tx` inputs easily without
|
|
82
|
+
// accessing private fields or reconstructing.
|
|
83
|
+
// For this V1, we will trust the user to resolve names using NameService
|
|
84
|
+
// OR we provides an async helper `builder.addResolvedCall(client, ...)`
|
|
85
|
+
// Let's add the helper method instead of a full pass for now,
|
|
86
|
+
// as full pass requires traversing the complex Block structure.
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Async helper to add a move call while automatically resolving any .sui arguments.
|
|
91
|
+
*/
|
|
92
|
+
async moveCallWithResolving(client, target, args = [], typeArgs = []) {
|
|
93
|
+
const resolvedArgs = await Promise.all(args.map(async (arg) => {
|
|
94
|
+
if (typeof arg === 'string' && arg.endsWith('.sui')) {
|
|
95
|
+
const address = await client.resolveNameServiceAddress({ name: arg });
|
|
96
|
+
if (!address)
|
|
97
|
+
throw new Error(`Could not resolve name: ${arg}`);
|
|
98
|
+
return address; // Will be auto-wrapped as object or pure string by moveCall
|
|
99
|
+
}
|
|
100
|
+
return arg;
|
|
101
|
+
}));
|
|
102
|
+
return this.moveCall(target, resolvedArgs, typeArgs);
|
|
103
|
+
}
|
|
104
|
+
build() {
|
|
105
|
+
return this.tx;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.AdvancedTxBuilder = AdvancedTxBuilder;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare class DataUtils {
|
|
2
|
+
/**
|
|
3
|
+
* Converts a standard UTF-8 string to a vector of bytes (u8).
|
|
4
|
+
* Useful for Move functions that expect `vector<u8>` or `String`.
|
|
5
|
+
*/
|
|
6
|
+
static stringToVectorU8(str: string): Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* Converts a vector of bytes (u8) back to a UTF-8 string.
|
|
9
|
+
*/
|
|
10
|
+
static vectorU8ToString(bytes: Uint8Array | number[]): string;
|
|
11
|
+
/**
|
|
12
|
+
* Normalizes a SUI address (adds 0x prefix, pads to correct length).
|
|
13
|
+
*/
|
|
14
|
+
static normalizeAddress(address: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Checks if a string is a valid SUI address.
|
|
17
|
+
*/
|
|
18
|
+
static isValidAddress(address: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Converts bytes to Hex string.
|
|
21
|
+
*/
|
|
22
|
+
static toHex(bytes: Uint8Array): string;
|
|
23
|
+
/**
|
|
24
|
+
* Converts Hex string to bytes.
|
|
25
|
+
*/
|
|
26
|
+
static fromHex(hex: string): Uint8Array;
|
|
27
|
+
/**
|
|
28
|
+
* Converts bytes to Base64 (std logic).
|
|
29
|
+
*/
|
|
30
|
+
static toBase64(bytes: Uint8Array): string;
|
|
31
|
+
/**
|
|
32
|
+
* Converts Base64 to bytes.
|
|
33
|
+
*/
|
|
34
|
+
static fromBase64(b64: string): Uint8Array;
|
|
35
|
+
}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DataUtils = void 0;
|
|
4
|
+
const bcs_1 = require("@mysten/bcs");
|
|
5
|
+
const utils_1 = require("@mysten/sui/utils");
|
|
6
|
+
class DataUtils {
|
|
7
|
+
/**
|
|
8
|
+
* Converts a standard UTF-8 string to a vector of bytes (u8).
|
|
9
|
+
* Useful for Move functions that expect `vector<u8>` or `String`.
|
|
10
|
+
*/
|
|
11
|
+
static stringToVectorU8(str) {
|
|
12
|
+
return new TextEncoder().encode(str);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Converts a vector of bytes (u8) back to a UTF-8 string.
|
|
16
|
+
*/
|
|
17
|
+
static vectorU8ToString(bytes) {
|
|
18
|
+
const u8 = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
19
|
+
return new TextDecoder().decode(u8);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Normalizes a SUI address (adds 0x prefix, pads to correct length).
|
|
23
|
+
*/
|
|
24
|
+
static normalizeAddress(address) {
|
|
25
|
+
return (0, utils_1.normalizeSuiAddress)(address);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Checks if a string is a valid SUI address.
|
|
29
|
+
*/
|
|
30
|
+
static isValidAddress(address) {
|
|
31
|
+
return (0, utils_1.isValidSuiAddress)(address);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Converts bytes to Hex string.
|
|
35
|
+
*/
|
|
36
|
+
static toHex(bytes) {
|
|
37
|
+
return (0, bcs_1.toHEX)(bytes);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Converts Hex string to bytes.
|
|
41
|
+
*/
|
|
42
|
+
static fromHex(hex) {
|
|
43
|
+
return (0, bcs_1.fromHEX)(hex);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Converts bytes to Base64 (std logic).
|
|
47
|
+
*/
|
|
48
|
+
static toBase64(bytes) {
|
|
49
|
+
return (0, bcs_1.toB64)(bytes);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Converts Base64 to bytes.
|
|
53
|
+
*/
|
|
54
|
+
static fromBase64(b64) {
|
|
55
|
+
return (0, bcs_1.fromB64)(b64);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.DataUtils = DataUtils;
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sui-easy-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A developer-friendly SDK for SUI blockchain interactions",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "rm -rf dist && tsc",
|
|
14
|
+
"prepublishOnly": "npm run build",
|
|
15
|
+
"test": "vitest",
|
|
16
|
+
"lint": "eslint ."
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"sui",
|
|
20
|
+
"blockchain",
|
|
21
|
+
"sdk",
|
|
22
|
+
"typescript"
|
|
23
|
+
],
|
|
24
|
+
"author": "dotandev",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@mysten/sui": "^1.21.1",
|
|
28
|
+
"@mysten/bcs": "^1.1.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5.7.3",
|
|
32
|
+
"vitest": "^3.0.4",
|
|
33
|
+
"@types/node": "^22.10.7",
|
|
34
|
+
"ts-node": "^10.9.2"
|
|
35
|
+
}
|
|
36
|
+
}
|