@msafe/sui3-sdk 0.0.90 → 1.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@msafe/sui3-sdk",
3
- "version": "0.0.90",
3
+ "version": "1.0.4",
4
4
  "description": "SDK for MSafe SUI V3",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -17,7 +17,10 @@
17
17
  "src/**/*",
18
18
  "tsconfig.json"
19
19
  ],
20
- "repository": "git@github.com:Momentum-Safe/msafe-sui3-sdk.git",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/Momentum-Safe/msafe-sui3-sdk.git"
23
+ },
21
24
  "author": "MSafe <admin@m-safe.io>",
22
25
  "license": "MIT",
23
26
  "dependencies": {
@@ -26,12 +29,14 @@
26
29
  "buffer": "^6.0.3"
27
30
  },
28
31
  "devDependencies": {
32
+ "@changesets/changelog-github": "^0.7.0",
33
+ "@changesets/cli": "^2.31.0",
29
34
  "@msafe/mpay-sdk-sui": "^1.0.25",
30
35
  "@msafe/sui-app-store": "^0.0.272",
31
- "@msafe/sui3-utils": "^5.0.2",
32
- "@mysten/sui.js": "^0.54.1",
33
- "@mysten/wallet-standard": "^0.11.6",
34
- "@types/jest": "^29.5.8",
36
+ "@msafe/sui3-utils": "^5.0.7",
37
+ "@mysten/sui": "^2.16.2",
38
+ "@mysten/wallet-standard": "^0.20.3",
39
+ "@types/jest": "^30.0.0",
35
40
  "@types/node": "^20.9.2",
36
41
  "@typescript-eslint/eslint-plugin": "^6.5.0",
37
42
  "@typescript-eslint/parser": "^6.5.0",
@@ -46,31 +51,41 @@
46
51
  "eslint-plugin-prettier": "^5.0.0",
47
52
  "eslint-plugin-unused-imports": "^3.0.0",
48
53
  "exponential-backoff": "^3.1.1",
49
- "jest": "^29.7.0",
54
+ "jest": "^30.0.0",
50
55
  "prettier": "^3.0.3",
56
+ "semver": "7.6.3",
51
57
  "sqlite3": "^5.1.7",
52
- "ts-jest": "^29.1.1",
58
+ "ts-jest": "^29.4.0",
53
59
  "ts-node": "^10.9.1",
54
60
  "tsconfig-paths": "^4.2.0",
55
61
  "tsup": "^8.0.1",
56
62
  "typescript": "^5.2.2"
57
63
  },
58
64
  "scripts": {
59
- "test": "jest",
60
- "test:e2e": "jest test/unit/core/msafe-e2e.test.ts",
61
- "test:e2e:testnet": "jest test/unit/core/msafe-e2e-testnet.test.ts --maxWorkers=1",
62
- "unit": "TEST_ENV=UNIT jest",
65
+ "test": "NODE_OPTIONS='--experimental-vm-modules' jest --testPathIgnorePatterns=/test/unit/core/msafe-e2e\\\\.test\\\\.ts$ --testPathIgnorePatterns=/test/unit/core/msafe-e2e-testnet\\\\.test\\\\.ts$",
66
+ "test:e2e": "NODE_OPTIONS='--experimental-vm-modules' jest test/unit/core/msafe-e2e.test.ts",
67
+ "test:e2e:testnet": "NODE_OPTIONS='--experimental-vm-modules' jest test/unit/core/msafe-e2e-testnet.test.ts --maxWorkers=1",
68
+ "unit": "NODE_OPTIONS='--experimental-vm-modules' jest --testPathIgnorePatterns=/test/unit/core/msafe-e2e\\\\.test\\\\.ts$ --testPathIgnorePatterns=/test/unit/core/msafe-e2e-testnet\\\\.test\\\\.ts$",
63
69
  "clean": "rm -rf ./dist",
64
70
  "lint": "eslint . --ext .ts,.tsx",
65
- "build": "yarn clean && tsup --platform browser --format cjs,esm --dts",
66
- "build-yalc": "yarn build && yalc publish --private",
67
- "prerelease": "yarn build && chmod +x ./scripts/prerelease.sh && ./scripts/prerelease.sh"
71
+ "typecheck": "tsc --noEmit",
72
+ "build": "bun run clean && tsup --platform browser --format cjs,esm --dts",
73
+ "build-yalc": "bun run build && yalc publish --private",
74
+ "changeset": "changeset",
75
+ "version-packages": "changeset version",
76
+ "release": "bun run build && changeset publish"
68
77
  },
69
78
  "peerDependencies": {
70
79
  "@msafe/sui-app-store": ">0.0.100",
71
80
  "@msafe/sui3-utils": ">3.0.0",
72
- "@mysten/sui.js": "^0.54.1",
73
- "@mysten/wallet-standard": ">0.11.0"
81
+ "@mysten/sui": "^2.0.0",
82
+ "@mysten/wallet-standard": "^0.20.0"
83
+ },
84
+ "overrides": {
85
+ "semver": "7.6.3"
74
86
  },
75
- "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
87
+ "publishConfig": {
88
+ "access": "public",
89
+ "provenance": true
90
+ }
76
91
  }
@@ -37,7 +37,7 @@ import {
37
37
  UpdateAddressBookEntry,
38
38
  UserMSafeStatus,
39
39
  } from '@msafe/sui3-utils';
40
- import { PublicKey, SerializedSignature } from '@mysten/sui.js/cryptography';
40
+ import { PublicKey } from '@mysten/sui/cryptography';
41
41
  import axios, { AxiosError, AxiosRequestConfig, AxiosRequestTransformer, AxiosResponse } from 'axios';
42
42
 
43
43
  import { IBackend } from '@/backend/interface';
@@ -104,7 +104,7 @@ export class BackendImpl implements IBackend {
104
104
  headers: this.headers(),
105
105
  });
106
106
  return res.data?.map((publicKeyWithSchema) =>
107
- publicKeyWithSchema ? PublicKeySerde.de(publicKeyWithSchema) : undefined,
107
+ publicKeyWithSchema ? (PublicKeySerde.de(publicKeyWithSchema) as unknown as PublicKey) : undefined,
108
108
  );
109
109
  }
110
110
 
@@ -265,7 +265,7 @@ export class BackendImpl implements IBackend {
265
265
  return res.data;
266
266
  }
267
267
 
268
- async updateAddressBook(input: { updates: UpdateAddressBookEntry[]; signature: SerializedSignature }) {
268
+ async updateAddressBook(input: { updates: UpdateAddressBookEntry[]; signature: string }) {
269
269
  await this.post(`/address-book`, input, { headers: this.headers() });
270
270
  }
271
271
 
@@ -29,7 +29,7 @@ import {
29
29
  UpdateAddressBookEntry,
30
30
  UserMSafeStatus,
31
31
  } from '@msafe/sui3-utils';
32
- import { PublicKey, SerializedSignature } from '@mysten/sui.js/cryptography';
32
+ import { PublicKey } from '@mysten/sui/cryptography';
33
33
 
34
34
  import { NotificationInfo, NotificationSubscribeDto, NotificationUpdateDto } from './types/notification';
35
35
 
@@ -65,7 +65,7 @@ export interface IBackend {
65
65
  proposeIntentionAndBuildVote(input: IProposeIntentionAndBuildAndVoteRequest): Promise<void>;
66
66
 
67
67
  getAddressBookEntries(pagination?: IPageOptions): Promise<IGetAddressBookResult>;
68
- updateAddressBook(input: { updates: UpdateAddressBookEntry[]; signature: SerializedSignature }): Promise<void>;
68
+ updateAddressBook(input: { updates: UpdateAddressBookEntry[]; signature: string }): Promise<void>;
69
69
  getAddressBookMode(): Promise<IGetAddressBookModeResult>;
70
70
  setAddressBookMode(input: ISetAddressBookModeReq): Promise<void>;
71
71
 
@@ -6,7 +6,7 @@ import {
6
6
  PublicKeyWithSchema,
7
7
  SigningMessageHelper,
8
8
  } from '@msafe/sui3-utils';
9
- import { PublicKey, SerializedSignature } from '@mysten/sui.js/cryptography';
9
+ import { PublicKey } from '@mysten/sui/cryptography';
10
10
 
11
11
  import { PublicKeyHelper } from '@/core/PublicKeyHelper';
12
12
  import { MSafeGlobals } from '@/globals/MSafeGlobals';
@@ -47,7 +47,7 @@ export class CreateHelper {
47
47
  if (!isSameAddress(address, pk.toSuiAddress())) {
48
48
  throw new Error('Public key not match');
49
49
  }
50
- this.pkHelper.addPublicKey(address, pk);
50
+ this.pkHelper.addPublicKey(address, pk as unknown as PublicKey);
51
51
  }
52
52
 
53
53
  async submitMSafeCreation(creationInfo: CreateMSafeInfo) {
@@ -70,21 +70,21 @@ export class CreateHelper {
70
70
  return {
71
71
  threshold: info.threshold,
72
72
  ownersWithWeight: info.owners.map((owner, i) => ({
73
- publicKey: publicKeys[i] as PublicKey,
73
+ publicKey: publicKeys[i] as unknown as PublicKey,
74
74
  weight: owner.weight,
75
75
  })),
76
76
  creationNonce: info.creationNonce,
77
- };
77
+ } as unknown as MultiSigConfig;
78
78
  }
79
79
 
80
- private async submitToBackend(createInfo: CreateMSafeInfo, signature: SerializedSignature) {
80
+ private async submitToBackend(createInfo: CreateMSafeInfo, signature: string) {
81
81
  const pks = await this.pkHelper.getPublicKeyBatch(createInfo.owners.map((owner) => owner.address));
82
82
  await this.globals.backend.createMSafeAccount({
83
83
  owners: createInfo.owners.map((owner, i) => ({
84
84
  address: owner.address,
85
85
  weight: owner.weight,
86
86
  status: '',
87
- ...PublicKeySerde.ser(pks[i] as PublicKey),
87
+ ...PublicKeySerde.ser(pks[i] as never),
88
88
  })),
89
89
  threshold: createInfo.threshold,
90
90
  name: createInfo.name, // name validation is deferred to backend
@@ -15,10 +15,8 @@ import {
15
15
  buildRejectTxb,
16
16
  isSameAddress,
17
17
  } from '@msafe/sui3-utils';
18
- import { SuiObjectData, SuiObjectResponse } from '@mysten/sui.js/client';
19
- import { SerializedSignature } from '@mysten/sui.js/cryptography';
20
- import { TransactionBlock } from '@mysten/sui.js/transactions';
21
- import { fromHEX, normalizeStructTag, toHEX } from '@mysten/sui.js/utils';
18
+ import { Transaction } from '@mysten/sui/transactions';
19
+ import { fromHex, normalizeStructTag, toHex } from '@mysten/sui/utils';
22
20
  import { SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN } from '@mysten/wallet-standard';
23
21
 
24
22
  import { MSafeGlobals } from '@/globals/MSafeGlobals';
@@ -27,7 +25,8 @@ import { OwnedCoin } from '@/types/assets';
27
25
  import { Pagination, PendingTx, SimulationResult } from '@/types/msafe';
28
26
  import { CoinHelper } from '@/utils';
29
27
  import { HexToUint8Array } from '@/utils/buffer';
30
- import { BatchObjectOptions, getAllOwnedObjects } from '@/utils/iter/object';
28
+ import { BatchObjectOptions, getAllOwnedObjects, type OwnedObjectData } from '@/utils/iter/object';
29
+ import { toSuiTransaction } from '@/utils/transaction';
31
30
 
32
31
  export class MSafeAccount {
33
32
  public multiSig: MultiSigAccount;
@@ -58,26 +57,30 @@ export class MSafeAccount {
58
57
  }
59
58
 
60
59
  async ownedCoins(): Promise<OwnedCoin[]> {
61
- const balances = await this.suiClient.getAllBalances({ owner: this.address });
60
+ const balances: { coinType: string; addressBalance: string }[] = [];
61
+ let cursor: string | null = null;
62
+ let hasNext = true;
63
+ while (hasNext) {
64
+ const page = await this.suiClient.listBalances({ owner: this.address, cursor, limit: 50 });
65
+ balances.push(...page.balances);
66
+ hasNext = page.hasNextPage;
67
+ cursor = page.cursor;
68
+ }
62
69
  return Promise.all(
63
70
  balances.map(async (balance): Promise<OwnedCoin> => {
64
71
  const meta = await this.coinHelper.getCoinMeta(balance.coinType);
65
- const unlockedBalance = balance.lockedBalance.number
66
- ? BigInt(balance.totalBalance) - BigInt(balance.lockedBalance.number)
67
- : BigInt(balance.totalBalance);
68
-
69
72
  return {
70
73
  type: normalizeStructTag(balance.coinType),
71
- balance: BigInt(unlockedBalance),
74
+ balance: BigInt(balance.addressBalance),
72
75
  metadata: meta,
73
76
  };
74
77
  }),
75
78
  );
76
79
  }
77
80
 
78
- async ownedObjects(options?: BatchObjectOptions): Promise<SuiObjectData[]> {
81
+ async ownedObjects(options?: BatchObjectOptions): Promise<OwnedObjectData[]> {
79
82
  const filterCoinObjectOptions = {
80
- filter: (objRes: SuiObjectResponse) => !objRes?.data?.type?.startsWith('0x2::coin::Coin'),
83
+ filter: (obj: OwnedObjectData) => !obj?.type?.startsWith('0x2::coin::Coin'),
81
84
  ...options,
82
85
  };
83
86
  return getAllOwnedObjects(this.suiClient, this.address, filterCoinObjectOptions);
@@ -133,10 +136,10 @@ export class MSafeAccount {
133
136
 
134
137
  async simulateIntention(
135
138
  request: Omit<IProposeIntentionRequest, 'msafeAddress' | 'signature'> & {
136
- txb?: TransactionBlock;
139
+ txb?: Transaction;
137
140
  },
138
141
  ) {
139
- let txb: TransactionBlock;
142
+ let txb: Transaction;
140
143
  if (request.txb) {
141
144
  txb = request.txb;
142
145
  } else {
@@ -144,19 +147,21 @@ export class MSafeAccount {
144
147
  if (!appHelper) {
145
148
  throw new Error(`Can't find app helper for application ${request.application}`);
146
149
  }
147
- txb = await appHelper.build({
148
- network: this.globals.config.network,
149
- intentionData: request.intention,
150
- txType: request.txType,
151
- txSubType: request.txSubType,
152
- clientUrl: this.globals.config.suiClient.url,
153
- account: {
154
- address: this.address,
155
- publicKey: fromHEX(this.address),
156
- chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
157
- features: [],
158
- },
159
- });
150
+ txb = toSuiTransaction(
151
+ await appHelper.build({
152
+ network: this.globals.config.network,
153
+ intentionData: request.intention,
154
+ txType: request.txType,
155
+ txSubType: request.txSubType,
156
+ clientUrl: this.globals.config.suiClient.url,
157
+ account: {
158
+ address: this.address,
159
+ publicKey: fromHex(this.address),
160
+ chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
161
+ features: [],
162
+ },
163
+ }),
164
+ );
160
165
  }
161
166
  txb.setSender(this.address);
162
167
  return this.simulator.simulate({ txb, sender: this.address });
@@ -181,10 +186,10 @@ export class MSafeAccount {
181
186
 
182
187
  async proposeIntentionAndBuildVote(
183
188
  input: Omit<IProposeIntentionAndBuildAndVoteRequest, 'signature' | 'msafeAddress' | 'payload' | 'digest'> & {
184
- txb?: TransactionBlock;
189
+ txb?: Transaction;
185
190
  },
186
191
  ): Promise<void> {
187
- let txb: TransactionBlock;
192
+ let txb: Transaction;
188
193
  if (input.txb) {
189
194
  txb = input.txb;
190
195
  } else {
@@ -192,19 +197,21 @@ export class MSafeAccount {
192
197
  if (!appHelper) {
193
198
  throw new Error(`Can't find app helper for application ${input.application}`);
194
199
  }
195
- txb = await appHelper.build({
196
- network: this.globals.config.network,
197
- intentionData: input.intention,
198
- txType: input.txType,
199
- txSubType: input.txSubType,
200
- clientUrl: this.globals.config.suiClient.url,
201
- account: {
202
- address: this.address,
203
- publicKey: fromHEX(this.address),
204
- chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
205
- features: [],
206
- },
207
- });
200
+ txb = toSuiTransaction(
201
+ await appHelper.build({
202
+ network: this.globals.config.network,
203
+ intentionData: input.intention,
204
+ txType: input.txType,
205
+ txSubType: input.txSubType,
206
+ clientUrl: this.globals.config.suiClient.url,
207
+ account: {
208
+ address: this.address,
209
+ publicKey: fromHex(this.address),
210
+ chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
211
+ features: [],
212
+ },
213
+ }),
214
+ );
208
215
  }
209
216
  txb.setGasPrice(input.gasPrice);
210
217
  txb.setGasBudget(input.gasBudget);
@@ -216,7 +223,7 @@ export class MSafeAccount {
216
223
 
217
224
  return this.backend.proposeIntentionAndBuildVote({
218
225
  ...input,
219
- payload: toHEX(payload),
226
+ payload: toHex(payload),
220
227
  digest,
221
228
  msafeAddress: this.address,
222
229
  signature: signature.signature,
@@ -281,9 +288,10 @@ export class MSafeAccount {
281
288
  async proposePlainPayloadIntention(intention: PlainPayloadIntention) {
282
289
  const sn = await this.nextSequenceNumber();
283
290
 
284
- const tb = TransactionBlock.from(intention.payload);
291
+ const tb = Transaction.from(intention.payload);
285
292
 
286
- if (!tb.blockData.sender || !isSameAddress(tb.blockData.sender, this.address)) {
293
+ const data = tb.getData();
294
+ if (!data.sender || !isSameAddress(data.sender, this.address)) {
287
295
  throw new Error('Transaction sender is not same as the multisig address');
288
296
  }
289
297
 
@@ -314,7 +322,7 @@ export class MSafeAccount {
314
322
  throw new Error('Already rejected');
315
323
  }
316
324
 
317
- const txb = buildRejectTxb(this.address);
325
+ const txb = toSuiTransaction(buildRejectTxb(this.address));
318
326
  txb.setSender(this.address);
319
327
  return this.simulator.simulate({ txb, sender: this.address });
320
328
  }
@@ -325,7 +333,7 @@ export class MSafeAccount {
325
333
  throw new Error('Already rejected');
326
334
  }
327
335
 
328
- const rejectTxb = buildRejectTxb(this.address);
336
+ const rejectTxb = toSuiTransaction(buildRejectTxb(this.address));
329
337
  const digest = await rejectTxb.getDigest({ client: this.suiClient });
330
338
  const payload = await rejectTxb.build({ client: this.suiClient });
331
339
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
@@ -343,19 +351,21 @@ export class MSafeAccount {
343
351
  if (!appHelper) {
344
352
  throw new Error(`Can't find app helper for application ${intention.application}`);
345
353
  }
346
- const txb = await appHelper.build({
347
- network: this.globals.config.network,
348
- intentionData: intention.intention,
349
- txType: intention.txType as TransactionType,
350
- txSubType: intention.txSubType,
351
- clientUrl: this.globals.config.suiClient.url,
352
- account: {
353
- address: this.address,
354
- publicKey: fromHEX(this.address),
355
- chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
356
- features: [],
357
- },
358
- });
354
+ const txb = toSuiTransaction(
355
+ await appHelper.build({
356
+ network: this.globals.config.network,
357
+ intentionData: intention.intention,
358
+ txType: intention.txType as TransactionType,
359
+ txSubType: intention.txSubType,
360
+ clientUrl: this.globals.config.suiClient.url,
361
+ account: {
362
+ address: this.address,
363
+ publicKey: fromHex(this.address),
364
+ chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
365
+ features: [],
366
+ },
367
+ }),
368
+ );
359
369
  txb.setSender(this.address);
360
370
  return this.simulator.simulate({ txb, sender: this.address });
361
371
  }
@@ -385,7 +395,7 @@ export class MSafeAccount {
385
395
  throw new Error('Not enough signature');
386
396
  }
387
397
 
388
- const sortedSigs: SerializedSignature[] = [];
398
+ const sortedSigs: string[] = [];
389
399
  const gotSigs = new Map(votes.map((vote) => [vote.userAddress, vote.signature]));
390
400
  for (let i = 0; i < this.info.owners.length; i++) {
391
401
  const owner = this.info.owners[i];
@@ -395,10 +405,10 @@ export class MSafeAccount {
395
405
  }
396
406
  }
397
407
  const multiSignature = this.multiSig.combinePartialSignatures(sortedSigs);
398
- return this.suiClient.executeTransactionBlock({
399
- transactionBlock: HexToUint8Array(payload),
400
- signature: multiSignature,
401
- options: { showEffects: true, showEvents: true },
408
+ return this.suiClient.executeTransaction({
409
+ transaction: HexToUint8Array(payload),
410
+ signatures: [multiSignature],
411
+ include: { effects: true, events: true },
402
412
  });
403
413
  }
404
414
 
@@ -1,6 +1,6 @@
1
1
  import { isSameAddress } from '@msafe/sui3-utils';
2
- import { PublicKey } from '@mysten/sui.js/cryptography';
3
- import { normalizeSuiAddress } from '@mysten/sui.js/utils';
2
+ import { PublicKey } from '@mysten/sui/cryptography';
3
+ import { normalizeSuiAddress } from '@mysten/sui/utils';
4
4
 
5
5
  import { MSafeGlobals } from '@/globals/MSafeGlobals';
6
6
  import { getPublicKeyFromChain } from '@/utils/sui';
@@ -1,20 +1,27 @@
1
- import { SuiClient } from '@mysten/sui.js/client';
1
+ import { SuiGrpcClient } from '@mysten/sui/grpc';
2
2
 
3
3
  import { BackendImpl } from '@/backend/BackendImpl';
4
4
  import { IBackend } from '@/backend/interface';
5
- import { getMSafeConfig, MSafeConfig, MSafeConfigOptions, MSafeEnv } from '@/globals/const';
5
+ import {
6
+ getMSafeConfig,
7
+ httpUrlToGrpcBaseUrl,
8
+ MSafeConfig,
9
+ MSafeConfigOptions,
10
+ MSafeEnv,
11
+ msafeChainToSuiNetwork,
12
+ } from '@/globals/const';
6
13
  import { IWallet } from '@/types/wallet';
7
14
 
8
15
  export class MSafeGlobals {
9
16
  public readonly backend: IBackend;
10
17
 
11
- public readonly suiClient: SuiClient;
18
+ public readonly suiClient: SuiGrpcClient;
12
19
 
13
20
  public readonly config: MSafeConfig;
14
21
 
15
22
  _wallet: IWallet | undefined;
16
23
 
17
- constructor(input: { backend: IBackend; suiClient: SuiClient; config: MSafeConfig }) {
24
+ constructor(input: { backend: IBackend; suiClient: SuiGrpcClient; config: MSafeConfig }) {
18
25
  this.backend = input.backend;
19
26
  this.suiClient = input.suiClient;
20
27
  this.config = input.config;
@@ -22,9 +29,15 @@ export class MSafeGlobals {
22
29
 
23
30
  static async New(env: MSafeEnv, options?: MSafeConfigOptions) {
24
31
  const config = getMSafeConfig(env, options);
25
- const suiClient = new SuiClient(
26
- config.suiClient.transport ? { transport: config.suiClient.transport } : { url: config.suiClient.url },
27
- );
32
+ const suiClient = config.suiClient.transport
33
+ ? new SuiGrpcClient({
34
+ transport: config.suiClient.transport,
35
+ network: msafeChainToSuiNetwork(config.network),
36
+ })
37
+ : new SuiGrpcClient({
38
+ baseUrl: httpUrlToGrpcBaseUrl(config.suiClient.url),
39
+ network: msafeChainToSuiNetwork(config.network),
40
+ });
28
41
  const backend = new BackendImpl(config.backend.url, options?.mockAddress);
29
42
  return new MSafeGlobals({
30
43
  backend,
@@ -1,4 +1,8 @@
1
- import type { SuiClientOptions } from '@mysten/sui.js/client';
1
+ import type { RpcTransport } from '@mysten/sui/grpc';
2
+
3
+ export function msafeChainToSuiNetwork(chain: 'sui:mainnet' | 'sui:testnet'): 'mainnet' | 'testnet' {
4
+ return chain === 'sui:mainnet' ? 'mainnet' : 'testnet';
5
+ }
2
6
 
3
7
  export enum MSafeEnv {
4
8
  local = 'local',
@@ -8,10 +12,22 @@ export enum MSafeEnv {
8
12
  prod = 'prod',
9
13
  }
10
14
 
15
+ /** Normalize HTTP(S) fullnode URL for SuiGrpcClient (gRPC-web) baseUrl. */
16
+ export function httpUrlToGrpcBaseUrl(url: string): string {
17
+ const parsed = new URL(url);
18
+ if (parsed.protocol === 'https:' && parsed.port === '') {
19
+ return `https://${parsed.hostname}:443${parsed.pathname}${parsed.search}`;
20
+ }
21
+ if (parsed.protocol === 'http:' && parsed.port === '') {
22
+ return `http://${parsed.hostname}:80${parsed.pathname}${parsed.search}`;
23
+ }
24
+ return `${parsed.origin}${parsed.pathname}${parsed.search}`;
25
+ }
26
+
11
27
  export interface MSafeConfig {
12
28
  suiClient: {
13
29
  url: string;
14
- transport?: SuiClientOptions['transport'];
30
+ transport?: RpcTransport;
15
31
  };
16
32
  backend: {
17
33
  url: string;
@@ -23,7 +39,7 @@ export interface MSafeConfig {
23
39
  export interface MSafeConfigOptions {
24
40
  suiClient?: {
25
41
  url: string;
26
- transport?: SuiClientOptions['transport'];
42
+ transport?: RpcTransport;
27
43
  };
28
44
  backend?: {
29
45
  url?: string;
@@ -52,9 +68,9 @@ export const ENV_CONFIGS = new Map<MSafeEnv, MSafeConfig>([
52
68
  url: TESTNET_RPC_URL,
53
69
  },
54
70
  backend: {
55
- url: LOCAL_API_URL,
71
+ url: DEV_API_URL,
56
72
  },
57
- syncingURL: LOCAL_SYNCING_URL,
73
+ syncingURL: DEV_SYNCING_URL,
58
74
  network: 'sui:testnet',
59
75
  },
60
76
  ],