-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsafe.test.ts
212 lines (181 loc) · 6.16 KB
/
safe.test.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
import chai, { expect } from "chai";
import chaiAsPromised from "chai-as-promised";
chai.use(chaiAsPromised);
import { ethers as ethersTypes } from "ethers";
import { ethers } from "hardhat";
import { L1__factory } from "../contract-types/factories/L1__factory";
import { L2__factory } from "../contract-types/factories/L2__factory";
import { L1, TicketStruct } from "../contract-types/L1";
import { L2, L2DepositStruct } from "../contract-types/L2";
import { TicketsWithIndex } from "../src/types";
import { hashTickets, signData } from "../src/utils";
const gasLimit = 30_000_000;
// Address 0x2a47Cd5718D67Dc81eAfB24C99d4db159B0e7bCa
const customerPK =
"0xe1743f0184b85ac1412311be9d6e5d333df23e22efdf615d0135ca2b9ed67938";
// Address 0xaaab35381a38c4ff4967dc29470f0f2637295983
const customer2PK =
"0x91f47a1911c0fd985b34c25962f661f0de606f7ad38ba156902dff48b4d05f97";
// Address 0x9552ceB4e6FA8c356c1A76A8Bc8b1EFA7B9fb205
const lpPK =
"0x23ac17b9c3590a8e67a1d1231ebab87dd2d3389d2f1526f842fd1326a0990f42";
const customerWallet = new ethers.Wallet(customerPK, ethers.provider);
const customer2Wallet = new ethers.Wallet(customer2PK, ethers.provider);
const lpWallet = new ethers.Wallet(lpPK, ethers.provider);
const l1Deployer = new L1__factory(lpWallet);
const l2Deployer = new L2__factory(lpWallet);
let lpL1: L1;
let customerL2: L2, customer2L2: L2, lpL2: L2;
async function waitForTx(
txPromise: Promise<ethersTypes.providers.TransactionResponse>,
) {
return (await txPromise).wait();
}
async function deposit(trustedNonce: number, trustedAmount: number) {
const depositAmount = 1;
const deposit: L2DepositStruct = {
trustedNonce,
trustedAmount,
depositAmount,
l1Recipient: customerWallet.address,
};
const deposit2: L2DepositStruct = {
...deposit,
l1Recipient: customer2Wallet.address,
};
await waitForTx(customerL2.depositOnL2(deposit, { value: depositAmount }));
await waitForTx(customer2L2.depositOnL2(deposit2, { value: depositAmount }));
}
async function authorizeWithdrawal(
trustedNonce: number,
): Promise<[TicketStruct, TicketStruct, ethersTypes.Signature]> {
const ticket = await lpL2.tickets(trustedNonce);
const ticket2 = await lpL2.tickets(trustedNonce + 1);
const ticketsWithIndex: TicketsWithIndex = {
startIndex: trustedNonce,
tickets: [ticket, ticket2],
};
const signature = signData(hashTickets(ticketsWithIndex), lpPK);
await waitForTx(
lpL2.authorizeWithdrawal(trustedNonce, trustedNonce + 1, signature, {
// TODO: remove this after addressing https://github.com/statechannels/SAFE-protocol/issues/70
gasLimit,
}),
);
return [ticket, ticket2, signature];
}
async function swap(trustedNonce: number, trustedAmount: number) {
await deposit(trustedNonce, trustedAmount);
const [ticket, ticket2, signature] = await authorizeWithdrawal(trustedNonce);
await waitForTx(lpL1.claimBatch([ticket, ticket2], signature));
await ethers.provider.send("evm_increaseTime", [121]);
await waitForTx(lpL2.claimL2Funds(trustedNonce));
}
beforeEach(async () => {
const l1 = await l1Deployer.deploy();
const l2 = await l2Deployer.deploy();
customerL2 = l2.connect(customerWallet);
customer2L2 = l2.connect(customer2Wallet);
lpL2 = l2.connect(lpWallet);
lpL1 = l1.connect(lpWallet);
await waitForTx(
lpWallet.sendTransaction({
to: l1.address,
value: ethers.utils.parseUnits("10", "wei"),
}),
);
});
it("Successfull e2e swap", async () => {
await swap(0, 10);
await swap(2, 8);
});
it("Unable to authorize overlapping batches", async () => {
await swap(0, 10);
await expect(swap(1, 9)).to.be.rejectedWith("Batches must be gapless");
});
it("Handles a fraud proofs", async () => {
/**
* Fraud instance 1. The liquidity provider signs a batch of tickets with the
* second ticket's l1Recipient switched to LP's address
*/
await deposit(0, 10);
// Sign fraudulent batch
const ticket = await lpL2.tickets(0);
const ticket2 = await lpL2.tickets(1);
await authorizeWithdrawal(0);
const fraudTicket = { ...ticket2, l1Recipient: lpWallet.address };
const ticketsWithIndex: TicketsWithIndex = {
startIndex: 0,
tickets: [ticket, fraudTicket],
};
const fraudSignature = signData(hashTickets(ticketsWithIndex), lpPK);
// Successfully prove fraud
await waitForTx(
customer2L2.refundOnFraud(
0,
1,
0,
1,
[ticket, fraudTicket],
fraudSignature,
),
);
// Unsuccessfully try to claim fraud again
await expect(
customer2L2.refundOnFraud(
0,
1,
0,
1,
[ticket, fraudTicket],
fraudSignature,
{
gasLimit,
},
),
).to.be.rejectedWith("Batch status must be Authorized");
/**
* Fraud instance 2. The setup is:
* - There are 4 tickets. The first two tickets have been refunded above.
* - The second two tickets are authorized by LP.
* - LP signs a batch that includes a correct 2nd ticket and a fraudulent 3rd ticket.
*/
await deposit(2, 8);
await authorizeWithdrawal(2);
// Sign fraudulent batch again
const ticket3 = await lpL2.tickets(1);
const ticket4 = await lpL2.tickets(2);
const fraudTicket2 = { ...ticket4, l1Recipient: lpWallet.address };
const ticketsWithIndex2: TicketsWithIndex = {
startIndex: 1,
tickets: [ticket3, fraudTicket2],
};
const fraudSignature2 = signData(hashTickets(ticketsWithIndex2), lpPK);
await waitForTx(
customer2L2.refundOnFraud(
2,
0,
1,
1,
[ticket3, fraudTicket2],
fraudSignature2,
{ gasLimit },
),
);
});
it("Able to get a ticket refunded", async () => {
await deposit(0, 10);
await expect(customerL2.refund(0, { gasLimit })).to.be.rejectedWith(
"maxAuthDelay must have passed since deposit",
);
await ethers.provider.send("evm_increaseTime", [61]);
await waitForTx(customerL2.refund(0, { gasLimit }));
await waitForTx(customerL2.refund(1, { gasLimit }));
await expect(customerL2.refund(1, { gasLimit })).to.be.rejectedWith(
"The nonce must not be a part of a batch",
);
await deposit(2, 8);
await ethers.provider.send("evm_increaseTime", [61]);
// Refund 3rd and 4th deposit
await waitForTx(customerL2.refund(2, { gasLimit }));
});