@msafe/sui3-sdk 0.0.2-pre-9f39791.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +3 -0
- package/.eslintrc +87 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/jsLinters/eslint.xml +6 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/msafe-sui3-sdk.iml +9 -0
- package/.idea/vcs.xml +6 -0
- package/.prettierrc +22 -0
- package/README.md +3 -0
- package/jest.config.ts +63 -0
- package/package.json +54 -0
- package/scripts/prerelease.sh +5 -0
- package/src/backend/CoreDatabase.ts +55 -0
- package/src/backend/PseudoBackend.ts +851 -0
- package/src/backend/interface.ts +57 -0
- package/src/backend/types.ts +22 -0
- package/src/core/CreateHelper.ts +76 -0
- package/src/core/MSafeAccount.ts +167 -0
- package/src/core/MSafeClient.ts +91 -0
- package/src/core/MessageHelper.ts +63 -0
- package/src/core/PublicKeyHelper.ts +88 -0
- package/src/core/index.ts +4 -0
- package/src/globals/MSafeGlobals.ts +48 -0
- package/src/globals/const.ts +95 -0
- package/src/globals/index.ts +2 -0
- package/src/index.ts +5 -0
- package/src/transactions/coin-transfer.ts +64 -0
- package/src/transactions/index.ts +1 -0
- package/src/transactions/intention.ts +63 -0
- package/src/transactions/object-transfer.ts +66 -0
- package/src/transactions/reject.ts +17 -0
- package/src/transactions/stream.ts +1 -0
- package/src/types/creation.ts +19 -0
- package/src/types/index.ts +3 -0
- package/src/types/msafe.ts +79 -0
- package/src/types/wallet.ts +23 -0
- package/src/utils/buffer.ts +11 -0
- package/src/utils/coin.ts +64 -0
- package/src/utils/crypto.ts +95 -0
- package/src/utils/format.ts +25 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/multi-sig.ts +113 -0
- package/src/utils/sui.ts +90 -0
- package/temp_package.json +54 -0
- package/test/lib/TestHelper.ts +94 -0
- package/test/lib/account.ts +87 -0
- package/test/lib/config.ts +9 -0
- package/test/lib/faucet.ts +49 -0
- package/test/unit/backend/backend.test.ts +367 -0
- package/test/unit/core/CreateHelper.test.ts +121 -0
- package/test/unit/core/MessageHelper.test.ts +32 -0
- package/test/unit/core/PublicKeyHelper.test.ts +80 -0
- package/test/unit/core/msafe.test.ts +298 -0
- package/test/unit/utils/buffer.test.ts +17 -0
- package/test/unit/utils/crypto.test.ts +32 -0
- package/test/unit/utils/multi-sig.test.ts +47 -0
- package/test/unit/utils/sui.test.ts +25 -0
- package/tsconfig.json +37 -0
- package/tsup.config.ts +9 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
|
|
4
|
+
import { CoreModel, PendingTransaction } from '@msafe/sui3-model/core';
|
|
5
|
+
|
|
6
|
+
import { WALLET_TYPE_KEY } from '@/backend/CoreDatabase';
|
|
7
|
+
import { PseudoBackend } from '@/backend/PseudoBackend';
|
|
8
|
+
import { MessageHelper } from '@/core/MessageHelper';
|
|
9
|
+
import { MSafeEnv } from '@/globals/const';
|
|
10
|
+
import { MSafeGlobals } from '@/globals/MSafeGlobals';
|
|
11
|
+
import { IntentionCoinTransfer } from '@/transactions/coin-transfer';
|
|
12
|
+
import { IntentionHelper, TxIntention } from '@/transactions/intention';
|
|
13
|
+
import { HexToUint8Array } from '@/utils/buffer';
|
|
14
|
+
import { SUI_COIN } from '@/utils/sui';
|
|
15
|
+
|
|
16
|
+
import { LocalWallet } from '../../lib/account';
|
|
17
|
+
import { TESTNET_SUI_CLIENT } from '../../lib/config';
|
|
18
|
+
import { TestHelper } from '../../lib/TestHelper';
|
|
19
|
+
|
|
20
|
+
// TODO: Move these tests cases to backend code.
|
|
21
|
+
describe('PseudoBackend', () => {
|
|
22
|
+
let backend: PseudoBackend;
|
|
23
|
+
let globals: MSafeGlobals;
|
|
24
|
+
let testHelper: TestHelper;
|
|
25
|
+
const testWallet = new LocalWallet(TESTNET_SUI_CLIENT);
|
|
26
|
+
|
|
27
|
+
beforeAll(async () => {
|
|
28
|
+
globals = await MSafeGlobals.New(MSafeEnv.unit);
|
|
29
|
+
testHelper = new TestHelper(globals);
|
|
30
|
+
backend = globals.backend as PseudoBackend;
|
|
31
|
+
await testHelper.init();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterAll(async () => {
|
|
35
|
+
await testHelper.close();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('authSign success', async () => {
|
|
39
|
+
const message = MessageHelper.welcomeMessage(new Date().toUTCString());
|
|
40
|
+
const sig = await testWallet.signPersonalMessage({
|
|
41
|
+
messageStr: message,
|
|
42
|
+
});
|
|
43
|
+
await backend.authSign({
|
|
44
|
+
address: await testWallet.address(),
|
|
45
|
+
message,
|
|
46
|
+
signature: sig.signature,
|
|
47
|
+
walletType: 'Unknown',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const userSetting = await backend.db.coreModel.userSetting.findOneBy({
|
|
51
|
+
userAddress: await testWallet.address(),
|
|
52
|
+
key: WALLET_TYPE_KEY,
|
|
53
|
+
});
|
|
54
|
+
expect(userSetting).not.toBeNull();
|
|
55
|
+
expect(userSetting?.value).toBe('Unknown');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('Invalid welcome message', async () => {
|
|
59
|
+
const message = 'User Sign';
|
|
60
|
+
const sig = await testWallet.signPersonalMessage({
|
|
61
|
+
messageStr: message,
|
|
62
|
+
});
|
|
63
|
+
await expect(
|
|
64
|
+
backend.authSign({
|
|
65
|
+
address: await testWallet.address(),
|
|
66
|
+
message: 'User sign',
|
|
67
|
+
signature: sig.signature,
|
|
68
|
+
walletType: 'Unknown',
|
|
69
|
+
}),
|
|
70
|
+
).rejects.toThrow('Invalid welcome message');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('user info no wallet', async () => {
|
|
74
|
+
const user = await backend.getUserInfo(await testWallet.address());
|
|
75
|
+
expect(user).toBeDefined();
|
|
76
|
+
expect(user.address).toBe(await testWallet.address());
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('public key', async () => {
|
|
80
|
+
const got = await backend.getPublicKey(await testWallet.address());
|
|
81
|
+
expect(got!.toSuiAddress()).toEqual(await testWallet.address());
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('public key not found', async () => {
|
|
85
|
+
expect(await backend.getPublicKey('0x1234')).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('create MSafe', async () => {
|
|
89
|
+
const wallets = [new LocalWallet(globals.suiClient), new LocalWallet(globals.suiClient)];
|
|
90
|
+
const msafeAddr = await testHelper.createMSafe(wallets);
|
|
91
|
+
const msafe = await backend.db.coreModel.msafe.findOneBy({ address: msafeAddr });
|
|
92
|
+
expect(msafe !== null).toBeTruthy();
|
|
93
|
+
|
|
94
|
+
const userInfo = await backend.getUserInfo(await wallets[0].address());
|
|
95
|
+
expect(userInfo.ownedMSafe.length).toBe(1);
|
|
96
|
+
expect(userInfo.creationNonce).toBe(1);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('PseudoBackend transaction related', () => {
|
|
101
|
+
let globals: MSafeGlobals;
|
|
102
|
+
let backend: PseudoBackend;
|
|
103
|
+
let testHelper: TestHelper;
|
|
104
|
+
let testWallets: LocalWallet[];
|
|
105
|
+
let msafeAddress: string;
|
|
106
|
+
let coreModel: CoreModel;
|
|
107
|
+
let userAddress: string;
|
|
108
|
+
let userWallet: LocalWallet;
|
|
109
|
+
|
|
110
|
+
beforeAll(async () => {
|
|
111
|
+
globals = await MSafeGlobals.New(MSafeEnv.unit);
|
|
112
|
+
testHelper = new TestHelper(globals);
|
|
113
|
+
backend = globals.backend as PseudoBackend;
|
|
114
|
+
|
|
115
|
+
testWallets = Array.from({ length: 3 }, () => new LocalWallet(globals.suiClient));
|
|
116
|
+
await testHelper.init(testWallets[0]);
|
|
117
|
+
msafeAddress = await testHelper.createMSafe(testWallets);
|
|
118
|
+
|
|
119
|
+
await testHelper.faucetWallet(msafeAddress, 100000n);
|
|
120
|
+
|
|
121
|
+
coreModel = backend.db.coreModel;
|
|
122
|
+
userAddress = await testWallets[0].address();
|
|
123
|
+
[userWallet] = testWallets;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
afterAll(async () => {
|
|
127
|
+
await testHelper.close();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('Initialization status check', async () => {
|
|
131
|
+
const histories = await coreModel.historyTransaction.findBy({ msafeAddress });
|
|
132
|
+
expect(histories.length).toBe(0);
|
|
133
|
+
|
|
134
|
+
const pendings = await coreModel.pendingTransaction.findBy({ msafeAddress });
|
|
135
|
+
expect(pendings.length).toBe(0);
|
|
136
|
+
|
|
137
|
+
const intentions = await coreModel.transactionIntention.findBy({ msafeAddress });
|
|
138
|
+
expect(intentions.length).toBe(0);
|
|
139
|
+
|
|
140
|
+
const votes = await coreModel.userVote.findBy({ msafeAddress });
|
|
141
|
+
expect(votes.length).toBe(0);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('Propose an intention', async () => {
|
|
145
|
+
const testIntention: TxIntention = {
|
|
146
|
+
txType: 'CoinTransfer',
|
|
147
|
+
recipient: await testWallets[0].address(),
|
|
148
|
+
amount: '100',
|
|
149
|
+
coinType: SUI_COIN,
|
|
150
|
+
};
|
|
151
|
+
const message = MessageHelper.proposeIntentionMessage({ intention: testIntention, sn: 0 });
|
|
152
|
+
const signature = await testWallets[0].signPersonalMessage({ messageStr: message });
|
|
153
|
+
await backend.proposeIntention({
|
|
154
|
+
intention: testIntention,
|
|
155
|
+
sequenceNumber: 0,
|
|
156
|
+
userAddress,
|
|
157
|
+
msafeAddress,
|
|
158
|
+
signature: signature.signature,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
await backend.buildNextIntentionAndAddToPending({ msafeAddress });
|
|
162
|
+
|
|
163
|
+
const pendings = await coreModel.pendingTransaction.findBy({ msafeAddress });
|
|
164
|
+
expect(pendings.length).toBe(1);
|
|
165
|
+
const intentions = await coreModel.transactionIntention.findBy({ msafeAddress });
|
|
166
|
+
expect(intentions.length).toBe(1);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('Propose more intention will add to intention not pendings', async () => {
|
|
170
|
+
const testIntention: IntentionCoinTransfer = {
|
|
171
|
+
txType: 'CoinTransfer',
|
|
172
|
+
recipient: await testWallets[0].address(),
|
|
173
|
+
amount: '200',
|
|
174
|
+
coinType: SUI_COIN,
|
|
175
|
+
};
|
|
176
|
+
const message2 = MessageHelper.proposeIntentionMessage({ intention: testIntention, sn: 1 });
|
|
177
|
+
const signature2 = await testWallets[0].signPersonalMessage({ messageStr: message2 });
|
|
178
|
+
await backend.proposeIntention({
|
|
179
|
+
intention: testIntention,
|
|
180
|
+
sequenceNumber: 1,
|
|
181
|
+
userAddress,
|
|
182
|
+
msafeAddress,
|
|
183
|
+
signature: signature2.signature,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const intentions = await coreModel.transactionIntention.findBy({ msafeAddress });
|
|
187
|
+
expect(intentions.length).toBe(2);
|
|
188
|
+
const pendings = await coreModel.pendingTransaction.findBy({ msafeAddress });
|
|
189
|
+
expect(pendings.length).toBe(1);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('Vote for transaction', async () => {
|
|
193
|
+
const pending = await coreModel.pendingTransaction.findOneBy({ msafeAddress });
|
|
194
|
+
if (pending === null) {
|
|
195
|
+
throw new Error('Null pending');
|
|
196
|
+
}
|
|
197
|
+
const payload = HexToUint8Array(pending.payload);
|
|
198
|
+
const signature = await testWallets[0].signTransactionBlock({ transactionBlock: payload });
|
|
199
|
+
await backend.voteForTransaction({
|
|
200
|
+
msafeAddress,
|
|
201
|
+
userAddress,
|
|
202
|
+
txDigest: pending.digest,
|
|
203
|
+
signature: signature.signature,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Check votes, user shall have 1 valid vote.
|
|
207
|
+
const userVotes = await coreModel.userVote.findBy({
|
|
208
|
+
userAddress,
|
|
209
|
+
msafeAddress,
|
|
210
|
+
isValid: true,
|
|
211
|
+
});
|
|
212
|
+
expect(userVotes.length).toBe(1);
|
|
213
|
+
expect(userVotes[0].msafeAddress).toBe(msafeAddress);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('rejectCurrentTx', async () => {
|
|
217
|
+
const pendingsTxs = await backend.getPendingTransactions(msafeAddress);
|
|
218
|
+
const payloadToReject = pendingsTxs[0].payload;
|
|
219
|
+
const txb = await IntentionHelper.buildRejectTransaction({ msafeAddress, payloadToReject });
|
|
220
|
+
const digest = await txb.getDigest({ client: globals.suiClient });
|
|
221
|
+
const signature = await testWallets[0].signTransactionBlock({ transactionBlock: txb });
|
|
222
|
+
await backend.rejectCurrentTx({
|
|
223
|
+
msafeAddress,
|
|
224
|
+
userAddress,
|
|
225
|
+
digest,
|
|
226
|
+
signature: signature.signature,
|
|
227
|
+
});
|
|
228
|
+
const pendings = await coreModel.pendingTransaction.findBy({ msafeAddress });
|
|
229
|
+
expect(pendings.length).toBe(2);
|
|
230
|
+
expect(pendings.find((pending) => pending.isRejectTx)).toBeDefined();
|
|
231
|
+
expect(pendings.find((pending) => !pending.isRejectTx)).toBeDefined();
|
|
232
|
+
|
|
233
|
+
const rejectTx = pendings.find((pending) => pending.isRejectTx) as PendingTransaction;
|
|
234
|
+
const rejectVote = await coreModel.userVote.findOneBy({
|
|
235
|
+
userAddress,
|
|
236
|
+
msafeAddress,
|
|
237
|
+
txDigest: rejectTx.digest,
|
|
238
|
+
isValid: true,
|
|
239
|
+
});
|
|
240
|
+
expect(rejectVote === null).toBeFalsy();
|
|
241
|
+
|
|
242
|
+
// Approve vote need to be invalid
|
|
243
|
+
const approveTx = pendings.find((pending) => !pending.isRejectTx) as PendingTransaction;
|
|
244
|
+
const invalidApprove = await coreModel.userVote.findOneBy({
|
|
245
|
+
txDigest: approveTx.digest,
|
|
246
|
+
userAddress,
|
|
247
|
+
msafeAddress,
|
|
248
|
+
});
|
|
249
|
+
expect(invalidApprove === null).toBeFalsy();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('vote overwrite', async () => {
|
|
253
|
+
const pending = await coreModel.pendingTransaction.findOneBy({
|
|
254
|
+
msafeAddress,
|
|
255
|
+
isRejectTx: false,
|
|
256
|
+
});
|
|
257
|
+
if (pending === null) {
|
|
258
|
+
throw new Error('pending tx not exist');
|
|
259
|
+
}
|
|
260
|
+
const { signature } = await userWallet.signTransactionBlock({ transactionBlock: HexToUint8Array(pending.payload) });
|
|
261
|
+
await backend.voteForTransaction({
|
|
262
|
+
txDigest: pending.digest,
|
|
263
|
+
msafeAddress,
|
|
264
|
+
userAddress,
|
|
265
|
+
signature,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const vote = await coreModel.userVote.findOneBy({
|
|
269
|
+
msafeAddress,
|
|
270
|
+
txDigest: pending.digest,
|
|
271
|
+
});
|
|
272
|
+
expect(vote).not.toBeNull();
|
|
273
|
+
expect(vote!.isValid).toBeTruthy();
|
|
274
|
+
|
|
275
|
+
// Another pending transaction with isRejectTx = true, shall have
|
|
276
|
+
// user vote with isValid = false.
|
|
277
|
+
const rejectPending = await coreModel.pendingTransaction.findOneBy({
|
|
278
|
+
msafeAddress,
|
|
279
|
+
isRejectTx: true,
|
|
280
|
+
});
|
|
281
|
+
if (rejectPending === null) {
|
|
282
|
+
throw new Error('Test cases must execute in sequence');
|
|
283
|
+
}
|
|
284
|
+
const userVote = await coreModel.userVote.findOneBy({
|
|
285
|
+
msafeAddress,
|
|
286
|
+
userAddress,
|
|
287
|
+
txDigest: rejectPending.digest,
|
|
288
|
+
});
|
|
289
|
+
expect(userVote).not.toBeNull();
|
|
290
|
+
expect(userVote!.isValid).toBeFalsy();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('processExecutedTransaction', async () => {
|
|
294
|
+
const oldPendings = await coreModel.pendingTransaction.findBy({
|
|
295
|
+
msafeAddress,
|
|
296
|
+
});
|
|
297
|
+
expect(oldPendings.length).toBe(2);
|
|
298
|
+
|
|
299
|
+
const executedPending = oldPendings.find((pending) => pending.isRejectTx);
|
|
300
|
+
const rejectedPending = oldPendings.find((pending) => !pending.isRejectTx);
|
|
301
|
+
expect(executedPending).toBeDefined();
|
|
302
|
+
expect(rejectedPending).toBeDefined();
|
|
303
|
+
if (!executedPending || !rejectedPending) {
|
|
304
|
+
throw new Error('pendings not found');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Process one transaction.
|
|
308
|
+
await backend.processExecutedTransaction(executedPending.digest);
|
|
309
|
+
await backend.buildNextIntentionAndAddToPending({ msafeAddress });
|
|
310
|
+
|
|
311
|
+
// Transaction has been added to history
|
|
312
|
+
const histories = await coreModel.historyTransaction.findBy({ msafeAddress });
|
|
313
|
+
expect(histories.length).toBe(2);
|
|
314
|
+
expect(
|
|
315
|
+
histories.find((history) => history.status === 'success' && history.digest === executedPending.digest),
|
|
316
|
+
).toBeDefined();
|
|
317
|
+
expect(
|
|
318
|
+
histories.find((history) => history.status === 'rejected' && history.digest === rejectedPending.digest),
|
|
319
|
+
).toBeDefined();
|
|
320
|
+
|
|
321
|
+
// Intentions has been updated
|
|
322
|
+
const intentions = await coreModel.transactionIntention.findBy({
|
|
323
|
+
msafeAddress,
|
|
324
|
+
sequenceNumber: executedPending.sequenceNumber,
|
|
325
|
+
});
|
|
326
|
+
expect(intentions.length).toBe(1);
|
|
327
|
+
expect(intentions[0].status).toBe('processed');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('proposePendingTransaction', async () => {
|
|
331
|
+
const testIntention: IntentionCoinTransfer = {
|
|
332
|
+
txType: 'CoinTransfer',
|
|
333
|
+
recipient: await testWallets[0].address(),
|
|
334
|
+
amount: '300',
|
|
335
|
+
coinType: SUI_COIN,
|
|
336
|
+
};
|
|
337
|
+
const pendings = await coreModel.pendingTransaction.findBy({ msafeAddress });
|
|
338
|
+
expect(pendings.length).toBe(1);
|
|
339
|
+
await backend.processExecutedTransaction(pendings[0].digest);
|
|
340
|
+
const txb = await IntentionHelper.buildTxb({
|
|
341
|
+
suiClient: globals.suiClient,
|
|
342
|
+
intention: testIntention,
|
|
343
|
+
sender: msafeAddress,
|
|
344
|
+
});
|
|
345
|
+
const digest = await txb.getDigest({ client: globals.suiClient });
|
|
346
|
+
const signature = await userWallet.signTransactionBlock({ transactionBlock: txb });
|
|
347
|
+
await backend.proposePendingTransaction({
|
|
348
|
+
intention: testIntention,
|
|
349
|
+
userAddress,
|
|
350
|
+
msafeAddress,
|
|
351
|
+
digest,
|
|
352
|
+
signature: signature.signature,
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const intentions = await coreModel.transactionIntention.findBy({
|
|
356
|
+
msafeAddress,
|
|
357
|
+
sequenceNumber: pendings[0].sequenceNumber + 1,
|
|
358
|
+
});
|
|
359
|
+
expect(intentions.length).toBe(1);
|
|
360
|
+
|
|
361
|
+
const newPendings = await coreModel.pendingTransaction.findBy({
|
|
362
|
+
msafeAddress,
|
|
363
|
+
});
|
|
364
|
+
expect(newPendings.length).toBe(1);
|
|
365
|
+
expect(newPendings[0].sequenceNumber).toBe(pendings[0].sequenceNumber + 1);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { PseudoBackend } from '@/backend/PseudoBackend';
|
|
2
|
+
import { CreateHelper } from '@/core/CreateHelper';
|
|
3
|
+
import { PublicKeyHelper } from '@/core/PublicKeyHelper';
|
|
4
|
+
import { MSafeEnv } from '@/globals/const';
|
|
5
|
+
import { MSafeGlobals } from '@/globals/MSafeGlobals';
|
|
6
|
+
import { CreateMSafeAccountInfo } from '@/types/creation';
|
|
7
|
+
|
|
8
|
+
import { LocalWallet } from '../../lib/account';
|
|
9
|
+
import { TESTNET_SUI_CLIENT } from '../../lib/config';
|
|
10
|
+
import { TestHelper } from '../../lib/TestHelper';
|
|
11
|
+
|
|
12
|
+
describe('MSafeCreationHelper', () => {
|
|
13
|
+
let createHelper: CreateHelper;
|
|
14
|
+
let testHelper: TestHelper;
|
|
15
|
+
|
|
16
|
+
const localWallet = new LocalWallet(TESTNET_SUI_CLIENT);
|
|
17
|
+
const coManagerAddress = '0x6e78327c80f18968c5227af0dc1a7637822f2f76cb78adbdc0454e8abe66b235';
|
|
18
|
+
|
|
19
|
+
beforeAll(async () => {
|
|
20
|
+
const globals = await MSafeGlobals.New(MSafeEnv.unit);
|
|
21
|
+
testHelper = new TestHelper(globals);
|
|
22
|
+
await testHelper.init(localWallet);
|
|
23
|
+
|
|
24
|
+
const pkHelper = new PublicKeyHelper(globals);
|
|
25
|
+
createHelper = new CreateHelper(globals, pkHelper);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterAll(async () => {
|
|
29
|
+
await testHelper.close();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('validate success', async () => {
|
|
33
|
+
const info: CreateMSafeAccountInfo = {
|
|
34
|
+
ownerWithWeight: [
|
|
35
|
+
{ address: await localWallet.address(), weight: 2 },
|
|
36
|
+
{ address: coManagerAddress, weight: 1 },
|
|
37
|
+
],
|
|
38
|
+
threshold: 2,
|
|
39
|
+
name: 'test wallet',
|
|
40
|
+
creationNonce: 1,
|
|
41
|
+
};
|
|
42
|
+
await createHelper.validateCreateInfo(info);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('unknown public key', async () => {
|
|
46
|
+
const info: CreateMSafeAccountInfo = {
|
|
47
|
+
ownerWithWeight: [{ address: '0x123123123', weight: 1 }],
|
|
48
|
+
threshold: 1,
|
|
49
|
+
name: 'test wallet',
|
|
50
|
+
creationNonce: 1,
|
|
51
|
+
};
|
|
52
|
+
await expect(createHelper.validateCreateInfo(info)).rejects.toThrow('Unknown public key for address');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('duplicate address', async () => {
|
|
56
|
+
const info: CreateMSafeAccountInfo = {
|
|
57
|
+
ownerWithWeight: [
|
|
58
|
+
{ address: await localWallet.address(), weight: 1 },
|
|
59
|
+
{ address: await localWallet.address(), weight: 3 },
|
|
60
|
+
],
|
|
61
|
+
threshold: 1,
|
|
62
|
+
name: 'test wallet',
|
|
63
|
+
creationNonce: 1,
|
|
64
|
+
};
|
|
65
|
+
await expect(createHelper.validateCreateInfo(info)).rejects.toThrow('Duplicate address detected');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('invalid weight', async () => {
|
|
69
|
+
const info: CreateMSafeAccountInfo = {
|
|
70
|
+
ownerWithWeight: [
|
|
71
|
+
{ address: await localWallet.address(), weight: 256 },
|
|
72
|
+
{ address: coManagerAddress, weight: 1 },
|
|
73
|
+
],
|
|
74
|
+
threshold: 2,
|
|
75
|
+
name: 'test wallet',
|
|
76
|
+
creationNonce: 1,
|
|
77
|
+
};
|
|
78
|
+
await expect(createHelper.validateCreateInfo(info)).rejects.toThrow('Invalid multi-sig weight: 256 (1-255)');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('invalid threshold', async () => {
|
|
82
|
+
const info: CreateMSafeAccountInfo = {
|
|
83
|
+
ownerWithWeight: [
|
|
84
|
+
{ address: await localWallet.address(), weight: 2 },
|
|
85
|
+
{ address: coManagerAddress, weight: 1 },
|
|
86
|
+
],
|
|
87
|
+
threshold: 5,
|
|
88
|
+
name: 'test wallet',
|
|
89
|
+
creationNonce: 1,
|
|
90
|
+
};
|
|
91
|
+
await expect(createHelper.validateCreateInfo(info)).rejects.toThrow('Threshold is larger than total weight');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('submit creation', async () => {
|
|
95
|
+
const info: CreateMSafeAccountInfo = {
|
|
96
|
+
ownerWithWeight: [
|
|
97
|
+
{ address: await localWallet.address(), weight: 2 },
|
|
98
|
+
{ address: coManagerAddress, weight: 1 },
|
|
99
|
+
],
|
|
100
|
+
threshold: 2,
|
|
101
|
+
name: 'test wallet',
|
|
102
|
+
creationNonce: 0,
|
|
103
|
+
};
|
|
104
|
+
await createHelper.submitMSafeCreation(info);
|
|
105
|
+
|
|
106
|
+
const msafeAddress = await createHelper.calculateMSafeAddress(info);
|
|
107
|
+
const db = createHelper.globals.backend as PseudoBackend;
|
|
108
|
+
const msafeAccountInfo = await db.getMSafeAccountInfo(msafeAddress);
|
|
109
|
+
expect(msafeAccountInfo).toBeDefined();
|
|
110
|
+
expect(msafeAccountInfo?.name).toBe(info.name);
|
|
111
|
+
expect(msafeAccountInfo?.threshold).toBe(info.threshold);
|
|
112
|
+
expect(msafeAccountInfo?.ownersWithWeightPK.length).toBe(info.ownerWithWeight.length);
|
|
113
|
+
expect(msafeAccountInfo?.ownersWithWeightPK[0].address).toBe(await localWallet.address());
|
|
114
|
+
expect(msafeAccountInfo?.ownersWithWeightPK[0].weight).toBe(2);
|
|
115
|
+
expect(msafeAccountInfo?.ownersWithWeightPK[1].address).toBe(coManagerAddress);
|
|
116
|
+
expect(msafeAccountInfo?.ownersWithWeightPK[1].weight).toBe(1);
|
|
117
|
+
|
|
118
|
+
const res = await db.getUserInfo(await localWallet.address());
|
|
119
|
+
expect(res.creationNonce).toBe(1);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { MessageHelper, ProposeIntentionData } from '@/core/MessageHelper';
|
|
2
|
+
|
|
3
|
+
describe('MessageHelper', () => {
|
|
4
|
+
it('create message', () => {
|
|
5
|
+
const addr = '0x123123';
|
|
6
|
+
const msg = MessageHelper.createMSafeMessage(addr);
|
|
7
|
+
const got = MessageHelper.deCreateMSafeMessage(msg);
|
|
8
|
+
expect(addr).toEqual(got);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('welcome message', () => {
|
|
12
|
+
const timeStr = new Date().toDateString();
|
|
13
|
+
const msg = MessageHelper.welcomeMessage(timeStr);
|
|
14
|
+
const got = MessageHelper.deWelcomeMessage(msg);
|
|
15
|
+
expect(timeStr).toEqual(got);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('proposeIntentMessage', async () => {
|
|
19
|
+
const proposeData: ProposeIntentionData = {
|
|
20
|
+
intention: {
|
|
21
|
+
txType: 'CoinTransfer',
|
|
22
|
+
recipient: '0x123',
|
|
23
|
+
coinType: '0x2::sui::Sui',
|
|
24
|
+
amount: '100',
|
|
25
|
+
},
|
|
26
|
+
sn: 100,
|
|
27
|
+
};
|
|
28
|
+
const msg = MessageHelper.proposeIntentionMessage(proposeData);
|
|
29
|
+
const got = MessageHelper.deProposeIntentionMessage(msg);
|
|
30
|
+
expect(proposeData).toEqual(got);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { PublicKeyHelper } from '@/core/PublicKeyHelper';
|
|
2
|
+
import { MSafeEnv } from '@/globals/const';
|
|
3
|
+
import { MSafeGlobals } from '@/globals/MSafeGlobals';
|
|
4
|
+
import { Formatter } from '@/utils/format';
|
|
5
|
+
|
|
6
|
+
import { LocalWallet } from '../../lib/account';
|
|
7
|
+
import { TESTNET_SUI_CLIENT } from '../../lib/config';
|
|
8
|
+
import { TestHelper } from '../../lib/TestHelper';
|
|
9
|
+
|
|
10
|
+
describe('PublicKeyHelper', () => {
|
|
11
|
+
let globals: MSafeGlobals;
|
|
12
|
+
let pkHelper: PublicKeyHelper;
|
|
13
|
+
let testHelper: TestHelper;
|
|
14
|
+
|
|
15
|
+
const localWallet = new LocalWallet(TESTNET_SUI_CLIENT);
|
|
16
|
+
const chainAddress = '0x6e78327c80f18968c5227af0dc1a7637822f2f76cb78adbdc0454e8abe66b235';
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
globals = await MSafeGlobals.New(MSafeEnv.unit);
|
|
20
|
+
testHelper = new TestHelper(globals);
|
|
21
|
+
await testHelper.init(localWallet);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterAll(async () => {
|
|
25
|
+
await testHelper.close();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
pkHelper = new PublicKeyHelper(globals);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('getPublicKey from blockchain', async () => {
|
|
33
|
+
const publicKey = await pkHelper.getPublicKey(chainAddress);
|
|
34
|
+
expect(publicKey).toBeDefined();
|
|
35
|
+
expect(Formatter.isSuiAddressEqual(publicKey!.toSuiAddress(), chainAddress)).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('getPublicKey with wallet', async () => {
|
|
39
|
+
const publicKey = await pkHelper.getPublicKey(await localWallet.address());
|
|
40
|
+
expect(publicKey).toBeDefined();
|
|
41
|
+
expect(Formatter.isSuiAddressEqual(publicKey!.toSuiAddress(), await localWallet.address())).toBeTruthy();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('getPublicKeyBatch', async () => {
|
|
45
|
+
const pks = await pkHelper.getPublicKeyBatch([await localWallet.address(), chainAddress]);
|
|
46
|
+
expect(pks.length).toBe(2);
|
|
47
|
+
for (let i = 0; i < pks.length; i++) {
|
|
48
|
+
expect(pks[i]).toBeDefined();
|
|
49
|
+
expect(Formatter.isSuiAddressEqual(pks[i]!.toSuiAddress(), await localWallet.address()));
|
|
50
|
+
}
|
|
51
|
+
expect((pkHelper as any).knownPublicKeys.size).toBe(2);
|
|
52
|
+
|
|
53
|
+
const pks2 = await pkHelper.getPublicKeyBatch([await localWallet.address(), chainAddress]);
|
|
54
|
+
expect(pks2.length).toBe(2);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('getPublicKeyBatch with uncached backend values', async () => {
|
|
58
|
+
await pkHelper.getPublicKey(chainAddress);
|
|
59
|
+
expect((pkHelper as any).knownPublicKeys.size).toBe(1);
|
|
60
|
+
|
|
61
|
+
const pks = await pkHelper.getPublicKeyBatch([await localWallet.address(), chainAddress]);
|
|
62
|
+
expect(pks.length).toBe(2);
|
|
63
|
+
expect((pkHelper as any).knownPublicKeys.size).toBe(2);
|
|
64
|
+
|
|
65
|
+
expect(Formatter.isSuiAddressEqual(pks[0]?.toSuiAddress() as string, await localWallet.address())).toBeTruthy();
|
|
66
|
+
expect(Formatter.isSuiAddressEqual(pks[1]?.toSuiAddress() as string, chainAddress)).toBeTruthy();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('getPublicKeyBatch with uncached chain values', async () => {
|
|
70
|
+
await pkHelper.getPublicKey(await localWallet.address());
|
|
71
|
+
expect((pkHelper as any).knownPublicKeys.size).toBe(1);
|
|
72
|
+
|
|
73
|
+
const pks = await pkHelper.getPublicKeyBatch([chainAddress, await localWallet.address()]);
|
|
74
|
+
expect(pks.length).toBe(2);
|
|
75
|
+
expect((pkHelper as any).knownPublicKeys.size).toBe(2);
|
|
76
|
+
|
|
77
|
+
expect(Formatter.isSuiAddressEqual(pks[0]?.toSuiAddress() as string, chainAddress)).toBeTruthy();
|
|
78
|
+
expect(Formatter.isSuiAddressEqual(pks[1]?.toSuiAddress() as string, await localWallet.address())).toBeTruthy();
|
|
79
|
+
});
|
|
80
|
+
});
|