@msafe/sui3-sdk 0.0.2-pre-9f39791.0 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1812 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +454 -0
- package/dist/index.d.ts +454 -0
- package/dist/index.js +1750 -0
- package/dist/index.js.map +1 -0
- package/package.json +6 -1
- package/.eslintignore +0 -3
- package/.eslintrc +0 -87
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/jsLinters/eslint.xml +0 -6
- package/.idea/misc.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/msafe-sui3-sdk.iml +0 -9
- package/.idea/vcs.xml +0 -6
- package/.prettierrc +0 -22
- package/jest.config.ts +0 -63
- package/scripts/prerelease.sh +0 -5
- package/temp_package.json +0 -54
- package/test/lib/TestHelper.ts +0 -94
- package/test/lib/account.ts +0 -87
- package/test/lib/config.ts +0 -9
- package/test/lib/faucet.ts +0 -49
- package/test/unit/backend/backend.test.ts +0 -367
- package/test/unit/core/CreateHelper.test.ts +0 -121
- package/test/unit/core/MessageHelper.test.ts +0 -32
- package/test/unit/core/PublicKeyHelper.test.ts +0 -80
- package/test/unit/core/msafe.test.ts +0 -298
- package/test/unit/utils/buffer.test.ts +0 -17
- package/test/unit/utils/crypto.test.ts +0 -32
- package/test/unit/utils/multi-sig.test.ts +0 -47
- package/test/unit/utils/sui.test.ts +0 -25
- package/tsup.config.ts +0 -9
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1812 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
AUTH_SIGN_MESSAGE: () => AUTH_SIGN_MESSAGE,
|
|
24
|
+
COIN_TYPE_ARG_REGEX: () => COIN_TYPE_ARG_REGEX,
|
|
25
|
+
Coin: () => Coin,
|
|
26
|
+
CoinHelper: () => CoinHelper,
|
|
27
|
+
CreateHelper: () => CreateHelper,
|
|
28
|
+
DEV_DATABASE_CONFIG: () => DEV_DATABASE_CONFIG,
|
|
29
|
+
ENV_CONFIGS: () => ENV_CONFIGS,
|
|
30
|
+
Formatter: () => Formatter,
|
|
31
|
+
HexToUint8Array: () => HexToUint8Array,
|
|
32
|
+
IntentionHelper: () => IntentionHelper,
|
|
33
|
+
LOCAL_DATABASE_CONFIG: () => LOCAL_DATABASE_CONFIG,
|
|
34
|
+
MAINNET_RPC_URL: () => MAINNET_RPC_URL,
|
|
35
|
+
MAX_OWNER_WITHOUT_NONCE: () => MAX_OWNER_WITHOUT_NONCE,
|
|
36
|
+
MAX_OWNER_WITH_NONCE: () => MAX_OWNER_WITH_NONCE,
|
|
37
|
+
MAX_WEIGHT: () => MAX_WEIGHT,
|
|
38
|
+
MIN_THRESHOLD: () => MIN_THRESHOLD,
|
|
39
|
+
MIN_WEIGHT: () => MIN_WEIGHT,
|
|
40
|
+
MSafeAccount: () => MSafeAccount,
|
|
41
|
+
MSafeClient: () => MSafeClient,
|
|
42
|
+
MSafeEnv: () => MSafeEnv,
|
|
43
|
+
MSafeGlobals: () => MSafeGlobals,
|
|
44
|
+
MessageHelper: () => MessageHelper,
|
|
45
|
+
NONCE_PK_PREFIX: () => NONCE_PK_PREFIX,
|
|
46
|
+
NONCE_PK_WEIGHT: () => NONCE_PK_WEIGHT,
|
|
47
|
+
NONCE_PREFIX_MAX_SIZE: () => NONCE_PREFIX_MAX_SIZE,
|
|
48
|
+
PublicKeySerde: () => PublicKeySerde,
|
|
49
|
+
RawMultiSig: () => RawMultiSig,
|
|
50
|
+
SUI_COIN: () => SUI_COIN,
|
|
51
|
+
SignatureVerifier: () => SignatureVerifier,
|
|
52
|
+
TESTNET_RPC_URL: () => TESTNET_RPC_URL,
|
|
53
|
+
Uint8ArrayToHex: () => Uint8ArrayToHex,
|
|
54
|
+
getAllCoins: () => getAllCoins,
|
|
55
|
+
getMSafeConfig: () => getMSafeConfig,
|
|
56
|
+
getMultiSigPublicKey: () => getMultiSigPublicKey,
|
|
57
|
+
getPublicKeyFromChain: () => getPublicKeyFromChain,
|
|
58
|
+
makeNoncePublicKey: () => makeNoncePublicKey,
|
|
59
|
+
stringToBuffer: () => stringToBuffer,
|
|
60
|
+
validateMultiSigConfig: () => validateMultiSigConfig
|
|
61
|
+
});
|
|
62
|
+
module.exports = __toCommonJS(src_exports);
|
|
63
|
+
|
|
64
|
+
// src/transactions/coin-transfer.ts
|
|
65
|
+
var import_transactions = require("@mysten/sui.js/transactions");
|
|
66
|
+
|
|
67
|
+
// src/utils/format.ts
|
|
68
|
+
var import_utils2 = require("@mysten/sui.js/utils");
|
|
69
|
+
|
|
70
|
+
// src/utils/coin.ts
|
|
71
|
+
var import_utils = require("@mysten/sui.js/utils");
|
|
72
|
+
var CoinHelper = class {
|
|
73
|
+
_client;
|
|
74
|
+
_coinMetaReg;
|
|
75
|
+
constructor(client) {
|
|
76
|
+
this._client = client;
|
|
77
|
+
this._coinMetaReg = /* @__PURE__ */ new Map();
|
|
78
|
+
}
|
|
79
|
+
async getCoinMeta(coinType) {
|
|
80
|
+
const normalized = (0, import_utils.normalizeStructTag)(coinType);
|
|
81
|
+
if (this._coinMetaReg.has(normalized)) {
|
|
82
|
+
return this._coinMetaReg.get(normalized);
|
|
83
|
+
}
|
|
84
|
+
const meta = await this.queryCoinMeta(normalized);
|
|
85
|
+
if (meta) {
|
|
86
|
+
this._coinMetaReg.set(normalized, meta);
|
|
87
|
+
}
|
|
88
|
+
return meta;
|
|
89
|
+
}
|
|
90
|
+
async queryCoinMeta(coinType) {
|
|
91
|
+
const res = await this._client.getCoinMetadata({ coinType });
|
|
92
|
+
return res || void 0;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var COIN_TYPE_ARG_REGEX = /^0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<(.+)>$/;
|
|
96
|
+
var Coin = class _Coin {
|
|
97
|
+
static isCoin(type) {
|
|
98
|
+
if (!type) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return (0, import_utils.normalizeStructTag)(type).match(COIN_TYPE_ARG_REGEX) != null;
|
|
102
|
+
}
|
|
103
|
+
static getCoinType(type) {
|
|
104
|
+
const [, res] = (0, import_utils.normalizeStructTag)(type).match(COIN_TYPE_ARG_REGEX) ?? [];
|
|
105
|
+
return res || null;
|
|
106
|
+
}
|
|
107
|
+
static getBalance(data) {
|
|
108
|
+
if (!_Coin.isCoin(data.type)) {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
if (data.content?.dataType !== "moveObject") {
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
const { balance } = data.content?.fields;
|
|
115
|
+
if (balance === void 0) {
|
|
116
|
+
return void 0;
|
|
117
|
+
}
|
|
118
|
+
return BigInt(balance);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// src/utils/format.ts
|
|
123
|
+
var Formatter = class {
|
|
124
|
+
static normalizeSuiAddress(addr) {
|
|
125
|
+
return (0, import_utils2.normalizeSuiAddress)(addr);
|
|
126
|
+
}
|
|
127
|
+
static normalizeStructTag(struct) {
|
|
128
|
+
return (0, import_utils2.normalizeStructTag)(struct);
|
|
129
|
+
}
|
|
130
|
+
static isSuiAddressEqual(addr1, addr2) {
|
|
131
|
+
return (0, import_utils2.normalizeSuiAddress)(addr1) === (0, import_utils2.normalizeSuiAddress)(addr2);
|
|
132
|
+
}
|
|
133
|
+
static isSuiStructEqual(struct1, struct2) {
|
|
134
|
+
return (0, import_utils2.normalizeStructTag)(struct1) === (0, import_utils2.normalizeStructTag)(struct2);
|
|
135
|
+
}
|
|
136
|
+
static isCoinObjectType(struct) {
|
|
137
|
+
return Coin.isCoin(struct);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/utils/sui.ts
|
|
142
|
+
var import_cryptography2 = require("@mysten/sui.js/cryptography");
|
|
143
|
+
var import_multisig = require("@mysten/sui.js/multisig");
|
|
144
|
+
|
|
145
|
+
// src/utils/crypto.ts
|
|
146
|
+
var import_cryptography = require("@mysten/sui.js/cryptography");
|
|
147
|
+
var import_ed25519 = require("@mysten/sui.js/keypairs/ed25519");
|
|
148
|
+
var import_secp256k1 = require("@mysten/sui.js/keypairs/secp256k1");
|
|
149
|
+
var import_secp256r1 = require("@mysten/sui.js/keypairs/secp256r1");
|
|
150
|
+
var import_verify = require("@mysten/sui.js/verify");
|
|
151
|
+
|
|
152
|
+
// src/utils/buffer.ts
|
|
153
|
+
function stringToBuffer(s) {
|
|
154
|
+
return Buffer.from(s, "utf-8");
|
|
155
|
+
}
|
|
156
|
+
function Uint8ArrayToHex(b) {
|
|
157
|
+
return `0x${Array.prototype.map.call(b, (x) => `0${x.toString(16)}`.slice(-2)).join("")}`;
|
|
158
|
+
}
|
|
159
|
+
function HexToUint8Array(hex) {
|
|
160
|
+
return Uint8Array.from(Buffer.from(hex.startsWith("0x") ? hex.slice(2) : hex, "hex"));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/utils/crypto.ts
|
|
164
|
+
var SignatureVerifier = class _SignatureVerifier {
|
|
165
|
+
static async getPublicKeyFromSignature(input) {
|
|
166
|
+
if (input.messageType === "TransactionBlock") {
|
|
167
|
+
return (0, import_verify.verifyTransactionBlock)(input.message, input.signature);
|
|
168
|
+
}
|
|
169
|
+
return (0, import_verify.verifyPersonalMessage)(input.message, input.signature);
|
|
170
|
+
}
|
|
171
|
+
static async getPublicKeyFromPersonalSignature(input) {
|
|
172
|
+
const message = stringToBuffer(input.messageStr);
|
|
173
|
+
return this.getPublicKeyFromSignature({
|
|
174
|
+
message,
|
|
175
|
+
messageType: "Personal",
|
|
176
|
+
signature: input.signature
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
static async verifySignature(input) {
|
|
180
|
+
const publicKey = await _SignatureVerifier.getPublicKeyFromSignature(input);
|
|
181
|
+
return Formatter.isSuiAddressEqual(publicKey.toSuiAddress(), input.targetAddress);
|
|
182
|
+
}
|
|
183
|
+
static async verifyPersonalSignature(input) {
|
|
184
|
+
const message = stringToBuffer(input.messageStr);
|
|
185
|
+
return this.verifySignature({
|
|
186
|
+
message,
|
|
187
|
+
messageType: "Personal",
|
|
188
|
+
signature: input.signature,
|
|
189
|
+
targetAddress: input.targetAddress
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
static async verifyTransactionSignature(input) {
|
|
193
|
+
return this.verifySignature({
|
|
194
|
+
messageType: "TransactionBlock",
|
|
195
|
+
message: input.payload,
|
|
196
|
+
signature: input.signature,
|
|
197
|
+
targetAddress: input.targetAddress
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
var PublicKeySerde = class {
|
|
202
|
+
static ser(publicKey) {
|
|
203
|
+
return {
|
|
204
|
+
publicKey: publicKey.toBase64(),
|
|
205
|
+
scheme: import_cryptography.SIGNATURE_FLAG_TO_SCHEME[publicKey.flag()]
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
static de(input) {
|
|
209
|
+
switch (input.scheme) {
|
|
210
|
+
case "ED25519":
|
|
211
|
+
return new import_ed25519.Ed25519PublicKey(input.publicKey);
|
|
212
|
+
case "Secp256k1":
|
|
213
|
+
return new import_secp256k1.Secp256k1PublicKey(input.publicKey);
|
|
214
|
+
case "Secp256r1":
|
|
215
|
+
return new import_secp256r1.Secp256r1PublicKey(input.publicKey);
|
|
216
|
+
default:
|
|
217
|
+
throw new Error("Unsupported signature scheme: $input.scheme");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/utils/sui.ts
|
|
223
|
+
var SUI_COIN = "0x2::sui::SUI";
|
|
224
|
+
async function getPublicKeyFromChain(suiClient, address) {
|
|
225
|
+
let txs;
|
|
226
|
+
try {
|
|
227
|
+
txs = await suiClient.queryTransactionBlocks({
|
|
228
|
+
// Disable naming rule since the variable is defined by Mysten
|
|
229
|
+
filter: { FromAddress: address },
|
|
230
|
+
options: { showInput: true },
|
|
231
|
+
limit: 2
|
|
232
|
+
});
|
|
233
|
+
} catch (e) {
|
|
234
|
+
return void 0;
|
|
235
|
+
}
|
|
236
|
+
if (txs.data.length === 0 || !txs.data[0].transaction?.txSignatures) {
|
|
237
|
+
return void 0;
|
|
238
|
+
}
|
|
239
|
+
const tx = txs.data[0];
|
|
240
|
+
const signatures = tx.transaction?.txSignatures;
|
|
241
|
+
for (let i = 0; i !== signatures.length; i++) {
|
|
242
|
+
const serializedSig = signatures[i];
|
|
243
|
+
const pk = getAddressFromSignatures(serializedSig, address);
|
|
244
|
+
if (!pk) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
return pk;
|
|
248
|
+
}
|
|
249
|
+
return void 0;
|
|
250
|
+
}
|
|
251
|
+
function getAddressFromSignatures(serializedSig, targetAddress) {
|
|
252
|
+
const decoded = (0, import_cryptography2.parseSerializedSignature)(serializedSig);
|
|
253
|
+
switch (decoded.signatureScheme) {
|
|
254
|
+
case "MultiSig": {
|
|
255
|
+
const multiSigAddress = new import_multisig.MultiSigPublicKey(decoded.multisig.multisig_pk).toSuiAddress();
|
|
256
|
+
if (Formatter.isSuiAddressEqual(multiSigAddress, targetAddress)) {
|
|
257
|
+
throw new Error("multi-sig wallet cannot be owner");
|
|
258
|
+
}
|
|
259
|
+
return void 0;
|
|
260
|
+
}
|
|
261
|
+
case "ZkLogin": {
|
|
262
|
+
if (Formatter.isSuiAddressEqual(decoded.zkLogin.address, targetAddress)) {
|
|
263
|
+
throw new Error("ZkLogin wallet cannot be owner");
|
|
264
|
+
}
|
|
265
|
+
return void 0;
|
|
266
|
+
}
|
|
267
|
+
case "ED25519":
|
|
268
|
+
case "Secp256k1":
|
|
269
|
+
case "Secp256r1": {
|
|
270
|
+
const pk = PublicKeySerde.de({ publicKey: decoded.publicKey, scheme: decoded.signatureScheme });
|
|
271
|
+
if (Formatter.isSuiAddressEqual(pk.toSuiAddress(), targetAddress)) {
|
|
272
|
+
return pk;
|
|
273
|
+
}
|
|
274
|
+
return void 0;
|
|
275
|
+
}
|
|
276
|
+
default:
|
|
277
|
+
throw new Error("Unknown schema");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function getAllCoins(input) {
|
|
281
|
+
let hasNext = true;
|
|
282
|
+
let cursor;
|
|
283
|
+
const res = [];
|
|
284
|
+
while (hasNext) {
|
|
285
|
+
const currentPage = await input.suiClient.getCoins({
|
|
286
|
+
owner: input.owner,
|
|
287
|
+
coinType: input.coinType,
|
|
288
|
+
cursor
|
|
289
|
+
});
|
|
290
|
+
res.push(...currentPage.data);
|
|
291
|
+
hasNext = currentPage.hasNextPage;
|
|
292
|
+
cursor = currentPage.nextCursor;
|
|
293
|
+
}
|
|
294
|
+
return res;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/transactions/coin-transfer.ts
|
|
298
|
+
async function buildCoinTransferTxb(input) {
|
|
299
|
+
if (Formatter.isSuiStructEqual(input.intention.coinType, SUI_COIN)) {
|
|
300
|
+
return buildSuiCoinTransferTxb(input);
|
|
301
|
+
}
|
|
302
|
+
return buildOtherCoinTransferTxb(input);
|
|
303
|
+
}
|
|
304
|
+
function buildSuiCoinTransferTxb(input) {
|
|
305
|
+
const txb = new import_transactions.TransactionBlock();
|
|
306
|
+
const [coin] = txb.splitCoins(txb.gas, [txb.pure(input.intention.amount)]);
|
|
307
|
+
txb.transferObjects([coin], txb.pure(input.intention.recipient));
|
|
308
|
+
txb.setSender(input.sender);
|
|
309
|
+
return txb;
|
|
310
|
+
}
|
|
311
|
+
async function buildOtherCoinTransferTxb(input) {
|
|
312
|
+
const { suiClient, sender, intention } = input;
|
|
313
|
+
const objs = await getAllCoins({
|
|
314
|
+
suiClient,
|
|
315
|
+
owner: sender,
|
|
316
|
+
coinType: intention.coinType
|
|
317
|
+
});
|
|
318
|
+
if (objs.length === 0) {
|
|
319
|
+
throw new Error("No valid coin found to send");
|
|
320
|
+
}
|
|
321
|
+
const totalBal = objs.reduce((sum, coin2) => sum + BigInt(coin2.balance), 0n);
|
|
322
|
+
if (totalBal < BigInt(intention.amount)) {
|
|
323
|
+
throw new Error("Not enough balance");
|
|
324
|
+
}
|
|
325
|
+
const txb = new import_transactions.TransactionBlock();
|
|
326
|
+
const primary = txb.object(objs[0].coinObjectId);
|
|
327
|
+
if (objs.length > 1) {
|
|
328
|
+
txb.mergeCoins(
|
|
329
|
+
primary,
|
|
330
|
+
objs.slice(1).map((obj) => txb.object(obj.coinObjectId))
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
const [coin] = txb.splitCoins(primary, [txb.pure(intention.amount)]);
|
|
334
|
+
txb.transferObjects([coin], txb.pure(intention.recipient));
|
|
335
|
+
txb.setSender(input.sender);
|
|
336
|
+
return txb;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// src/transactions/object-transfer.ts
|
|
340
|
+
var import_transactions2 = require("@mysten/sui.js/transactions");
|
|
341
|
+
async function buildObjectTransferTxb(input) {
|
|
342
|
+
await validateObjectTransfer(input);
|
|
343
|
+
const txb = new import_transactions2.TransactionBlock();
|
|
344
|
+
txb.transferObjects([txb.object(input.intention.objectId)], txb.pure(input.intention.receiver));
|
|
345
|
+
txb.setSender(input.sender);
|
|
346
|
+
return txb;
|
|
347
|
+
}
|
|
348
|
+
async function validateObjectTransfer(input) {
|
|
349
|
+
const { suiClient, sender, intention } = input;
|
|
350
|
+
const obj = await suiClient.getObject({
|
|
351
|
+
id: intention.objectId
|
|
352
|
+
});
|
|
353
|
+
if (obj.data === void 0) {
|
|
354
|
+
throw new Error("Object not found");
|
|
355
|
+
}
|
|
356
|
+
if (!obj.data?.type) {
|
|
357
|
+
throw new Error("Object type is null");
|
|
358
|
+
}
|
|
359
|
+
if (!Formatter.isSuiStructEqual(obj.data.type, intention.objectType)) {
|
|
360
|
+
throw new Error("Object type not expected");
|
|
361
|
+
}
|
|
362
|
+
if (Formatter.isCoinObjectType(obj.data.type)) {
|
|
363
|
+
throw new Error("Can not transfer coin object in Object Transfer transactions");
|
|
364
|
+
}
|
|
365
|
+
const addressOwner = getAddressOwner(obj);
|
|
366
|
+
if (!Formatter.isSuiAddressEqual(addressOwner, sender)) {
|
|
367
|
+
throw new Error("Object owner not match");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function getAddressOwner(object) {
|
|
371
|
+
const owner = object.data?.owner;
|
|
372
|
+
if (!owner) {
|
|
373
|
+
throw new Error("Object Owner not found");
|
|
374
|
+
}
|
|
375
|
+
if (typeof owner !== "object" || !("AddressOwner" in owner)) {
|
|
376
|
+
throw new Error("Invalid object owner");
|
|
377
|
+
}
|
|
378
|
+
return owner.AddressOwner;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/transactions/reject.ts
|
|
382
|
+
var import_transactions3 = require("@mysten/sui.js/transactions");
|
|
383
|
+
async function buildRejectTxb(input) {
|
|
384
|
+
const approveTxb = import_transactions3.TransactionBlock.from(HexToUint8Array(input.payloadToReject));
|
|
385
|
+
const gasPayment = approveTxb.blockData.gasConfig.payment;
|
|
386
|
+
if (!gasPayment) {
|
|
387
|
+
throw new Error("No gas payment found for approve payload");
|
|
388
|
+
}
|
|
389
|
+
const txb = new import_transactions3.TransactionBlock();
|
|
390
|
+
txb.setGasPayment(gasPayment);
|
|
391
|
+
txb.setSender(input.sender);
|
|
392
|
+
return txb;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/transactions/intention.ts
|
|
396
|
+
var IntentionHelper = class {
|
|
397
|
+
static ser(intention) {
|
|
398
|
+
return JSON.stringify(intention);
|
|
399
|
+
}
|
|
400
|
+
static de(val) {
|
|
401
|
+
const intention = JSON.parse(val);
|
|
402
|
+
if (typeof intention !== "object" || !("txType" in intention)) {
|
|
403
|
+
throw new Error(`Failed to deserialize intention: ${val}`);
|
|
404
|
+
}
|
|
405
|
+
return JSON.parse(val);
|
|
406
|
+
}
|
|
407
|
+
// TODO: Add gas option here.
|
|
408
|
+
static buildTxb(input) {
|
|
409
|
+
switch (input.intention.txType) {
|
|
410
|
+
case "CoinTransfer":
|
|
411
|
+
return buildCoinTransferTxb({
|
|
412
|
+
suiClient: input.suiClient,
|
|
413
|
+
sender: input.sender,
|
|
414
|
+
intention: input.intention
|
|
415
|
+
});
|
|
416
|
+
case "ObjectTransfer":
|
|
417
|
+
return buildObjectTransferTxb({
|
|
418
|
+
suiClient: input.suiClient,
|
|
419
|
+
sender: input.sender,
|
|
420
|
+
intention: input.intention
|
|
421
|
+
});
|
|
422
|
+
default:
|
|
423
|
+
throw new Error(`Unknown tx type: ${input.intention}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
static getTxType(intention) {
|
|
427
|
+
switch (intention.txType) {
|
|
428
|
+
case "CoinTransfer":
|
|
429
|
+
return {
|
|
430
|
+
txType: "CoinTransfer",
|
|
431
|
+
txSubType: "CoinTransfer"
|
|
432
|
+
};
|
|
433
|
+
case "ObjectTransfer":
|
|
434
|
+
return {
|
|
435
|
+
txType: "ObjectTransfer",
|
|
436
|
+
txSubType: "ObjectTransfer"
|
|
437
|
+
};
|
|
438
|
+
default:
|
|
439
|
+
throw new Error("Unknown intention type");
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
static buildRejectTransaction(input) {
|
|
443
|
+
return buildRejectTxb({ sender: input.msafeAddress, payloadToReject: input.payloadToReject });
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// src/core/MessageHelper.ts
|
|
448
|
+
var MessageHelper = class {
|
|
449
|
+
// Message used for MSafe creation
|
|
450
|
+
static createMSafeMessage(msafeAddress) {
|
|
451
|
+
return `Create MSafe Account: ${msafeAddress}`;
|
|
452
|
+
}
|
|
453
|
+
static deCreateMSafeMessage(msg) {
|
|
454
|
+
const regex = /Create MSafe Account: (.+)/;
|
|
455
|
+
const matches = msg.match(regex);
|
|
456
|
+
return matches ? matches[1] : void 0;
|
|
457
|
+
}
|
|
458
|
+
// Message to be used when user login. The timestamp string
|
|
459
|
+
// is used to for extra validation.
|
|
460
|
+
static welcomeMessage(timestamp) {
|
|
461
|
+
return `Welcome to MSafe. ${timestamp}`;
|
|
462
|
+
}
|
|
463
|
+
static deWelcomeMessage(msg) {
|
|
464
|
+
const regex = /Welcome to MSafe. (.+)/;
|
|
465
|
+
const matches = msg.match(regex);
|
|
466
|
+
return matches ? matches[1] : void 0;
|
|
467
|
+
}
|
|
468
|
+
static proposeIntentionMessage(data) {
|
|
469
|
+
const { intention, sn } = data;
|
|
470
|
+
const intentionData = IntentionHelper.ser(intention);
|
|
471
|
+
const msg = {
|
|
472
|
+
intentionData,
|
|
473
|
+
sequenceNumber: sn
|
|
474
|
+
};
|
|
475
|
+
return JSON.stringify(msg);
|
|
476
|
+
}
|
|
477
|
+
static deProposeIntentionMessage(s) {
|
|
478
|
+
const de = JSON.parse(s);
|
|
479
|
+
if (!("intentionData" in de) || typeof de.intentionData !== "string" || !("sequenceNumber" in de) || typeof de.sequenceNumber !== "number") {
|
|
480
|
+
throw new Error("Invalid intention data");
|
|
481
|
+
}
|
|
482
|
+
const { sequenceNumber, intentionData } = de;
|
|
483
|
+
return {
|
|
484
|
+
sn: sequenceNumber,
|
|
485
|
+
intention: IntentionHelper.de(intentionData)
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// src/utils/multi-sig.ts
|
|
491
|
+
var import_ed255192 = require("@mysten/sui.js/keypairs/ed25519");
|
|
492
|
+
var import_multisig2 = require("@mysten/sui.js/multisig");
|
|
493
|
+
var NONCE_PK_PREFIX = "maven";
|
|
494
|
+
var NONCE_PREFIX_MAX_SIZE = 16;
|
|
495
|
+
var NONCE_PK_WEIGHT = 1;
|
|
496
|
+
var MAX_WEIGHT = 255;
|
|
497
|
+
var MIN_WEIGHT = 1;
|
|
498
|
+
var MAX_OWNER_WITH_NONCE = 9;
|
|
499
|
+
var MAX_OWNER_WITHOUT_NONCE = 10;
|
|
500
|
+
var MIN_THRESHOLD = 1;
|
|
501
|
+
var RawMultiSig = class _RawMultiSig {
|
|
502
|
+
constructor(config) {
|
|
503
|
+
this.config = config;
|
|
504
|
+
this.rawMsPK = getMultiSigPublicKey(config);
|
|
505
|
+
}
|
|
506
|
+
rawMsPK;
|
|
507
|
+
static fromMSafeAccountInfo(info) {
|
|
508
|
+
const parsed = {
|
|
509
|
+
threshold: info.threshold,
|
|
510
|
+
ownerWithWeight: info.ownersWithWeightPK,
|
|
511
|
+
creationNonce: info.creationNonce
|
|
512
|
+
};
|
|
513
|
+
return new _RawMultiSig(parsed);
|
|
514
|
+
}
|
|
515
|
+
get suiAddress() {
|
|
516
|
+
return this.rawMsPK.toSuiAddress();
|
|
517
|
+
}
|
|
518
|
+
get publicKeys() {
|
|
519
|
+
return this.rawMsPK.getPublicKeys();
|
|
520
|
+
}
|
|
521
|
+
get threshold() {
|
|
522
|
+
return this.config.threshold;
|
|
523
|
+
}
|
|
524
|
+
combinePartialSignatures(signatures) {
|
|
525
|
+
return this.rawMsPK.combinePartialSignatures(signatures);
|
|
526
|
+
}
|
|
527
|
+
async verifyPersonalMessage(messageStr, multiSigSignature) {
|
|
528
|
+
const message = stringToBuffer(messageStr);
|
|
529
|
+
return this.rawMsPK.verifyPersonalMessage(message, multiSigSignature);
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
function getMultiSigPublicKey(config) {
|
|
533
|
+
const { ownerWithWeight, threshold, creationNonce } = config;
|
|
534
|
+
const pks = ownerWithWeight.map((pk) => ({
|
|
535
|
+
publicKey: pk.publicKey,
|
|
536
|
+
weight: pk.weight
|
|
537
|
+
}));
|
|
538
|
+
if (creationNonce !== void 0) {
|
|
539
|
+
pks.push({
|
|
540
|
+
publicKey: makeNoncePublicKey(creationNonce),
|
|
541
|
+
weight: NONCE_PK_WEIGHT
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
return import_multisig2.MultiSigPublicKey.fromPublicKeys({ threshold, publicKeys: pks });
|
|
545
|
+
}
|
|
546
|
+
function makeNoncePublicKey(nonce) {
|
|
547
|
+
const buffer = new ArrayBuffer(import_ed255192.Ed25519PublicKey.SIZE);
|
|
548
|
+
const textEncoder = new TextEncoder();
|
|
549
|
+
textEncoder.encodeInto(NONCE_PK_PREFIX, new Uint8Array(buffer, 0, NONCE_PREFIX_MAX_SIZE));
|
|
550
|
+
const nonceView = new DataView(buffer, NONCE_PREFIX_MAX_SIZE, 4);
|
|
551
|
+
nonceView.setUint32(0, nonce, true);
|
|
552
|
+
return new import_ed255192.Ed25519PublicKey(new Uint8Array(buffer));
|
|
553
|
+
}
|
|
554
|
+
function validateMultiSigConfig(config) {
|
|
555
|
+
config.ownerWithWeight.forEach((pk) => {
|
|
556
|
+
const { weight } = pk;
|
|
557
|
+
if (weight < MIN_WEIGHT || weight > MAX_WEIGHT) {
|
|
558
|
+
throw new Error(`Invalid multi-sig weight: ${weight} (1-${MAX_WEIGHT})`);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
const totalWeight = config.ownerWithWeight.reduce((s, pk) => s + pk.weight, 0);
|
|
562
|
+
if (config.threshold > totalWeight) {
|
|
563
|
+
throw new Error("Threshold is larger than total weight");
|
|
564
|
+
}
|
|
565
|
+
if (config.threshold < MIN_THRESHOLD) {
|
|
566
|
+
throw new Error("Threshold is smaller than 1");
|
|
567
|
+
}
|
|
568
|
+
const maxOwner = config.creationNonce === void 0 ? MAX_OWNER_WITHOUT_NONCE : MAX_OWNER_WITH_NONCE;
|
|
569
|
+
if (config.ownerWithWeight.length > maxOwner) {
|
|
570
|
+
throw new Error("Owner number bigger than upper cap");
|
|
571
|
+
}
|
|
572
|
+
const addressSet = new Set(config.ownerWithWeight.map((pk) => pk.publicKey.toSuiAddress()));
|
|
573
|
+
if (addressSet.size !== config.ownerWithWeight.length) {
|
|
574
|
+
throw new Error("Duplicate address detected");
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/core/CreateHelper.ts
|
|
579
|
+
var CreateHelper = class {
|
|
580
|
+
constructor(globals, pkHelper) {
|
|
581
|
+
this.globals = globals;
|
|
582
|
+
this.pkHelper = pkHelper;
|
|
583
|
+
}
|
|
584
|
+
async getPublicKeyBatch(addresses) {
|
|
585
|
+
return this.pkHelper.getPublicKeyBatch(addresses);
|
|
586
|
+
}
|
|
587
|
+
async calculateMSafeAddress(info) {
|
|
588
|
+
const msConfig = await this.reduceCreationInfoToRawConfig(info);
|
|
589
|
+
const ms = new RawMultiSig(msConfig);
|
|
590
|
+
return ms.suiAddress;
|
|
591
|
+
}
|
|
592
|
+
// Validate the create info and return the msafe address.
|
|
593
|
+
async validateCreateInfo(createInfo) {
|
|
594
|
+
const rawConfig = await this.reduceCreationInfoToRawConfig(createInfo);
|
|
595
|
+
validateMultiSigConfig(rawConfig);
|
|
596
|
+
return this.calculateMSafeAddress(createInfo);
|
|
597
|
+
}
|
|
598
|
+
async submitMSafeCreation(creationInfo) {
|
|
599
|
+
const msafeAddress = await this.validateCreateInfo(creationInfo);
|
|
600
|
+
const signingMessage = MessageHelper.createMSafeMessage(msafeAddress);
|
|
601
|
+
const signature = await this.globals.wallet.signPersonalMessage({ messageStr: signingMessage });
|
|
602
|
+
await this.submitToBackend(creationInfo, signature.signature);
|
|
603
|
+
return msafeAddress;
|
|
604
|
+
}
|
|
605
|
+
async reduceCreationInfoToRawConfig(info) {
|
|
606
|
+
const publicKeys = await this.getPublicKeyBatch(info.ownerWithWeight.map((oww) => oww.address));
|
|
607
|
+
publicKeys.forEach((pk, i) => {
|
|
608
|
+
if (pk === void 0) {
|
|
609
|
+
throw new Error(`Unknown public key for address: ${info.ownerWithWeight[i].address}`);
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
return {
|
|
613
|
+
threshold: info.threshold,
|
|
614
|
+
ownerWithWeight: info.ownerWithWeight.map((owner, i) => ({
|
|
615
|
+
publicKey: publicKeys[i],
|
|
616
|
+
weight: owner.weight
|
|
617
|
+
})),
|
|
618
|
+
creationNonce: info.creationNonce
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
async submitToBackend(createInfo, signature) {
|
|
622
|
+
const pks = await this.pkHelper.getPublicKeyBatch(createInfo.ownerWithWeight.map((owner) => owner.address));
|
|
623
|
+
await this.globals.backend.createMSafeAccount({
|
|
624
|
+
ownerWithWeight: createInfo.ownerWithWeight.map((owner, i) => ({
|
|
625
|
+
address: owner.address,
|
|
626
|
+
weight: owner.weight,
|
|
627
|
+
publicKey: pks[i]
|
|
628
|
+
// PublicKey has been verified.
|
|
629
|
+
})),
|
|
630
|
+
threshold: createInfo.threshold,
|
|
631
|
+
name: createInfo.name,
|
|
632
|
+
// name validation is deferred to backend
|
|
633
|
+
description: createInfo.description,
|
|
634
|
+
// description validation is deferred to backend
|
|
635
|
+
creationNonce: createInfo.creationNonce,
|
|
636
|
+
signature
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
// src/core/MSafeAccount.ts
|
|
642
|
+
var MSafeAccount = class _MSafeAccount {
|
|
643
|
+
constructor(globals, info) {
|
|
644
|
+
this.globals = globals;
|
|
645
|
+
this.info = info;
|
|
646
|
+
this.rawMultiSig = RawMultiSig.fromMSafeAccountInfo(info);
|
|
647
|
+
}
|
|
648
|
+
rawMultiSig;
|
|
649
|
+
static async new(globals, address) {
|
|
650
|
+
const info = await globals.backend.getMSafeAccountInfo(address);
|
|
651
|
+
return new _MSafeAccount(globals, info);
|
|
652
|
+
}
|
|
653
|
+
async pendingTransactions() {
|
|
654
|
+
return this.backend.getPendingTransactions(this.address);
|
|
655
|
+
}
|
|
656
|
+
async historyTransaction() {
|
|
657
|
+
return this.backend.getHistoryTransactions(this.address);
|
|
658
|
+
}
|
|
659
|
+
async futureIntentions() {
|
|
660
|
+
return this.backend.getFutureIntentions(this.address);
|
|
661
|
+
}
|
|
662
|
+
async currentSequenceNumber() {
|
|
663
|
+
return this.backend.getCurrentSequenceNumber(this.address);
|
|
664
|
+
}
|
|
665
|
+
async nextSequenceNumber() {
|
|
666
|
+
return this.backend.getNextSequenceNumber(this.address);
|
|
667
|
+
}
|
|
668
|
+
async proposeIntention(intention, sequenceNumber) {
|
|
669
|
+
const message = MessageHelper.proposeIntentionMessage({ intention, sn: sequenceNumber });
|
|
670
|
+
const signature = await this.wallet.signPersonalMessage({
|
|
671
|
+
messageStr: message
|
|
672
|
+
});
|
|
673
|
+
await this.backend.proposeIntention({
|
|
674
|
+
intention,
|
|
675
|
+
sequenceNumber,
|
|
676
|
+
msafeAddress: this.address,
|
|
677
|
+
userAddress: await this.userAddress(),
|
|
678
|
+
signature: signature.signature
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
async voteForTransaction(digest, payload) {
|
|
682
|
+
const payloadBytes = HexToUint8Array(payload);
|
|
683
|
+
const signature = await this.wallet.signTransactionBlock({ transactionBlock: payloadBytes });
|
|
684
|
+
return this.backend.voteForTransaction({
|
|
685
|
+
msafeAddress: this.address,
|
|
686
|
+
userAddress: await this.userAddress(),
|
|
687
|
+
txDigest: digest,
|
|
688
|
+
signature: signature.signature
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
// Shortcut for proposing a transaction to be a pending transaction, and add user vote to it.
|
|
692
|
+
// Requires the multi-sig to be empty in pending transaction.
|
|
693
|
+
async proposePendingTransaction(intention) {
|
|
694
|
+
const txb = await IntentionHelper.buildTxb({
|
|
695
|
+
suiClient: this.suiClient,
|
|
696
|
+
intention,
|
|
697
|
+
sender: this.address
|
|
698
|
+
});
|
|
699
|
+
const payload = await txb.build({ client: this.suiClient });
|
|
700
|
+
const digest = await txb.getDigest({ client: this.suiClient });
|
|
701
|
+
const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
|
|
702
|
+
return this.backend.proposePendingTransaction({
|
|
703
|
+
msafeAddress: this.address,
|
|
704
|
+
userAddress: await this.userAddress(),
|
|
705
|
+
intention,
|
|
706
|
+
digest,
|
|
707
|
+
signature: signature.signature
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
async rejectCurrentTx(pending) {
|
|
711
|
+
let payloadToReject;
|
|
712
|
+
if (pending) {
|
|
713
|
+
if (pending.isRejectTx) {
|
|
714
|
+
throw new Error("Pending not reject transaction");
|
|
715
|
+
}
|
|
716
|
+
payloadToReject = pending.payload;
|
|
717
|
+
} else {
|
|
718
|
+
const pendings = await this.pendingTransactions();
|
|
719
|
+
if (pendings.length !== 1 || pendings[0].isRejectTx) {
|
|
720
|
+
throw new Error("Already rejected");
|
|
721
|
+
}
|
|
722
|
+
payloadToReject = pendings[0].payload;
|
|
723
|
+
}
|
|
724
|
+
const rejectTxb = await IntentionHelper.buildRejectTransaction({
|
|
725
|
+
msafeAddress: this.address,
|
|
726
|
+
payloadToReject
|
|
727
|
+
});
|
|
728
|
+
const digest = await rejectTxb.getDigest({ client: this.suiClient });
|
|
729
|
+
const payload = await rejectTxb.build({ client: this.suiClient });
|
|
730
|
+
const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
|
|
731
|
+
return this.backend.rejectCurrentTx({
|
|
732
|
+
msafeAddress: this.address,
|
|
733
|
+
userAddress: await this.userAddress(),
|
|
734
|
+
digest,
|
|
735
|
+
signature: signature.signature
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
async buildNextIntentionAndAddToPending() {
|
|
739
|
+
return this.backend.buildNextIntentionAndAddToPending({ msafeAddress: this.address });
|
|
740
|
+
}
|
|
741
|
+
async skipNextFailedIntention() {
|
|
742
|
+
return this.backend.skipNextFailedIntention({ msafeAddress: this.address, userAddress: await this.userAddress() });
|
|
743
|
+
}
|
|
744
|
+
async executePendingTx(pending) {
|
|
745
|
+
if (pending.votes.length < this.info.threshold) {
|
|
746
|
+
throw new Error("Not enough signatures");
|
|
747
|
+
}
|
|
748
|
+
const sigs = [];
|
|
749
|
+
const gotSigs = new Map(pending.votes.map((vote) => [vote.userAddress, vote.signature]));
|
|
750
|
+
for (let i = 0; i < this.info.ownersWithWeightPK.length; i++) {
|
|
751
|
+
const owner = this.info.ownersWithWeightPK[i];
|
|
752
|
+
const signature = gotSigs.get(owner.publicKey.toSuiAddress());
|
|
753
|
+
if (signature) {
|
|
754
|
+
sigs.push(signature);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
const multiSignature = this.rawMultiSig.combinePartialSignatures(sigs);
|
|
758
|
+
return this.suiClient.executeTransactionBlock({
|
|
759
|
+
transactionBlock: HexToUint8Array(pending.payload),
|
|
760
|
+
signature: multiSignature,
|
|
761
|
+
options: { showEffects: true }
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
get address() {
|
|
765
|
+
return this.info.address;
|
|
766
|
+
}
|
|
767
|
+
get backend() {
|
|
768
|
+
return this.globals.backend;
|
|
769
|
+
}
|
|
770
|
+
get wallet() {
|
|
771
|
+
return this.globals.wallet;
|
|
772
|
+
}
|
|
773
|
+
async userAddress() {
|
|
774
|
+
return this.globals.wallet.address();
|
|
775
|
+
}
|
|
776
|
+
get suiClient() {
|
|
777
|
+
return this.globals.suiClient;
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
// src/core/PublicKeyHelper.ts
|
|
782
|
+
var PublicKeyHelper = class {
|
|
783
|
+
constructor(globals) {
|
|
784
|
+
this.globals = globals;
|
|
785
|
+
this.knownPublicKeys = /* @__PURE__ */ new Map();
|
|
786
|
+
}
|
|
787
|
+
knownPublicKeys;
|
|
788
|
+
async getPublicKey(address) {
|
|
789
|
+
const cached = this.knownPublicKeys.get(address);
|
|
790
|
+
if (cached) {
|
|
791
|
+
return cached;
|
|
792
|
+
}
|
|
793
|
+
const pk = await this._getPublicKey(address);
|
|
794
|
+
if (pk) {
|
|
795
|
+
this.knownPublicKeys.set(address, pk);
|
|
796
|
+
}
|
|
797
|
+
return pk;
|
|
798
|
+
}
|
|
799
|
+
async getPublicKeyBatch(addresses) {
|
|
800
|
+
const results = new Array(addresses.length).fill(void 0);
|
|
801
|
+
for (let i = 0; i < addresses.length; i++) {
|
|
802
|
+
const address = addresses[i];
|
|
803
|
+
results[i] = this.knownPublicKeys.get(address);
|
|
804
|
+
}
|
|
805
|
+
const emptyIndexes = results.map((elem, index) => elem === void 0 ? index : -1).filter((index) => index !== -1);
|
|
806
|
+
const backendResult = await this.globals.backend.getPublicKeyBatch(emptyIndexes.map((index) => addresses[index]));
|
|
807
|
+
for (let i = 0; i < emptyIndexes.length; i++) {
|
|
808
|
+
const index = emptyIndexes[i];
|
|
809
|
+
results[index] = backendResult[i];
|
|
810
|
+
}
|
|
811
|
+
for (let i = 0; i < results.length; i++) {
|
|
812
|
+
if (results[i] === void 0) {
|
|
813
|
+
results[i] = await this.getPublicKeyFromChain(addresses[i]);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
for (let i = 0; i < addresses.length; i++) {
|
|
817
|
+
if (results[i]) {
|
|
818
|
+
this.knownPublicKeys.set(addresses[i], results[i]);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return results;
|
|
822
|
+
}
|
|
823
|
+
async _getPublicKey(address) {
|
|
824
|
+
const pkBackend = await this.getPublicKeyFromBackend(address);
|
|
825
|
+
if (pkBackend) {
|
|
826
|
+
return pkBackend;
|
|
827
|
+
}
|
|
828
|
+
const pkChain = await this.getPublicKeyFromChain(address);
|
|
829
|
+
if (pkChain) {
|
|
830
|
+
return pkChain;
|
|
831
|
+
}
|
|
832
|
+
return void 0;
|
|
833
|
+
}
|
|
834
|
+
async getPublicKeyFromBackend(address) {
|
|
835
|
+
try {
|
|
836
|
+
const pk = await this.globals.backend.getPublicKey(address);
|
|
837
|
+
return pk;
|
|
838
|
+
} catch (_) {
|
|
839
|
+
return void 0;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
async getPublicKeyFromChain(address) {
|
|
843
|
+
return getPublicKeyFromChain(this.globals.suiClient, address);
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
// src/globals/MSafeGlobals.ts
|
|
848
|
+
var import_client = require("@mysten/sui.js/client");
|
|
849
|
+
|
|
850
|
+
// src/backend/PseudoBackend.ts
|
|
851
|
+
var import_reflect_metadata2 = require("reflect-metadata");
|
|
852
|
+
var import_typeorm = require("typeorm");
|
|
853
|
+
|
|
854
|
+
// src/backend/CoreDatabase.ts
|
|
855
|
+
var import_reflect_metadata = require("reflect-metadata");
|
|
856
|
+
var import_core = require("@msafe/sui3-model/core");
|
|
857
|
+
var WALLET_TYPE_KEY = "wallet_type";
|
|
858
|
+
var CoreDB = class _CoreDB {
|
|
859
|
+
constructor(coreModel) {
|
|
860
|
+
this.coreModel = coreModel;
|
|
861
|
+
}
|
|
862
|
+
static async New(dbConfig) {
|
|
863
|
+
const core = await import_core.CoreModel.New(dbConfig);
|
|
864
|
+
return new _CoreDB(core);
|
|
865
|
+
}
|
|
866
|
+
async upsertUser(input) {
|
|
867
|
+
const currentUser = await this.coreModel.user.findOneBy({
|
|
868
|
+
address: input.address
|
|
869
|
+
});
|
|
870
|
+
if (currentUser !== null) {
|
|
871
|
+
currentUser.lastLogin = input.lastLogin;
|
|
872
|
+
await this.coreModel.user.save(currentUser);
|
|
873
|
+
} else {
|
|
874
|
+
const serializedPublicKey = PublicKeySerde.ser(input.publicKey);
|
|
875
|
+
const user = {
|
|
876
|
+
address: input.address,
|
|
877
|
+
publicKey: serializedPublicKey.publicKey,
|
|
878
|
+
schema: serializedPublicKey.scheme,
|
|
879
|
+
nonce: 0,
|
|
880
|
+
lastLogin: /* @__PURE__ */ new Date()
|
|
881
|
+
};
|
|
882
|
+
await this.coreModel.user.save(user);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
async updateUserWalletType(input) {
|
|
886
|
+
const exist = await this.coreModel.userSetting.findOneBy({
|
|
887
|
+
userAddress: input.address,
|
|
888
|
+
key: WALLET_TYPE_KEY
|
|
889
|
+
});
|
|
890
|
+
const walletType = input.walletType.trim();
|
|
891
|
+
if (exist === null || exist.value !== walletType) {
|
|
892
|
+
const userSetting = {
|
|
893
|
+
userAddress: input.address,
|
|
894
|
+
key: WALLET_TYPE_KEY,
|
|
895
|
+
value: walletType
|
|
896
|
+
};
|
|
897
|
+
await this.coreModel.userSetting.save(userSetting);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
// src/backend/PseudoBackend.ts
|
|
903
|
+
var PseudoBackend = class _PseudoBackend {
|
|
904
|
+
constructor(db, _suiClient) {
|
|
905
|
+
this.db = db;
|
|
906
|
+
this._suiClient = _suiClient;
|
|
907
|
+
}
|
|
908
|
+
_token;
|
|
909
|
+
static async New(dbConfig, suiClient) {
|
|
910
|
+
const db = await CoreDB.New(dbConfig);
|
|
911
|
+
return new _PseudoBackend(db, suiClient);
|
|
912
|
+
}
|
|
913
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars,unused-imports/no-unused-vars
|
|
914
|
+
async isJWTTokenValid(_jwt) {
|
|
915
|
+
return true;
|
|
916
|
+
}
|
|
917
|
+
async authSign(input) {
|
|
918
|
+
const timestamp = MessageHelper.deWelcomeMessage(input.message);
|
|
919
|
+
if (!timestamp) {
|
|
920
|
+
throw new Error("Invalid welcome message");
|
|
921
|
+
}
|
|
922
|
+
const date = Date.parse(timestamp);
|
|
923
|
+
if ((/* @__PURE__ */ new Date()).getTime() - date < 0) {
|
|
924
|
+
throw new Error("Invalid timestamp");
|
|
925
|
+
}
|
|
926
|
+
if ((/* @__PURE__ */ new Date()).getTime() - date > 10 * 1e3) {
|
|
927
|
+
throw new Error("Signing message expired");
|
|
928
|
+
}
|
|
929
|
+
const publicKey = await SignatureVerifier.getPublicKeyFromPersonalSignature({
|
|
930
|
+
messageStr: input.message,
|
|
931
|
+
signature: input.signature
|
|
932
|
+
});
|
|
933
|
+
if (!Formatter.isSuiAddressEqual(input.address, publicKey.toSuiAddress())) {
|
|
934
|
+
throw new Error("Invalid signature");
|
|
935
|
+
}
|
|
936
|
+
await this.db.upsertUser({
|
|
937
|
+
address: input.address,
|
|
938
|
+
publicKey,
|
|
939
|
+
lastLogin: /* @__PURE__ */ new Date()
|
|
940
|
+
});
|
|
941
|
+
await this.db.updateUserWalletType({
|
|
942
|
+
address: input.address,
|
|
943
|
+
walletType: input.walletType
|
|
944
|
+
});
|
|
945
|
+
this._token = "";
|
|
946
|
+
return this._token;
|
|
947
|
+
}
|
|
948
|
+
setJWTToken(token) {
|
|
949
|
+
this._token = token;
|
|
950
|
+
}
|
|
951
|
+
async getPublicKey(address) {
|
|
952
|
+
const user = await this.model.user.findOneBy({
|
|
953
|
+
address
|
|
954
|
+
});
|
|
955
|
+
if (user === null) {
|
|
956
|
+
return void 0;
|
|
957
|
+
}
|
|
958
|
+
return PublicKeySerde.de({
|
|
959
|
+
publicKey: user.publicKey,
|
|
960
|
+
scheme: user.schema
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
async getPublicKeyBatch(addresses) {
|
|
964
|
+
const res = [];
|
|
965
|
+
for (let i = 0; i < addresses.length; i++) {
|
|
966
|
+
const address = addresses[i];
|
|
967
|
+
const user = await this.getUser(address);
|
|
968
|
+
res.push(
|
|
969
|
+
user ? PublicKeySerde.de({
|
|
970
|
+
publicKey: user.publicKey,
|
|
971
|
+
scheme: user.schema
|
|
972
|
+
}) : void 0
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
return res;
|
|
976
|
+
}
|
|
977
|
+
async createMSafeAccount(input) {
|
|
978
|
+
const ms = new RawMultiSig(input);
|
|
979
|
+
const msafeAddr = ms.suiAddress;
|
|
980
|
+
const signingMsg = MessageHelper.createMSafeMessage(msafeAddr);
|
|
981
|
+
const targetAddr = input.ownerWithWeight[0].address;
|
|
982
|
+
const verifyResult = await SignatureVerifier.verifyPersonalSignature({
|
|
983
|
+
messageStr: signingMsg,
|
|
984
|
+
signature: input.signature,
|
|
985
|
+
targetAddress: targetAddr
|
|
986
|
+
});
|
|
987
|
+
if (!verifyResult) {
|
|
988
|
+
throw new Error("Signature verification failed");
|
|
989
|
+
}
|
|
990
|
+
if (input.name.length > 128) {
|
|
991
|
+
throw new Error("Name too long");
|
|
992
|
+
}
|
|
993
|
+
if (input.description && input.description.length > 512) {
|
|
994
|
+
throw new Error("Description too long");
|
|
995
|
+
}
|
|
996
|
+
const creatorAddress = input.ownerWithWeight[0].address;
|
|
997
|
+
const creator = await this.getUser(creatorAddress);
|
|
998
|
+
if (creator === null) {
|
|
999
|
+
throw new Error("Creator not found");
|
|
1000
|
+
}
|
|
1001
|
+
if (creator.nonce !== input.creationNonce) {
|
|
1002
|
+
throw new Error("Nonce not match");
|
|
1003
|
+
}
|
|
1004
|
+
const msafeExistCheck = await this.model.msafe.findOneBy({
|
|
1005
|
+
address: msafeAddr
|
|
1006
|
+
});
|
|
1007
|
+
if (msafeExistCheck !== null) {
|
|
1008
|
+
throw new Error("MSafe already exist in database");
|
|
1009
|
+
}
|
|
1010
|
+
input.ownerWithWeight.forEach((ownerInfo) => {
|
|
1011
|
+
if (!Formatter.isSuiAddressEqual(ownerInfo.address, ownerInfo.publicKey.toSuiAddress())) {
|
|
1012
|
+
throw new Error("Sui address public key not match");
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
for (let i = 0; i !== input.ownerWithWeight.length; i++) {
|
|
1016
|
+
const ownerInfo = input.ownerWithWeight[i];
|
|
1017
|
+
if (i !== 0) {
|
|
1018
|
+
const coManager = await this.getUser(ownerInfo.address);
|
|
1019
|
+
const serPK = PublicKeySerde.ser(ownerInfo.publicKey);
|
|
1020
|
+
if (coManager === null) {
|
|
1021
|
+
const user = {
|
|
1022
|
+
address: ownerInfo.address,
|
|
1023
|
+
publicKey: serPK.publicKey,
|
|
1024
|
+
schema: serPK.scheme,
|
|
1025
|
+
nonce: 0,
|
|
1026
|
+
lastLogin: /* @__PURE__ */ new Date()
|
|
1027
|
+
};
|
|
1028
|
+
await this.model.user.save(user);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
const userMSafe = {
|
|
1032
|
+
userAddress: ownerInfo.address,
|
|
1033
|
+
msafeAddress: msafeAddr,
|
|
1034
|
+
index: i,
|
|
1035
|
+
weight: ownerInfo.weight,
|
|
1036
|
+
status: i === 0 ? "active" : "pending"
|
|
1037
|
+
};
|
|
1038
|
+
await this.model.userMSafe.save(userMSafe);
|
|
1039
|
+
}
|
|
1040
|
+
const creationNonce = creator.nonce;
|
|
1041
|
+
creator.nonce++;
|
|
1042
|
+
await this.model.user.save(creator);
|
|
1043
|
+
const msafe = {
|
|
1044
|
+
address: msafeAddr,
|
|
1045
|
+
creationNonce,
|
|
1046
|
+
creator: creatorAddress,
|
|
1047
|
+
name: input.name,
|
|
1048
|
+
description: input.description,
|
|
1049
|
+
threshold: input.threshold
|
|
1050
|
+
};
|
|
1051
|
+
await this.model.msafe.save(msafe);
|
|
1052
|
+
}
|
|
1053
|
+
async getMSafeAccountInfo(msafeAddress) {
|
|
1054
|
+
const msafe = await this.model.msafe.findOneBy({ address: msafeAddress });
|
|
1055
|
+
if (msafe === null) {
|
|
1056
|
+
throw new Error("MSafe not found");
|
|
1057
|
+
}
|
|
1058
|
+
const userMSafes = await this.model.userMSafe.find({
|
|
1059
|
+
where: {
|
|
1060
|
+
msafeAddress
|
|
1061
|
+
},
|
|
1062
|
+
order: {
|
|
1063
|
+
index: "asc"
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
if (userMSafes.length === 0) {
|
|
1067
|
+
throw new Error("MSafe does not have user info.");
|
|
1068
|
+
}
|
|
1069
|
+
const users = await Promise.all(userMSafes.map((um) => this.getUser(um.userAddress)));
|
|
1070
|
+
users.forEach((user) => {
|
|
1071
|
+
if (user === null) {
|
|
1072
|
+
throw new Error("User not found");
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
const ownersWithWeightPK = userMSafes.map((userMSafe, i) => ({
|
|
1076
|
+
address: userMSafe.userAddress,
|
|
1077
|
+
weight: userMSafe.weight,
|
|
1078
|
+
publicKey: PublicKeySerde.de({ publicKey: users[i].publicKey, scheme: users[i].schema })
|
|
1079
|
+
}));
|
|
1080
|
+
return {
|
|
1081
|
+
address: msafeAddress,
|
|
1082
|
+
ownersWithWeightPK,
|
|
1083
|
+
threshold: msafe.threshold,
|
|
1084
|
+
name: msafe.name,
|
|
1085
|
+
description: msafe.description,
|
|
1086
|
+
creationNonce: msafe.creationNonce
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
async getUserInfo(userAddress) {
|
|
1090
|
+
const user = await this.getUser(userAddress);
|
|
1091
|
+
if (user === null) {
|
|
1092
|
+
throw new Error("404: user not found");
|
|
1093
|
+
}
|
|
1094
|
+
const msafeAccounts = await this.model.userMSafe.findBy({ userAddress });
|
|
1095
|
+
const ownedMSafe = await Promise.all(
|
|
1096
|
+
msafeAccounts.map(async (msafeAccount) => this.getMSafeAccountInfo(msafeAccount.msafeAddress))
|
|
1097
|
+
);
|
|
1098
|
+
return {
|
|
1099
|
+
address: userAddress,
|
|
1100
|
+
publicKey: user.publicKey,
|
|
1101
|
+
schema: user.schema,
|
|
1102
|
+
creationNonce: user.nonce,
|
|
1103
|
+
ownedMSafe
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
async getPendingTransactions(msafeAddress) {
|
|
1107
|
+
const pendings = await this.db.coreModel.pendingTransaction.findBy({
|
|
1108
|
+
msafeAddress
|
|
1109
|
+
});
|
|
1110
|
+
if (pendings.length === 0) {
|
|
1111
|
+
return [];
|
|
1112
|
+
}
|
|
1113
|
+
const votes = await Promise.all(
|
|
1114
|
+
pendings.map((pending) => this.db.coreModel.userVote.findBy({ txDigest: pending.digest, isValid: true }))
|
|
1115
|
+
);
|
|
1116
|
+
const intention = await this.model.transactionIntention.findOneBy({
|
|
1117
|
+
msafeAddress,
|
|
1118
|
+
sequenceNumber: pendings[0].sequenceNumber
|
|
1119
|
+
});
|
|
1120
|
+
if (intention === null) {
|
|
1121
|
+
throw new Error("Intention not found");
|
|
1122
|
+
}
|
|
1123
|
+
const intent = IntentionHelper.de(intention.data);
|
|
1124
|
+
return pendings.map((pending, i) => ({
|
|
1125
|
+
digest: pending.digest,
|
|
1126
|
+
payload: pending.payload,
|
|
1127
|
+
msafeAddress: pending.msafeAddress,
|
|
1128
|
+
isRejectTx: pending.isRejectTx,
|
|
1129
|
+
creator: pending.creator,
|
|
1130
|
+
createdAt: pending.createdAt,
|
|
1131
|
+
votes: votes[i].map((vote) => ({
|
|
1132
|
+
userAddress: vote.userAddress,
|
|
1133
|
+
signature: vote.signature,
|
|
1134
|
+
timestamp: vote.updatedAt
|
|
1135
|
+
})),
|
|
1136
|
+
sequenceNumber: pending.sequenceNumber,
|
|
1137
|
+
intention: pending.isRejectTx ? void 0 : intent
|
|
1138
|
+
}));
|
|
1139
|
+
}
|
|
1140
|
+
async getCurrentSequenceNumber(msafeAddress) {
|
|
1141
|
+
const maxSNHistory = await this.model.historyTransaction.findOne({
|
|
1142
|
+
where: { msafeAddress },
|
|
1143
|
+
order: { sequenceNumber: "desc" }
|
|
1144
|
+
});
|
|
1145
|
+
return maxSNHistory === null ? 0 : maxSNHistory.sequenceNumber + 1;
|
|
1146
|
+
}
|
|
1147
|
+
async getNextSequenceNumber(msafeAddress) {
|
|
1148
|
+
const maxSNIntention = await this.model.transactionIntention.findOne({
|
|
1149
|
+
where: { msafeAddress },
|
|
1150
|
+
order: { sequenceNumber: "desc" }
|
|
1151
|
+
});
|
|
1152
|
+
return maxSNIntention === null ? 0 : maxSNIntention.sequenceNumber + 1;
|
|
1153
|
+
}
|
|
1154
|
+
async getHistoryTransactions(msafeAddress) {
|
|
1155
|
+
const transactions = await this.db.coreModel.historyTransaction.find({
|
|
1156
|
+
where: {
|
|
1157
|
+
msafeAddress
|
|
1158
|
+
},
|
|
1159
|
+
order: { sequenceNumber: "desc" }
|
|
1160
|
+
});
|
|
1161
|
+
const votes = await Promise.all(
|
|
1162
|
+
transactions.map(
|
|
1163
|
+
(tx) => this.db.coreModel.userVote.findBy({
|
|
1164
|
+
txDigest: tx.digest,
|
|
1165
|
+
isValid: true
|
|
1166
|
+
})
|
|
1167
|
+
)
|
|
1168
|
+
);
|
|
1169
|
+
return transactions.map((tx, i) => ({
|
|
1170
|
+
digest: tx.digest,
|
|
1171
|
+
payload: tx.payload,
|
|
1172
|
+
msafeAddress: tx.msafeAddress,
|
|
1173
|
+
isRejectTx: tx.isRejectTx,
|
|
1174
|
+
status: tx.status,
|
|
1175
|
+
creator: tx.creator,
|
|
1176
|
+
createdAt: tx.createdAt,
|
|
1177
|
+
sequenceNumber: tx.sequenceNumber,
|
|
1178
|
+
votes: votes[i].map((vote) => ({
|
|
1179
|
+
userAddress: vote.userAddress,
|
|
1180
|
+
timestamp: vote.updatedAt
|
|
1181
|
+
}))
|
|
1182
|
+
}));
|
|
1183
|
+
}
|
|
1184
|
+
async getFutureIntentions(msafeAddress) {
|
|
1185
|
+
const currentSequenceNumber = await this.getCurrentSequenceNumber(msafeAddress);
|
|
1186
|
+
const hasPending = await this.model.pendingTransaction.findOneBy({ msafeAddress }) !== null;
|
|
1187
|
+
const futureSNStart = hasPending ? currentSequenceNumber + 1 : currentSequenceNumber;
|
|
1188
|
+
const futureTxs = await this.model.transactionIntention.find({
|
|
1189
|
+
where: { msafeAddress, sequenceNumber: (0, import_typeorm.MoreThanOrEqual)(futureSNStart) },
|
|
1190
|
+
order: { sequenceNumber: "asc" }
|
|
1191
|
+
});
|
|
1192
|
+
return futureTxs.map((tx) => ({
|
|
1193
|
+
intention: IntentionHelper.de(tx.data),
|
|
1194
|
+
msafeAddress: tx.msafeAddress,
|
|
1195
|
+
sequenceNumber: tx.sequenceNumber,
|
|
1196
|
+
rawData: tx.data,
|
|
1197
|
+
txType: tx.txType,
|
|
1198
|
+
txSubType: tx.txSubType,
|
|
1199
|
+
status: tx.status,
|
|
1200
|
+
statusRemark: tx.statusRemark,
|
|
1201
|
+
creator: tx.creator,
|
|
1202
|
+
createdAt: tx.createdAt
|
|
1203
|
+
}));
|
|
1204
|
+
}
|
|
1205
|
+
async proposeIntention(input) {
|
|
1206
|
+
const userMSafe = await this.model.userMSafe.findOneBy({
|
|
1207
|
+
msafeAddress: input.msafeAddress,
|
|
1208
|
+
userAddress: input.userAddress
|
|
1209
|
+
});
|
|
1210
|
+
if (!userMSafe) {
|
|
1211
|
+
throw new Error("User does not have permission to propose intention");
|
|
1212
|
+
}
|
|
1213
|
+
const verified = await SignatureVerifier.verifyPersonalSignature({
|
|
1214
|
+
messageStr: MessageHelper.proposeIntentionMessage({ intention: input.intention, sn: input.sequenceNumber }),
|
|
1215
|
+
signature: input.signature,
|
|
1216
|
+
targetAddress: input.userAddress
|
|
1217
|
+
});
|
|
1218
|
+
if (!verified) {
|
|
1219
|
+
throw new Error("Invalid signature");
|
|
1220
|
+
}
|
|
1221
|
+
const sequenceNumber = await this.model.transactionIntention.count({
|
|
1222
|
+
where: {
|
|
1223
|
+
msafeAddress: input.msafeAddress
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
if (sequenceNumber !== input.sequenceNumber) {
|
|
1227
|
+
throw new Error("Sequence number not expected");
|
|
1228
|
+
}
|
|
1229
|
+
const intention = {
|
|
1230
|
+
msafeAddress: input.msafeAddress,
|
|
1231
|
+
sequenceNumber,
|
|
1232
|
+
...IntentionHelper.getTxType(input.intention),
|
|
1233
|
+
data: IntentionHelper.ser(input.intention),
|
|
1234
|
+
status: "future",
|
|
1235
|
+
creator: input.userAddress
|
|
1236
|
+
};
|
|
1237
|
+
await this.model.transactionIntention.save(intention);
|
|
1238
|
+
}
|
|
1239
|
+
// Propose a pending transaction. Require the msafe account
|
|
1240
|
+
// Does not have any pending transactions.
|
|
1241
|
+
async proposePendingTransaction(input) {
|
|
1242
|
+
const userMSafe = await this.model.userMSafe.findOneBy({
|
|
1243
|
+
msafeAddress: input.msafeAddress,
|
|
1244
|
+
userAddress: input.userAddress
|
|
1245
|
+
});
|
|
1246
|
+
if (userMSafe === null) {
|
|
1247
|
+
throw new Error("Unauthorized");
|
|
1248
|
+
}
|
|
1249
|
+
const msafePendings = await this.model.pendingTransaction.findBy({
|
|
1250
|
+
msafeAddress: input.msafeAddress
|
|
1251
|
+
});
|
|
1252
|
+
if (msafePendings.length !== 0) {
|
|
1253
|
+
throw new Error("Still have pending transaction");
|
|
1254
|
+
}
|
|
1255
|
+
const txb = await IntentionHelper.buildTxb({
|
|
1256
|
+
suiClient: this._suiClient,
|
|
1257
|
+
intention: input.intention,
|
|
1258
|
+
sender: input.msafeAddress
|
|
1259
|
+
});
|
|
1260
|
+
const payload = await txb.build({ client: this._suiClient });
|
|
1261
|
+
const txDigest = await txb.getDigest({ client: this._suiClient });
|
|
1262
|
+
if (txDigest !== input.digest) {
|
|
1263
|
+
throw new Error("Transaction digest un-match");
|
|
1264
|
+
}
|
|
1265
|
+
const verified = await SignatureVerifier.verifyTransactionSignature({
|
|
1266
|
+
payload,
|
|
1267
|
+
targetAddress: input.userAddress,
|
|
1268
|
+
signature: input.signature
|
|
1269
|
+
});
|
|
1270
|
+
if (!verified) {
|
|
1271
|
+
throw new Error("Failed to verify signature");
|
|
1272
|
+
}
|
|
1273
|
+
const maxSNHistory = await this.model.historyTransaction.findOne({
|
|
1274
|
+
where: { msafeAddress: input.msafeAddress },
|
|
1275
|
+
order: { sequenceNumber: "desc" }
|
|
1276
|
+
});
|
|
1277
|
+
const sequenceNumber = maxSNHistory === null ? 0 : maxSNHistory.sequenceNumber + 1;
|
|
1278
|
+
const intention = {
|
|
1279
|
+
msafeAddress: input.msafeAddress,
|
|
1280
|
+
sequenceNumber,
|
|
1281
|
+
...IntentionHelper.getTxType(input.intention),
|
|
1282
|
+
data: IntentionHelper.ser(input.intention),
|
|
1283
|
+
status: "future",
|
|
1284
|
+
creator: input.userAddress
|
|
1285
|
+
};
|
|
1286
|
+
await this.model.transactionIntention.save(intention);
|
|
1287
|
+
const pendingTx = {
|
|
1288
|
+
digest: txDigest,
|
|
1289
|
+
msafeAddress: input.msafeAddress,
|
|
1290
|
+
payload: Uint8ArrayToHex(payload),
|
|
1291
|
+
sequenceNumber,
|
|
1292
|
+
isRejectTx: false,
|
|
1293
|
+
creator: input.userAddress
|
|
1294
|
+
};
|
|
1295
|
+
await this.model.pendingTransaction.save(pendingTx);
|
|
1296
|
+
const userVote = {
|
|
1297
|
+
userAddress: input.userAddress,
|
|
1298
|
+
txDigest,
|
|
1299
|
+
msafeAddress: input.msafeAddress,
|
|
1300
|
+
signature: input.signature,
|
|
1301
|
+
isValid: true
|
|
1302
|
+
};
|
|
1303
|
+
await this.model.userVote.save(userVote);
|
|
1304
|
+
}
|
|
1305
|
+
// Calls for the first reject transaction
|
|
1306
|
+
async rejectCurrentTx(input) {
|
|
1307
|
+
const userMSafe = await this.model.userMSafe.findOneBy({
|
|
1308
|
+
userAddress: input.userAddress,
|
|
1309
|
+
msafeAddress: input.msafeAddress
|
|
1310
|
+
});
|
|
1311
|
+
if (userMSafe === null) {
|
|
1312
|
+
throw new Error("User does not have permission to MSafe");
|
|
1313
|
+
}
|
|
1314
|
+
const currentPending = await this.model.pendingTransaction.findOneBy({
|
|
1315
|
+
msafeAddress: input.msafeAddress,
|
|
1316
|
+
isRejectTx: false
|
|
1317
|
+
});
|
|
1318
|
+
if (currentPending === null) {
|
|
1319
|
+
throw new Error("No active pending transaction");
|
|
1320
|
+
}
|
|
1321
|
+
const txb = await IntentionHelper.buildRejectTransaction({
|
|
1322
|
+
msafeAddress: input.msafeAddress,
|
|
1323
|
+
payloadToReject: currentPending.payload
|
|
1324
|
+
});
|
|
1325
|
+
const payload = await txb.build({ client: this._suiClient });
|
|
1326
|
+
const digest = await txb.getDigest({ client: this._suiClient });
|
|
1327
|
+
if (input.digest !== digest) {
|
|
1328
|
+
throw new Error("Digest not match");
|
|
1329
|
+
}
|
|
1330
|
+
const verified = await SignatureVerifier.verifyTransactionSignature({
|
|
1331
|
+
payload,
|
|
1332
|
+
targetAddress: input.userAddress,
|
|
1333
|
+
signature: input.signature
|
|
1334
|
+
});
|
|
1335
|
+
if (!verified) {
|
|
1336
|
+
throw new Error("Signature unverified");
|
|
1337
|
+
}
|
|
1338
|
+
const existPendingReject = await this.model.pendingTransaction.findOneBy({
|
|
1339
|
+
msafeAddress: input.msafeAddress,
|
|
1340
|
+
isRejectTx: true
|
|
1341
|
+
});
|
|
1342
|
+
if (existPendingReject !== null) {
|
|
1343
|
+
await this.voteForTransaction({
|
|
1344
|
+
txDigest: existPendingReject.digest,
|
|
1345
|
+
msafeAddress: input.msafeAddress,
|
|
1346
|
+
userAddress: input.userAddress,
|
|
1347
|
+
signature: input.signature
|
|
1348
|
+
});
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
const rejectPayloadStr = Uint8ArrayToHex(payload);
|
|
1352
|
+
const rejectDigest = await txb.getDigest({ client: this._suiClient });
|
|
1353
|
+
const rejectPending = {
|
|
1354
|
+
msafeAddress: input.msafeAddress,
|
|
1355
|
+
digest: rejectDigest,
|
|
1356
|
+
payload: rejectPayloadStr,
|
|
1357
|
+
sequenceNumber: currentPending.sequenceNumber,
|
|
1358
|
+
isRejectTx: true,
|
|
1359
|
+
creator: input.userAddress
|
|
1360
|
+
};
|
|
1361
|
+
await this.model.pendingTransaction.save(rejectPending);
|
|
1362
|
+
const rejectVote = {
|
|
1363
|
+
userAddress: input.userAddress,
|
|
1364
|
+
msafeAddress: input.msafeAddress,
|
|
1365
|
+
txDigest: rejectDigest,
|
|
1366
|
+
signature: input.signature,
|
|
1367
|
+
isValid: true
|
|
1368
|
+
};
|
|
1369
|
+
await this.model.userVote.save(rejectVote);
|
|
1370
|
+
const existVote = await this.model.userVote.findOneBy({
|
|
1371
|
+
txDigest: currentPending.digest,
|
|
1372
|
+
userAddress: input.userAddress
|
|
1373
|
+
});
|
|
1374
|
+
if (existVote !== null) {
|
|
1375
|
+
await this.model.userVote.update(
|
|
1376
|
+
{ userAddress: input.userAddress, txDigest: currentPending.digest },
|
|
1377
|
+
{ isValid: false }
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
async voteForTransaction(input) {
|
|
1382
|
+
const pendingTx = await this.model.pendingTransaction.findOneBy({ digest: input.txDigest });
|
|
1383
|
+
if (!pendingTx) {
|
|
1384
|
+
throw new Error(`Pending transaction not found: ${input.txDigest}`);
|
|
1385
|
+
}
|
|
1386
|
+
if (pendingTx.msafeAddress !== input.msafeAddress) {
|
|
1387
|
+
throw new Error("MSafe address not match");
|
|
1388
|
+
}
|
|
1389
|
+
const userMSafe = await this.model.userMSafe.findOneBy({
|
|
1390
|
+
userAddress: input.userAddress,
|
|
1391
|
+
msafeAddress: input.msafeAddress
|
|
1392
|
+
});
|
|
1393
|
+
if (userMSafe === null) {
|
|
1394
|
+
throw new Error(`User (${input.userAddress}) does not have permission on MSafe account (${input.msafeAddress})`);
|
|
1395
|
+
}
|
|
1396
|
+
const payload = HexToUint8Array(pendingTx.payload);
|
|
1397
|
+
const verified = await SignatureVerifier.verifyTransactionSignature({
|
|
1398
|
+
payload,
|
|
1399
|
+
targetAddress: input.userAddress,
|
|
1400
|
+
signature: input.signature
|
|
1401
|
+
});
|
|
1402
|
+
if (!verified) {
|
|
1403
|
+
throw new Error("Invalid signature");
|
|
1404
|
+
}
|
|
1405
|
+
const rejectPendingTx = await this.model.pendingTransaction.findOneBy({
|
|
1406
|
+
msafeAddress: input.msafeAddress,
|
|
1407
|
+
sequenceNumber: pendingTx.sequenceNumber,
|
|
1408
|
+
isRejectTx: !pendingTx.isRejectTx
|
|
1409
|
+
});
|
|
1410
|
+
if (rejectPendingTx !== null) {
|
|
1411
|
+
await this.model.userVote.update(
|
|
1412
|
+
{
|
|
1413
|
+
userAddress: input.userAddress,
|
|
1414
|
+
txDigest: rejectPendingTx.digest,
|
|
1415
|
+
isValid: true
|
|
1416
|
+
},
|
|
1417
|
+
{ isValid: false }
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
const existVote = await this.model.userVote.exist({
|
|
1421
|
+
where: {
|
|
1422
|
+
txDigest: input.txDigest,
|
|
1423
|
+
userAddress: input.userAddress
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
if (!existVote) {
|
|
1427
|
+
const userVote = {
|
|
1428
|
+
txDigest: input.txDigest,
|
|
1429
|
+
userAddress: input.userAddress,
|
|
1430
|
+
msafeAddress: input.msafeAddress,
|
|
1431
|
+
signature: input.signature,
|
|
1432
|
+
isValid: true
|
|
1433
|
+
};
|
|
1434
|
+
await this.model.userVote.save(userVote);
|
|
1435
|
+
} else {
|
|
1436
|
+
await this.model.userVote.update(
|
|
1437
|
+
{
|
|
1438
|
+
txDigest: input.txDigest,
|
|
1439
|
+
userAddress: input.userAddress
|
|
1440
|
+
},
|
|
1441
|
+
{ isValid: true }
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
// Mock the process of an executed transaction
|
|
1446
|
+
// Here only the essential logic of transaction processing logic
|
|
1447
|
+
// is implemented. Need more transaction parsing from the fetcher
|
|
1448
|
+
// module.
|
|
1449
|
+
//
|
|
1450
|
+
// TODO: User queryRunner to make the transaction atomic.
|
|
1451
|
+
async processExecutedTransaction(digest) {
|
|
1452
|
+
const historyTx = await this.model.historyTransaction.findOneBy({ digest });
|
|
1453
|
+
if (historyTx) {
|
|
1454
|
+
const allHistories = await this.model.historyTransaction.findBy({
|
|
1455
|
+
msafeAddress: historyTx.msafeAddress
|
|
1456
|
+
});
|
|
1457
|
+
console.log(allHistories);
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
const pendingTx = await this.model.pendingTransaction.findOneBy({ digest });
|
|
1461
|
+
if (!pendingTx) {
|
|
1462
|
+
throw new Error("Transaction digest not found");
|
|
1463
|
+
}
|
|
1464
|
+
await this.model.transactionIntention.update(
|
|
1465
|
+
{ msafeAddress: pendingTx.msafeAddress, sequenceNumber: pendingTx.sequenceNumber },
|
|
1466
|
+
{ status: "processed" }
|
|
1467
|
+
);
|
|
1468
|
+
const pendings = await this.model.pendingTransaction.findBy({
|
|
1469
|
+
msafeAddress: pendingTx.msafeAddress,
|
|
1470
|
+
sequenceNumber: pendingTx.sequenceNumber
|
|
1471
|
+
});
|
|
1472
|
+
if (pendings.length === 0) {
|
|
1473
|
+
throw new Error("Pending transaction not found");
|
|
1474
|
+
}
|
|
1475
|
+
const executionResult = "success";
|
|
1476
|
+
for (let i = 0; i !== pendings.length; i++) {
|
|
1477
|
+
const pending = pendings[i];
|
|
1478
|
+
const history = {
|
|
1479
|
+
digest: pending.digest,
|
|
1480
|
+
payload: pending.payload,
|
|
1481
|
+
msafeAddress: pending.msafeAddress,
|
|
1482
|
+
sequenceNumber: pending.sequenceNumber,
|
|
1483
|
+
isRejectTx: pending.isRejectTx,
|
|
1484
|
+
creator: pending.creator,
|
|
1485
|
+
createdAt: pending.createdAt,
|
|
1486
|
+
// TODO: Fill in this field based on transaction processing result
|
|
1487
|
+
status: pending.digest === digest ? executionResult : "rejected"
|
|
1488
|
+
};
|
|
1489
|
+
await this.model.historyTransaction.save(history);
|
|
1490
|
+
await this.model.pendingTransaction.delete({ digest: pending.digest });
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Build the next transaction intention, and add the built transaction to pendings.
|
|
1495
|
+
* @param input
|
|
1496
|
+
*/
|
|
1497
|
+
async buildNextIntentionAndAddToPending(input) {
|
|
1498
|
+
const maxSNHistory = await this.model.historyTransaction.findOne({
|
|
1499
|
+
where: { msafeAddress: input.msafeAddress },
|
|
1500
|
+
order: { sequenceNumber: "desc" }
|
|
1501
|
+
});
|
|
1502
|
+
const nextSequenceNumber = maxSNHistory ? maxSNHistory.sequenceNumber + 1 : 0;
|
|
1503
|
+
const currentPendings = await this.model.pendingTransaction.findBy({
|
|
1504
|
+
msafeAddress: input.msafeAddress
|
|
1505
|
+
});
|
|
1506
|
+
if (currentPendings.length !== 0) {
|
|
1507
|
+
throw new Error("Still have pendings");
|
|
1508
|
+
}
|
|
1509
|
+
const nextTx = await this.model.transactionIntention.findOneBy({
|
|
1510
|
+
msafeAddress: input.msafeAddress,
|
|
1511
|
+
sequenceNumber: nextSequenceNumber
|
|
1512
|
+
});
|
|
1513
|
+
if (nextTx === null) {
|
|
1514
|
+
throw new Error("No future intentions to build");
|
|
1515
|
+
}
|
|
1516
|
+
const nextIntention = IntentionHelper.de(nextTx.data);
|
|
1517
|
+
try {
|
|
1518
|
+
const newTxb = await IntentionHelper.buildTxb({
|
|
1519
|
+
suiClient: this._suiClient,
|
|
1520
|
+
sender: input.msafeAddress,
|
|
1521
|
+
intention: nextIntention
|
|
1522
|
+
});
|
|
1523
|
+
const payload = await newTxb.build({ client: this._suiClient });
|
|
1524
|
+
const newDigest = await newTxb.getDigest({ client: this._suiClient });
|
|
1525
|
+
const newPending = {
|
|
1526
|
+
digest: newDigest,
|
|
1527
|
+
payload: Uint8ArrayToHex(payload),
|
|
1528
|
+
msafeAddress: input.msafeAddress,
|
|
1529
|
+
sequenceNumber: nextTx.sequenceNumber,
|
|
1530
|
+
isRejectTx: false,
|
|
1531
|
+
creator: nextTx.creator
|
|
1532
|
+
};
|
|
1533
|
+
await this.model.pendingTransaction.save(newPending);
|
|
1534
|
+
} catch (e) {
|
|
1535
|
+
await this.model.transactionIntention.update(
|
|
1536
|
+
{
|
|
1537
|
+
msafeAddress: input.msafeAddress,
|
|
1538
|
+
sequenceNumber: nextSequenceNumber
|
|
1539
|
+
},
|
|
1540
|
+
{
|
|
1541
|
+
status: "failed",
|
|
1542
|
+
statusRemark: e.toString()
|
|
1543
|
+
}
|
|
1544
|
+
);
|
|
1545
|
+
throw new Error(`Intention build failed: {sequenceNumber: ${nextSequenceNumber}}`);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Skip next failed transaction if build of the intention has been failed before.
|
|
1550
|
+
*/
|
|
1551
|
+
async skipNextFailedIntention(input) {
|
|
1552
|
+
const userMSafe = await this.model.userMSafe.findOneBy({
|
|
1553
|
+
msafeAddress: input.msafeAddress,
|
|
1554
|
+
userAddress: input.userAddress
|
|
1555
|
+
});
|
|
1556
|
+
if (userMSafe === null) {
|
|
1557
|
+
throw new Error("user does not have permission to MSafe");
|
|
1558
|
+
}
|
|
1559
|
+
const curSequenceNumber = await this.model.historyTransaction.findOne({
|
|
1560
|
+
where: { msafeAddress: input.msafeAddress },
|
|
1561
|
+
order: { sequenceNumber: "desc" }
|
|
1562
|
+
});
|
|
1563
|
+
const nextSequenceNumber = curSequenceNumber ? curSequenceNumber.sequenceNumber + 1 : 0;
|
|
1564
|
+
const failedIntention = await this.model.transactionIntention.findOneBy({
|
|
1565
|
+
msafeAddress: input.msafeAddress,
|
|
1566
|
+
sequenceNumber: nextSequenceNumber
|
|
1567
|
+
});
|
|
1568
|
+
if (failedIntention === null) {
|
|
1569
|
+
throw new Error("Next intention not found");
|
|
1570
|
+
}
|
|
1571
|
+
if (failedIntention.status !== "failed") {
|
|
1572
|
+
throw new Error("Next intention not failed");
|
|
1573
|
+
}
|
|
1574
|
+
const history = {
|
|
1575
|
+
msafeAddress: input.msafeAddress,
|
|
1576
|
+
digest: "0x0",
|
|
1577
|
+
// Special case for build has been failed,
|
|
1578
|
+
payload: "",
|
|
1579
|
+
sequenceNumber: nextSequenceNumber,
|
|
1580
|
+
creator: failedIntention.creator,
|
|
1581
|
+
isRejectTx: false,
|
|
1582
|
+
status: "build-failed"
|
|
1583
|
+
};
|
|
1584
|
+
await this.model.historyTransaction.save(history);
|
|
1585
|
+
}
|
|
1586
|
+
async getUser(address) {
|
|
1587
|
+
return this.model.user.findOneBy({ address });
|
|
1588
|
+
}
|
|
1589
|
+
get model() {
|
|
1590
|
+
return this.db.coreModel;
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
|
|
1594
|
+
// src/globals/const.ts
|
|
1595
|
+
var MSafeEnv = /* @__PURE__ */ ((MSafeEnv3) => {
|
|
1596
|
+
MSafeEnv3["local"] = "local";
|
|
1597
|
+
MSafeEnv3["unit"] = "unit";
|
|
1598
|
+
MSafeEnv3["dev"] = "dev";
|
|
1599
|
+
MSafeEnv3["prev"] = "prev";
|
|
1600
|
+
MSafeEnv3["prod"] = "prod";
|
|
1601
|
+
return MSafeEnv3;
|
|
1602
|
+
})(MSafeEnv || {});
|
|
1603
|
+
var LOCAL_DATABASE_CONFIG = {
|
|
1604
|
+
type: "mysql",
|
|
1605
|
+
host: "127.0.0.1",
|
|
1606
|
+
port: 3306,
|
|
1607
|
+
username: "msafe",
|
|
1608
|
+
password: "msafe",
|
|
1609
|
+
database: "msafe_sui_local",
|
|
1610
|
+
logging: false
|
|
1611
|
+
};
|
|
1612
|
+
var DEV_DATABASE_CONFIG = {
|
|
1613
|
+
type: "mysql",
|
|
1614
|
+
host: "msafe-dev-database.cluster-caos3ssocrx6.us-west-1.rds.amazonaws.com",
|
|
1615
|
+
port: 3306,
|
|
1616
|
+
username: "msafe",
|
|
1617
|
+
password: "Momentum.Safe2022",
|
|
1618
|
+
database: "msafe_sui_dev",
|
|
1619
|
+
logging: false
|
|
1620
|
+
};
|
|
1621
|
+
var TESTNET_RPC_URL = "https://sui-testnet.blockvision.org/v1/2Sgk89ivT64MnKdcGzjmyjY2ndD";
|
|
1622
|
+
var MAINNET_RPC_URL = "https://sui-mainnet.blockvision.org/v1/2Sgk7NPvqkd7mESYkxF01yX15l7";
|
|
1623
|
+
var ENV_CONFIGS = /* @__PURE__ */ new Map([
|
|
1624
|
+
[
|
|
1625
|
+
"unit" /* unit */,
|
|
1626
|
+
{
|
|
1627
|
+
suiClient: {
|
|
1628
|
+
url: TESTNET_RPC_URL
|
|
1629
|
+
},
|
|
1630
|
+
backend: LOCAL_DATABASE_CONFIG
|
|
1631
|
+
}
|
|
1632
|
+
],
|
|
1633
|
+
[
|
|
1634
|
+
"local" /* local */,
|
|
1635
|
+
{
|
|
1636
|
+
suiClient: {
|
|
1637
|
+
url: TESTNET_RPC_URL
|
|
1638
|
+
},
|
|
1639
|
+
backend: LOCAL_DATABASE_CONFIG
|
|
1640
|
+
}
|
|
1641
|
+
],
|
|
1642
|
+
[
|
|
1643
|
+
"dev" /* dev */,
|
|
1644
|
+
{
|
|
1645
|
+
suiClient: {
|
|
1646
|
+
url: TESTNET_RPC_URL
|
|
1647
|
+
},
|
|
1648
|
+
backend: DEV_DATABASE_CONFIG
|
|
1649
|
+
}
|
|
1650
|
+
]
|
|
1651
|
+
]);
|
|
1652
|
+
function getMSafeConfig(env, options) {
|
|
1653
|
+
const config = ENV_CONFIGS.get(env);
|
|
1654
|
+
if (!config) {
|
|
1655
|
+
throw new Error("Unknown environment");
|
|
1656
|
+
}
|
|
1657
|
+
if (options?.suiClient?.url) {
|
|
1658
|
+
config.suiClient.url = options.suiClient.url;
|
|
1659
|
+
}
|
|
1660
|
+
if (options?.backend) {
|
|
1661
|
+
config.backend = options.backend;
|
|
1662
|
+
}
|
|
1663
|
+
return config;
|
|
1664
|
+
}
|
|
1665
|
+
var AUTH_SIGN_MESSAGE = "Welcome to MSafe";
|
|
1666
|
+
|
|
1667
|
+
// src/globals/MSafeGlobals.ts
|
|
1668
|
+
var MSafeGlobals = class _MSafeGlobals {
|
|
1669
|
+
backend;
|
|
1670
|
+
suiClient;
|
|
1671
|
+
config;
|
|
1672
|
+
_wallet;
|
|
1673
|
+
constructor(input) {
|
|
1674
|
+
this.backend = input.backend;
|
|
1675
|
+
this.suiClient = input.suiClient;
|
|
1676
|
+
this.config = input.config;
|
|
1677
|
+
}
|
|
1678
|
+
static async New(env, options) {
|
|
1679
|
+
const config = getMSafeConfig(env, options);
|
|
1680
|
+
const suiClient = new import_client.SuiClient(config.suiClient);
|
|
1681
|
+
const backend = await PseudoBackend.New(config.backend, suiClient);
|
|
1682
|
+
return new _MSafeGlobals({
|
|
1683
|
+
backend,
|
|
1684
|
+
suiClient,
|
|
1685
|
+
config
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
connectWallet(wallet) {
|
|
1689
|
+
this._wallet = wallet;
|
|
1690
|
+
}
|
|
1691
|
+
get wallet() {
|
|
1692
|
+
if (!this._wallet) {
|
|
1693
|
+
throw new Error("wallet not connected");
|
|
1694
|
+
}
|
|
1695
|
+
return this._wallet;
|
|
1696
|
+
}
|
|
1697
|
+
set wallet(val) {
|
|
1698
|
+
this._wallet = val;
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
|
|
1702
|
+
// src/core/MSafeClient.ts
|
|
1703
|
+
var MSafeClient = class _MSafeClient {
|
|
1704
|
+
constructor(globals) {
|
|
1705
|
+
this.globals = globals;
|
|
1706
|
+
}
|
|
1707
|
+
_creationHelper;
|
|
1708
|
+
_publicKeyHelper;
|
|
1709
|
+
static async New(env, options) {
|
|
1710
|
+
const globals = await MSafeGlobals.New(env, options);
|
|
1711
|
+
return new _MSafeClient(globals);
|
|
1712
|
+
}
|
|
1713
|
+
async connectWallet(input) {
|
|
1714
|
+
this.globals.wallet = input.wallet;
|
|
1715
|
+
const isValidJWT = input.jwtToken && await this.backend.isJWTTokenValid(input.jwtToken);
|
|
1716
|
+
if (isValidJWT) {
|
|
1717
|
+
this.backend.setJWTToken(input.jwtToken);
|
|
1718
|
+
return input.jwtToken;
|
|
1719
|
+
}
|
|
1720
|
+
const messageStr = MessageHelper.welcomeMessage((/* @__PURE__ */ new Date()).toUTCString());
|
|
1721
|
+
const sig = await input.wallet.signPersonalMessage({
|
|
1722
|
+
messageStr
|
|
1723
|
+
});
|
|
1724
|
+
return this.backend.authSign({
|
|
1725
|
+
address: await input.wallet.address(),
|
|
1726
|
+
message: messageStr,
|
|
1727
|
+
signature: sig.signature,
|
|
1728
|
+
walletType: input.wallet.walletType
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
async userInfo() {
|
|
1732
|
+
return this.globals.backend.getUserInfo(await this.walletAddress());
|
|
1733
|
+
}
|
|
1734
|
+
async createAccount(info) {
|
|
1735
|
+
return this.creationHelper.submitMSafeCreation(info);
|
|
1736
|
+
}
|
|
1737
|
+
getMSafeAccount(info) {
|
|
1738
|
+
return new MSafeAccount(this.globals, info);
|
|
1739
|
+
}
|
|
1740
|
+
async getMSafeAccountFromAddress(msafeAddress) {
|
|
1741
|
+
const info = await this.backend.getMSafeAccountInfo(msafeAddress);
|
|
1742
|
+
return new MSafeAccount(this.globals, info);
|
|
1743
|
+
}
|
|
1744
|
+
// Get the creation helper to calculate the msafe account address,
|
|
1745
|
+
// validation, and more.
|
|
1746
|
+
get creationHelper() {
|
|
1747
|
+
if (!this._creationHelper) {
|
|
1748
|
+
this._creationHelper = new CreateHelper(this.globals, this.publicKeyHelper);
|
|
1749
|
+
}
|
|
1750
|
+
return this._creationHelper;
|
|
1751
|
+
}
|
|
1752
|
+
get publicKeyHelper() {
|
|
1753
|
+
if (!this._publicKeyHelper) {
|
|
1754
|
+
this._publicKeyHelper = new PublicKeyHelper(this.globals);
|
|
1755
|
+
}
|
|
1756
|
+
return this._publicKeyHelper;
|
|
1757
|
+
}
|
|
1758
|
+
get config() {
|
|
1759
|
+
return this.globals.config;
|
|
1760
|
+
}
|
|
1761
|
+
get backend() {
|
|
1762
|
+
return this.globals.backend;
|
|
1763
|
+
}
|
|
1764
|
+
get wallet() {
|
|
1765
|
+
return this.globals.wallet;
|
|
1766
|
+
}
|
|
1767
|
+
async walletAddress() {
|
|
1768
|
+
return this.wallet.address();
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1772
|
+
0 && (module.exports = {
|
|
1773
|
+
AUTH_SIGN_MESSAGE,
|
|
1774
|
+
COIN_TYPE_ARG_REGEX,
|
|
1775
|
+
Coin,
|
|
1776
|
+
CoinHelper,
|
|
1777
|
+
CreateHelper,
|
|
1778
|
+
DEV_DATABASE_CONFIG,
|
|
1779
|
+
ENV_CONFIGS,
|
|
1780
|
+
Formatter,
|
|
1781
|
+
HexToUint8Array,
|
|
1782
|
+
IntentionHelper,
|
|
1783
|
+
LOCAL_DATABASE_CONFIG,
|
|
1784
|
+
MAINNET_RPC_URL,
|
|
1785
|
+
MAX_OWNER_WITHOUT_NONCE,
|
|
1786
|
+
MAX_OWNER_WITH_NONCE,
|
|
1787
|
+
MAX_WEIGHT,
|
|
1788
|
+
MIN_THRESHOLD,
|
|
1789
|
+
MIN_WEIGHT,
|
|
1790
|
+
MSafeAccount,
|
|
1791
|
+
MSafeClient,
|
|
1792
|
+
MSafeEnv,
|
|
1793
|
+
MSafeGlobals,
|
|
1794
|
+
MessageHelper,
|
|
1795
|
+
NONCE_PK_PREFIX,
|
|
1796
|
+
NONCE_PK_WEIGHT,
|
|
1797
|
+
NONCE_PREFIX_MAX_SIZE,
|
|
1798
|
+
PublicKeySerde,
|
|
1799
|
+
RawMultiSig,
|
|
1800
|
+
SUI_COIN,
|
|
1801
|
+
SignatureVerifier,
|
|
1802
|
+
TESTNET_RPC_URL,
|
|
1803
|
+
Uint8ArrayToHex,
|
|
1804
|
+
getAllCoins,
|
|
1805
|
+
getMSafeConfig,
|
|
1806
|
+
getMultiSigPublicKey,
|
|
1807
|
+
getPublicKeyFromChain,
|
|
1808
|
+
makeNoncePublicKey,
|
|
1809
|
+
stringToBuffer,
|
|
1810
|
+
validateMultiSigConfig
|
|
1811
|
+
});
|
|
1812
|
+
//# sourceMappingURL=index.cjs.map
|