forked from coral-xyz/multisig
-
Notifications
You must be signed in to change notification settings - Fork 1
/
multisigApprovalTest.ts
231 lines (176 loc) Β· 9.58 KB
/
multisigApprovalTest.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import assert from "assert";
import {setUpValidator} from "./utils/before";
import {AnchorProvider, BN, Program} from "@coral-xyz/anchor";
import {Keypair, PublicKey, SystemProgram,} from "@solana/web3.js";
import {MultisigDsl} from "./utils/multisigDsl";
import {describe} from "mocha";
import {fail} from "node:assert";
describe("Test performing signing and execution", async () => {
let provider: AnchorProvider;
let program: Program;
let dsl: MultisigDsl;
before(async () => {
let result = await setUpValidator(false);
program = result.program;
provider = result.provider;
dsl = new MultisigDsl(program, provider);
});
it("should perform instructions if reached multisig approval threshold", async () => {
const multisig = await dsl.createMultisig(2, 3, 1_000_000);
const [ownerA, ownerB, _ownerC] = multisig.owners;
// Create instruction to send funds from multisig
let transactionInstruction = SystemProgram.transfer({
fromPubkey: multisig.signer,
lamports: new BN(1_000_000),
toPubkey: provider.publicKey,
});
await dsl.assertBalance(multisig.signer, 1_000_000);
const transactionAddress: PublicKey = await dsl.proposeTransaction(ownerA, [transactionInstruction], multisig.address);
await dsl.approveTransaction(ownerB, multisig.address, transactionAddress);
await dsl.executeTransaction(transactionAddress, transactionInstruction, multisig.signer, multisig.address, ownerB, ownerA.publicKey);
await dsl.assertBalance(multisig.signer, 0);
});
it("should transfer partial funds", async () => {
const multisig = await dsl.createMultisig(2, 3, 1_000_000);
const [ownerA, ownerB, _ownerC] = multisig.owners;
// Create instruction to send funds from multisig
let transactionInstruction = SystemProgram.transfer({
fromPubkey: multisig.signer,
lamports: new BN(100_000),
toPubkey: provider.publicKey,
});
await dsl.assertBalance(multisig.signer, 1_000_000);
const transactionAddress: PublicKey = await dsl.proposeTransaction(ownerA, [transactionInstruction], multisig.address);
await dsl.approveTransaction(ownerB, multisig.address, transactionAddress);
await dsl.executeTransaction(transactionAddress, transactionInstruction, multisig.signer, multisig.address, ownerB, ownerA.publicKey);
await dsl.assertBalance(multisig.signer, 900_000);
}).timeout(10000);
it("should handle multiple transactions in parallel", async () => {
const multisig = await dsl.createMultisig(2, 3, 2_000_000);
const [ownerA, ownerB, _ownerC] = multisig.owners;
// Create instruction to send funds from multisig
let transactionInstruction = SystemProgram.transfer({
fromPubkey: multisig.signer,
lamports: new BN(1_000_000),
toPubkey: provider.publicKey,
});
await dsl.assertBalance(multisig.signer, 2_000_000);
const transactionAddress1: PublicKey = await dsl.proposeTransaction(ownerA, [transactionInstruction], multisig.address);
const transactionAddress2: PublicKey = await dsl.proposeTransaction(ownerA, [transactionInstruction], multisig.address);
await dsl.approveTransaction(ownerB, multisig.address, transactionAddress1);
await dsl.approveTransaction(ownerB, multisig.address, transactionAddress2);
await dsl.executeTransaction(transactionAddress1, transactionInstruction, multisig.signer, multisig.address, ownerB, ownerA.publicKey);
await dsl.executeTransaction(transactionAddress2, transactionInstruction, multisig.signer, multisig.address, ownerB, ownerA.publicKey);
await dsl.assertBalance(multisig.signer, 0);
}).timeout(20000);
it("should not perform instructions if not reached multisig approval threshold", async () => {
const multisig = await dsl.createMultisig(2, 3, 1_000_000);
const [ownerA, ownerB, _ownerC] = multisig.owners;
// Create instruction to send funds from multisig
let transactionInstruction = SystemProgram.transfer({
fromPubkey: multisig.signer,
lamports: new BN(1_000_000),
toPubkey: provider.publicKey,
});
await dsl.assertBalance(multisig.signer, 1_000_000);
const transactionAddress: PublicKey = await dsl.proposeTransaction(ownerA, [transactionInstruction], multisig.address);
try {
await dsl.executeTransaction(transactionAddress, transactionInstruction, multisig.signer, multisig.address, ownerB, ownerA.publicKey);
fail("Should have failed to execute transaction");
} catch (e) {
assert.match(e.message,
new RegExp(".*Error Code: NotEnoughSigners. Error Number: 6003. Error Message: Not enough owners signed this transaction"));
}
await dsl.assertBalance(multisig.signer, 1_000_000);
});
it("should approve idempotently", async () => {
const multisig = await dsl.createMultisig(2, 3, 1_000_000);
const [ownerA, ownerB, _ownerC] = multisig.owners;
// Create instruction to send funds from multisig
let transactionInstruction = SystemProgram.transfer({
fromPubkey: multisig.signer,
lamports: new BN(1_000_000),
toPubkey: provider.publicKey,
});
await dsl.assertBalance(multisig.signer, 1_000_000);
const transactionAddress: PublicKey = await dsl.proposeTransaction(ownerA, [transactionInstruction], multisig.address);
// Approve twice with the same owner
await dsl.approveTransaction(ownerB, multisig.address, transactionAddress);
await dsl.approveTransaction(ownerB, multisig.address, transactionAddress);
await dsl.executeTransaction(transactionAddress, transactionInstruction, multisig.signer, multisig.address, ownerB, ownerA.publicKey);
await dsl.assertBalance(multisig.signer, 0);
}).timeout(10000);
it("should not execute transaction if same user has approved multiple times to reach the threshold", async () => {
const multisig = await dsl.createMultisig(2, 3, 1_000_000);
const [ownerA, ownerB, _ownerC] = multisig.owners;
// Create instruction to send funds from multisig
let transactionInstruction = SystemProgram.transfer({
fromPubkey: multisig.signer,
lamports: new BN(1_000_000),
toPubkey: provider.publicKey,
});
await dsl.assertBalance(multisig.signer, 1_000_000);
const transactionAddress: PublicKey = await dsl.proposeTransaction(ownerA, [transactionInstruction], multisig.address);
//Approve again with the same owner meaning still only 1/3 approval
await dsl.approveTransaction(ownerA, multisig.address, transactionAddress);
try {
await dsl.executeTransaction(transactionAddress, transactionInstruction, multisig.signer, multisig.address, ownerB, ownerA.publicKey);
fail("Should have failed to execute transaction");
} catch (e) {
assert.match(e.message,
new RegExp(".*Error Code: NotEnoughSigners. Error Number: 6003. Error Message: Not enough owners signed this transaction"));
}
await dsl.assertBalance(multisig.signer, 1_000_000);
});
it("should not allow non owner to approve", async () => {
const multisig = await dsl.createMultisig(2, 3, 1_000_000);
const [ownerA, _ownerB, _ownerC] = multisig.owners;
// Create instruction to send funds from multisig
let transactionInstruction = SystemProgram.transfer({
fromPubkey: multisig.signer,
lamports: new BN(1_000_000),
toPubkey: provider.publicKey,
});
await dsl.assertBalance(multisig.signer, 1_000_000);
const transactionAddress: PublicKey = await dsl.proposeTransaction(ownerA, [transactionInstruction], multisig.address);
const notAnOwner = Keypair.generate();
try {
//Attempt to approve with not an owner
await dsl.approveTransaction(
notAnOwner,
multisig.address,
transactionAddress
);
fail("Should have failed to approve transaction");
} catch (e) {
assert.match(e.message,
new RegExp(".*Error Code: InvalidOwner. Error Number: 6000. Error Message: The given owner is not part of this multisig"));
}
await dsl.assertBalance(multisig.signer, 1_000_000);
});
it("should transfer funds from two different multisig accounts", async () => {
const [ownerA, ownerB, ownerC, ownerD] = Array.from({length: 4}, (_, _n) => Keypair.generate());
const multisig1 = await dsl.createMultisigWithOwners(2, [ownerA, ownerB, ownerC], 1_000_000);
const multisig2 = await dsl.createMultisigWithOwners(2, [ownerB, ownerC, ownerD], 1_100_000);
let transactionInstruction1 = SystemProgram.transfer({
fromPubkey: multisig1.signer,
lamports: new BN(50_000),
toPubkey: provider.publicKey,
});
let transactionInstruction2 = SystemProgram.transfer({
fromPubkey: multisig2.signer,
lamports: new BN(100_000),
toPubkey: provider.publicKey,
});
await dsl.assertBalance(multisig1.signer, 1_000_000);
await dsl.assertBalance(multisig2.signer, 1_100_000);
const transactionAddress1: PublicKey = await dsl.proposeTransaction(ownerA, [transactionInstruction1], multisig1.address);
const transactionAddress2: PublicKey = await dsl.proposeTransaction(ownerB, [transactionInstruction2], multisig2.address);
await dsl.approveTransaction(ownerB, multisig1.address, transactionAddress1);
await dsl.approveTransaction(ownerC, multisig2.address, transactionAddress2);
await dsl.executeTransaction(transactionAddress1, transactionInstruction1, multisig1.signer, multisig1.address, ownerB, ownerA.publicKey);
await dsl.executeTransaction(transactionAddress2, transactionInstruction2, multisig2.signer, multisig2.address, ownerC, ownerA.publicKey);
await dsl.assertBalance(multisig1.signer, 950_000);
await dsl.assertBalance(multisig2.signer, 1_000_000);
}).timeout(20000);
});