@wireio/stake 0.4.0 → 0.4.1
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/lib/stake.browser.js +1738 -549
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +41 -8
- package/lib/stake.js +1863 -642
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +1738 -549
- package/lib/stake.m.js.map +1 -1
- package/package.json +1 -1
- package/src/networks/ethereum/clients/{deposit.client.ts → convert.client.ts} +36 -4
- package/src/networks/ethereum/clients/opp.client.ts +390 -0
- package/src/networks/ethereum/clients/pretoken.client.ts +88 -49
- package/src/networks/ethereum/clients/receipt.client.ts +129 -0
- package/src/networks/ethereum/clients/stake.client.ts +1 -148
- package/src/networks/ethereum/contract.ts +7 -4
- package/src/networks/ethereum/ethereum.ts +44 -65
- package/src/networks/ethereum/types.ts +1 -0
- package/src/types.ts +53 -0
- package/src/networks/ethereum/clients/liq.client.ts +0 -47
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { BigNumber, ethers
|
|
2
|
-
import { DepositEvent, DepositResult,
|
|
1
|
+
import { BigNumber, ethers } from "ethers";
|
|
2
|
+
import { DepositEvent, DepositResult, SharesBurnedEvent } from "../types";
|
|
3
3
|
import { EthereumContractService } from "../contract";
|
|
4
|
-
import { ChainID } from "@wireio/core";
|
|
5
4
|
import { formatContractErrors } from "../utils";
|
|
6
5
|
|
|
7
|
-
export class
|
|
6
|
+
export class ConvertClient {
|
|
8
7
|
|
|
9
8
|
private readonly contractService: EthereumContractService;
|
|
10
9
|
|
|
@@ -84,7 +83,39 @@ export class DepositClient {
|
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
|
|
86
|
+
public async performWithdraw(signerAddress: string, amountWei: BigNumber): Promise<any> {
|
|
87
|
+
let tx, receipt;
|
|
88
|
+
try {
|
|
89
|
+
tx = await this.contract.LiqEth.safeBurn(signerAddress, amountWei);
|
|
90
|
+
receipt = await tx.wait(1);
|
|
91
|
+
} catch (err: any) {
|
|
92
|
+
let errorObj = formatContractErrors(err);
|
|
93
|
+
throw new Error(errorObj.name ?? errorObj.raw)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Parse SharesBurned event if present
|
|
97
|
+
let event: SharesBurnedEvent | undefined;
|
|
98
|
+
const ev = receipt.events?.find((e) => e.event === 'SharesBurned');
|
|
87
99
|
|
|
100
|
+
if (ev && ev.args) {
|
|
101
|
+
const { from, shares, tokenValue } = ev.args;
|
|
102
|
+
event = {
|
|
103
|
+
from,
|
|
104
|
+
shares: BigNumber.from(shares),
|
|
105
|
+
tokenValue: BigNumber.from(tokenValue),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
txHash: tx.hash,
|
|
111
|
+
receipt,
|
|
112
|
+
event,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
/*
|
|
88
119
|
// OLD - this was replaced with LiqEth.safeBurn() on 1/13/26
|
|
89
120
|
async requestWithdraw(amountWei: BigNumber, signer: Signer, chainId: ChainID): Promise<WithdrawResult> {
|
|
90
121
|
// deadline is a period of time in the future that the signature is valid for
|
|
@@ -153,6 +184,7 @@ export class DepositClient {
|
|
|
153
184
|
withdrawRequested,
|
|
154
185
|
} as WithdrawResult;
|
|
155
186
|
}
|
|
187
|
+
*/
|
|
156
188
|
|
|
157
189
|
|
|
158
190
|
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { ethers } from "ethers";
|
|
2
|
+
import { EthereumContractService } from "../contract";
|
|
3
|
+
import { OPPAssertion } from "../../../types";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export class OPPClient {
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
private readonly decoders: Record<number, Decoder> = {
|
|
10
|
+
2001: (d) => this.decode2001(d),
|
|
11
|
+
2002: (d) => this.decode2002(d),
|
|
12
|
+
|
|
13
|
+
3001: (d) => this.decodeStake3001(d),
|
|
14
|
+
3002: (d) => this.decodeUnstake3002(d),
|
|
15
|
+
// 3003 not implemented
|
|
16
|
+
3004: (d) => this.decodeLiqPretoken3004(d),
|
|
17
|
+
3005: (d) => this.decodePretoken3005(d),
|
|
18
|
+
3006: (d) => this.decodeYieldPretoken3006(d),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
private readonly contractService: EthereumContractService;
|
|
22
|
+
|
|
23
|
+
get contract() { return this.contractService.contract; }
|
|
24
|
+
|
|
25
|
+
constructor(contract: EthereumContractService) {
|
|
26
|
+
this.contractService = contract;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read OPP / Outpost state used by the Depositor to decide whether staking is allowed.
|
|
33
|
+
* Returns various data
|
|
34
|
+
*/
|
|
35
|
+
async getStatus(): Promise<any> {
|
|
36
|
+
const depositor = this.contract.Depositor;
|
|
37
|
+
const opp = this.contract.OPP;
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
const oppAddress: string = await depositor.oppAddress();
|
|
41
|
+
const oppInboundAddress: string = await depositor.oppInboundAddress();
|
|
42
|
+
const prevEpochSent = await opp.prevEpochSent();
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
const inbound = this.contractService.getReadOnly('OPPInbound');
|
|
46
|
+
|
|
47
|
+
// Query useful getters
|
|
48
|
+
const nextEpochBN: any = await inbound.nextEpochNum();
|
|
49
|
+
const pendingEpochRaw: any = await inbound.pendingEpoch();
|
|
50
|
+
const pendingMessageCount: any = await inbound.pendingMessageCount();
|
|
51
|
+
const previousEpochHash: string = await inbound.previousEpochHash();
|
|
52
|
+
const nextEpochNum = (nextEpochBN && typeof nextEpochBN.toNumber === 'function') ? nextEpochBN.toNumber() : Number(nextEpochBN || 0);
|
|
53
|
+
|
|
54
|
+
const pendingEpoch = (pendingEpochRaw && pendingEpochRaw.epochNumber !== undefined)
|
|
55
|
+
? {
|
|
56
|
+
epochNumber: (pendingEpochRaw.epochNumber && typeof pendingEpochRaw.epochNumber.toNumber === 'function') ? pendingEpochRaw.epochNumber.toNumber() : Number(pendingEpochRaw.epochNumber || 0),
|
|
57
|
+
timestamp: (pendingEpochRaw.timestamp && typeof pendingEpochRaw.timestamp.toNumber === 'function') ? pendingEpochRaw.timestamp.toNumber() : Number(pendingEpochRaw.timestamp || 0),
|
|
58
|
+
prevEpochHash: pendingEpochRaw.prevEpochHash,
|
|
59
|
+
merkleRoot: pendingEpochRaw.merkleRoot,
|
|
60
|
+
firstMessageID: pendingEpochRaw.firstMessageID,
|
|
61
|
+
lastMessageID: pendingEpochRaw.lastMessageID,
|
|
62
|
+
}
|
|
63
|
+
: null;
|
|
64
|
+
|
|
65
|
+
const pendingMessagesBN = pendingMessageCount;
|
|
66
|
+
const pendingMessages = (pendingMessagesBN && typeof pendingMessagesBN.toString === 'function') ? pendingMessagesBN.toString() : String(pendingMessagesBN || '0');
|
|
67
|
+
|
|
68
|
+
const hasPendingMessages = (pendingMessagesBN && typeof pendingMessagesBN.gt === 'function') ? pendingMessagesBN.gt(0) : (Number(pendingMessages) > 0);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
oppAddress,
|
|
72
|
+
prevEpochSent,
|
|
73
|
+
oppInboundAddress,
|
|
74
|
+
nextEpochNum,
|
|
75
|
+
pendingEpoch,
|
|
76
|
+
pendingMessageCount: pendingMessages,
|
|
77
|
+
previousEpochHash,
|
|
78
|
+
hasPendingMessages,
|
|
79
|
+
raw: {
|
|
80
|
+
nextEpochBN,
|
|
81
|
+
pendingEpochRaw,
|
|
82
|
+
pendingMessageCount: pendingMessagesBN,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Fetches all OPPMessage events and flattens all assertions into a single OPPAssertion[] array.
|
|
92
|
+
*/
|
|
93
|
+
async getMessages(address: string): Promise<OPPAssertion[]> {
|
|
94
|
+
const oppMessageFilter = this.contract.OPP.filters.OPPMessage();
|
|
95
|
+
const events = await this.contract.OPP.queryFilter(oppMessageFilter, 0, "latest");
|
|
96
|
+
const allAssertions: OPPAssertion[] = [];
|
|
97
|
+
for (const event of events) {
|
|
98
|
+
const assertions = await this.extractAssertionsFromEvent(event);
|
|
99
|
+
allAssertions.push(...assertions);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ! Current implementation is not ideal - no current way to filter OPP Messages by a single user
|
|
103
|
+
const normalized = address ? address.toLowerCase() : null;
|
|
104
|
+
const filtered = allAssertions.filter(a =>
|
|
105
|
+
(a.from && a.from.toLowerCase() === normalized) ||
|
|
106
|
+
(a.to && a.to.toLowerCase() === normalized)
|
|
107
|
+
);
|
|
108
|
+
return filtered.reverse();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Extracts all OPPAssertions from a single OPPMessage event, attaching event metadata to each.
|
|
117
|
+
*/
|
|
118
|
+
private async extractAssertionsFromEvent(event: any): Promise<OPPAssertion[]> {
|
|
119
|
+
const header = event.args.header;
|
|
120
|
+
const payloadAssertions = event.args.payload.assertions;
|
|
121
|
+
|
|
122
|
+
const timestamp = header.timestamp ? Number(header.timestamp) : null;
|
|
123
|
+
const from = event.address || null;
|
|
124
|
+
const txHash = event.transactionHash;
|
|
125
|
+
const chain = 'ETH';
|
|
126
|
+
|
|
127
|
+
const assertionList: OPPAssertion[] = [];
|
|
128
|
+
|
|
129
|
+
if (payloadAssertions && payloadAssertions.length > 0) {
|
|
130
|
+
for (let a of payloadAssertions) {
|
|
131
|
+
const assertionType = a[0] as number;
|
|
132
|
+
const assertionData = a[1] as string;
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
let type: OPPAssertion["type"] = "unknown";
|
|
136
|
+
let data: any = {};
|
|
137
|
+
let to: string | null = null;
|
|
138
|
+
|
|
139
|
+
const decoder = this.decoders[assertionType];
|
|
140
|
+
|
|
141
|
+
if (decoder) {
|
|
142
|
+
try {
|
|
143
|
+
const decoded = decoder(assertionData);
|
|
144
|
+
type = decoded.type;
|
|
145
|
+
data = decoded.data;
|
|
146
|
+
to = decoded.to ?? null;
|
|
147
|
+
} catch (e: any) {
|
|
148
|
+
// catch a bad assertion so it doesn't fail the entire function
|
|
149
|
+
type = "unknown";
|
|
150
|
+
data = {
|
|
151
|
+
decodeError: e?.message ?? String(e),
|
|
152
|
+
assertionType,
|
|
153
|
+
rawHex: assertionData,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
// Unknown assertion type
|
|
158
|
+
data = { assertionType, rawHex: assertionData };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
assertionList.push({
|
|
162
|
+
type,
|
|
163
|
+
data,
|
|
164
|
+
chain,
|
|
165
|
+
timestamp,
|
|
166
|
+
from,
|
|
167
|
+
to,
|
|
168
|
+
txHash,
|
|
169
|
+
raw: event,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return assertionList;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
private decode2001(assertionData: string): DecoderResult {
|
|
179
|
+
const hex = this.strip0x(assertionData);
|
|
180
|
+
|
|
181
|
+
let cursor = 0;
|
|
182
|
+
const r1 = this.readAddress(hex, cursor); cursor = r1.next;
|
|
183
|
+
const r2 = this.readAddress(hex, cursor); cursor = r2.next;
|
|
184
|
+
const r3 = this.readBytes32(hex, cursor); cursor = r3.next;
|
|
185
|
+
const r4 = this.readUint256LE(hex, cursor); cursor = r4.next;
|
|
186
|
+
const r5 = this.readUint64LE(hex, cursor); cursor = r5.next;
|
|
187
|
+
|
|
188
|
+
const data = {
|
|
189
|
+
actor: r1.addr,
|
|
190
|
+
owner: r2.addr,
|
|
191
|
+
bondLevelId: r3.value,
|
|
192
|
+
tokenId: r4.value,
|
|
193
|
+
bondedAt: r5.value,
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return { type: "bonded_actor", data, to: r1.addr };
|
|
197
|
+
}
|
|
198
|
+
private decode2002(assertionData: string): DecoderResult {
|
|
199
|
+
const hex = this.strip0x(assertionData);
|
|
200
|
+
|
|
201
|
+
let cursor = 0;
|
|
202
|
+
const r1 = this.readAddress(hex, cursor); cursor = r1.next;
|
|
203
|
+
const r2 = this.readBytes32(hex, cursor); cursor = r2.next;
|
|
204
|
+
const r3 = this.readUint256LE(hex, cursor); cursor = r3.next;
|
|
205
|
+
const r4 = this.readUint64LE(hex, cursor); cursor = r4.next;
|
|
206
|
+
|
|
207
|
+
const data = {
|
|
208
|
+
actor: r1.addr,
|
|
209
|
+
bondLevelId: r2.value,
|
|
210
|
+
tokenId: r3.value,
|
|
211
|
+
unbondedAt: r4.value,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
return { type: "unbonded_actor", data, to: r1.addr };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
private decodeStake3001(assertionData: string): DecoderResult {
|
|
219
|
+
const { addr, nums } = this.decodeAddrPlusU256(assertionData, 3);
|
|
220
|
+
const [principal, shares, indexAtMint] = nums;
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
type: "stake",
|
|
224
|
+
data: {
|
|
225
|
+
staker: addr,
|
|
226
|
+
principal: ethers.utils.formatUnits(principal, 18),
|
|
227
|
+
shares: ethers.utils.formatUnits(shares, 18),
|
|
228
|
+
indexAtMint
|
|
229
|
+
},
|
|
230
|
+
to: addr,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
private decodeUnstake3002(assertionData: string): DecoderResult {
|
|
234
|
+
const { addr, nums } = this.decodeAddrPlusU256(assertionData, 4);
|
|
235
|
+
const [unstakeAmount, shares, indexAtBurn, tokenId] = nums;
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
type: "unstake",
|
|
239
|
+
data: {
|
|
240
|
+
unstaker: addr,
|
|
241
|
+
unstakeAmount: ethers.utils.formatUnits(unstakeAmount, 18),
|
|
242
|
+
shares,
|
|
243
|
+
indexAtBurn,
|
|
244
|
+
tokenId
|
|
245
|
+
},
|
|
246
|
+
to: addr,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
private decodeLiqPretoken3004(assertionData: string): DecoderResult {
|
|
250
|
+
const { addr, nums } = this.decodeAddrPlusU256(assertionData, 3);
|
|
251
|
+
const [principal, shares, indexAtMint] = nums;
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
type: "liq_pretoken_purchase",
|
|
255
|
+
data: {
|
|
256
|
+
purchaser: addr,
|
|
257
|
+
principal: ethers.utils.formatUnits(principal, 18),
|
|
258
|
+
shares: ethers.utils.formatUnits(shares, 18),
|
|
259
|
+
indexAtMint
|
|
260
|
+
},
|
|
261
|
+
to: addr,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
private decodePretoken3005(assertionData: string): DecoderResult {
|
|
265
|
+
const { addr, nums } = this.decodeAddrPlusU256(assertionData, 3);
|
|
266
|
+
const [ethInWei, usdValue, pretokensOut] = nums;
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
type: "pretoken_purchase",
|
|
270
|
+
data: {
|
|
271
|
+
buyer: addr,
|
|
272
|
+
ethInWei,
|
|
273
|
+
ethIn: ethers.utils.formatUnits(ethInWei, 18),
|
|
274
|
+
usdValue: ethers.utils.formatUnits(usdValue, 18),
|
|
275
|
+
pretokensOut: ethers.utils.formatUnits(pretokensOut, 18),
|
|
276
|
+
},
|
|
277
|
+
to: addr,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
private decodeYieldPretoken3006(assertionData: string): DecoderResult {
|
|
281
|
+
const { addr, nums } = this.decodeAddrPlusU256(assertionData, 2);
|
|
282
|
+
const [principal, indexAtMint] = nums;
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
type: "yield_pretoken_purchase",
|
|
286
|
+
data: { purchaser: addr, principal, indexAtMint },
|
|
287
|
+
to: addr,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
*
|
|
296
|
+
* @param hex string to check
|
|
297
|
+
* @returns the passed string, without a prefixed 0x if it existed
|
|
298
|
+
*/
|
|
299
|
+
private strip0x(hex: string): string {
|
|
300
|
+
return hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private decodeAddrPlusU256(assertionData: string, count: number): { addr: string; nums: string[] } {
|
|
304
|
+
const hex = this.strip0x(assertionData);
|
|
305
|
+
let cursor = 0;
|
|
306
|
+
|
|
307
|
+
const { addr, next } = this.readAddress(hex, cursor);
|
|
308
|
+
cursor = next;
|
|
309
|
+
|
|
310
|
+
const nums: string[] = [];
|
|
311
|
+
for (let i = 0; i < count; i++) {
|
|
312
|
+
const r = this.readUint256LE(hex, cursor);
|
|
313
|
+
nums.push(r.value);
|
|
314
|
+
cursor = r.next;
|
|
315
|
+
}
|
|
316
|
+
return { addr, nums };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private uint256FromLE(hexNo0x: string): ethers.BigNumber {
|
|
320
|
+
if (hexNo0x.length !== 64) {
|
|
321
|
+
throw new Error(`uint256 LE must be 32 bytes (64 hex chars), got ${hexNo0x.length}`);
|
|
322
|
+
}
|
|
323
|
+
const bytes = ethers.utils.arrayify("0x" + hexNo0x);
|
|
324
|
+
const reversed = Uint8Array.from(bytes).reverse(); // little -> big endian
|
|
325
|
+
return ethers.BigNumber.from(ethers.utils.hexlify(reversed));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Parses "0x14 + <addr>" and returns addr + next cursor (in hex chars, excluding 0x)
|
|
331
|
+
* @param payload full payload
|
|
332
|
+
* @param cursor place to start
|
|
333
|
+
* @returns string
|
|
334
|
+
*/
|
|
335
|
+
private readAddress(payload: string, cursor: number): { addr: string; next: number } {
|
|
336
|
+
const lenByteHex = payload.slice(cursor, cursor + 2);
|
|
337
|
+
const len = parseInt(lenByteHex, 16);
|
|
338
|
+
if (!Number.isFinite(len) || len <= 0) throw new Error(`Bad address len byte: ${lenByteHex}`);
|
|
339
|
+
const start = cursor + 2;
|
|
340
|
+
const end = start + len * 2;
|
|
341
|
+
const addrHex = payload.slice(start, end);
|
|
342
|
+
if (addrHex.length !== len * 2) throw new Error(`Truncated address bytes`);
|
|
343
|
+
return { addr: ethers.utils.getAddress("0x" + addrHex), next: end };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Reads 32-byte uint256 little-endian from payload at cursor (hex chars, excluding 0x)
|
|
348
|
+
* @param payload
|
|
349
|
+
* @param cursor
|
|
350
|
+
* @returns
|
|
351
|
+
*/
|
|
352
|
+
private readUint256LE(payload: string, cursor: number): { value: string; next: number } {
|
|
353
|
+
const word = payload.slice(cursor, cursor + 64);
|
|
354
|
+
if (word.length !== 64) throw new Error(`Truncated uint256 at ${cursor}`);
|
|
355
|
+
return { value: this.uint256FromLE(word).toString(), next: cursor + 64 };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Reads 8-byte uint64 little-endian from payload at cursor (hex chars, excluding 0x)
|
|
360
|
+
* @param payload
|
|
361
|
+
* @param cursor
|
|
362
|
+
* @returns
|
|
363
|
+
*/
|
|
364
|
+
private readUint64LE(payload: string, cursor: number): { value: number; next: number } {
|
|
365
|
+
const word = payload.slice(cursor, cursor + 16);
|
|
366
|
+
if (word.length !== 16) throw new Error(`Truncated uint64 at ${cursor}`);
|
|
367
|
+
const bytes = ethers.utils.arrayify("0x" + word);
|
|
368
|
+
const reversed = Uint8Array.from(bytes).reverse();
|
|
369
|
+
const bn = ethers.BigNumber.from(ethers.utils.hexlify(reversed));
|
|
370
|
+
return { value: bn.toNumber(), next: cursor + 16 };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Reads bytes32 from payload at cursor (hex chars, excluding 0x)
|
|
375
|
+
* @param payload
|
|
376
|
+
* @param cursor
|
|
377
|
+
* @returns
|
|
378
|
+
*/
|
|
379
|
+
private readBytes32(payload: string, cursor: number): { value: string; next: number } {
|
|
380
|
+
const word = payload.slice(cursor, cursor + 64);
|
|
381
|
+
if (word.length !== 64) throw new Error(`Truncated bytes32 at ${cursor}`);
|
|
382
|
+
return { value: "0x" + word, next: cursor + 64 };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
type DecoderResult = { type: OPPAssertion["type"]; data: any; to?: string | null };
|
|
390
|
+
type Decoder = (assertionData: string) => DecoderResult;
|
|
@@ -13,61 +13,50 @@ export class PretokenClient {
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
* Purchase pretokens by sending ETH to the Depositor.purchasePretokensWithETH(buyer) payable function.
|
|
18
|
-
* Returns txHash, receipt and parsed PurchasedWithETH event (if present).
|
|
19
|
-
*/
|
|
20
|
-
// async purchasePretokensWithETH(amountWei: BigNumber, buyer: string): Promise<any> {
|
|
21
|
-
// // attempt a simulation of the purchase call
|
|
22
|
-
// try {
|
|
23
|
-
// await this.contract.Depositor.callStatic.purchasePretokensWithETH(buyer, { value: amountWei });
|
|
24
|
-
// } catch (err: any) {
|
|
25
|
-
// let errorObj = formatContractErrors(err);
|
|
26
|
-
// throw new Error(errorObj.name ?? errorObj.raw)
|
|
27
|
-
// }
|
|
28
|
-
|
|
29
|
-
// // attempt the real purchase call
|
|
30
|
-
// let tx, receipt;
|
|
31
|
-
// try {
|
|
32
|
-
// tx = await this.contract.Depositor.purchasePretokensWithETH(buyer, { value: amountWei });
|
|
33
|
-
// receipt = await tx.wait(1);
|
|
34
|
-
// } catch (err: any) {
|
|
35
|
-
// let errorObj = formatContractErrors(err);
|
|
36
|
-
// throw new Error(errorObj.name ?? errorObj.raw)
|
|
37
|
-
// }
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// // Attempt to parse PurchasedWithETH event
|
|
41
|
-
// let purchased: any | undefined;
|
|
42
|
-
// const ev = receipt.events?.find((e) => e.event === 'PurchasedWithETH' || e.event === 'PurchasedWithETH(address,uint256,uint256,uint256)');
|
|
43
|
-
|
|
44
|
-
// if (ev && ev.args) {
|
|
45
|
-
// // event signature: PurchasedWithETH(address indexed user, uint256 ethIn, uint256 shares, uint256 tokenId)
|
|
46
|
-
// const { user, ethIn, shares, tokenId } = ev.args as any;
|
|
47
|
-
|
|
48
|
-
// purchased = {
|
|
49
|
-
// buyer: user,
|
|
50
|
-
// amount: BigNumber.from(ethIn),
|
|
51
|
-
// shares: BigNumber.from(shares),
|
|
52
|
-
// tokenId: BigNumber.from(tokenId),
|
|
53
|
-
// };
|
|
54
|
-
// }
|
|
55
|
-
|
|
56
|
-
// return {
|
|
57
|
-
// txHash: tx.hash,
|
|
58
|
-
// receipt,
|
|
59
|
-
// purchased,
|
|
60
|
-
// };
|
|
61
|
-
// }
|
|
62
|
-
|
|
63
16
|
/**
|
|
64
17
|
* Purchase pretokens by transferring liqETH into the pool (ERC-20) and calling Depositor.purchasePretokensWithLiqETH(amountLiq, buyer).
|
|
65
18
|
* Returns txHash, receipt and parsed PurchasedWithLiqETH event (if present).
|
|
66
19
|
*/
|
|
67
|
-
async purchasePretokensWithLiqETH(amountLiq:
|
|
20
|
+
async purchasePretokensWithLiqETH(amountLiq: bigint, buyer: string): Promise<any> {
|
|
21
|
+
|
|
22
|
+
const bal = await this.contract.LiqEth.balanceOf(buyer);
|
|
23
|
+
const paused = await this.contract.Depositor.paused();
|
|
24
|
+
if(paused) {
|
|
25
|
+
throw new Error("Error - Depositor is in a paused state");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// if current liq balance is less than the requested buy amount, throw an error
|
|
29
|
+
if (bal.lt(amountLiq)) {
|
|
30
|
+
throw new Error(`Balance insufficient for pre-token purchase`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//check that the contract has allowance for the token
|
|
34
|
+
const depositorAddr = this.contract.Depositor.address;
|
|
35
|
+
const allowance = await this.contract.LiqEth.allowance(buyer, depositorAddr);
|
|
36
|
+
|
|
37
|
+
// if allowance is less than the requested stake amount, request permission to spend LiqEth
|
|
38
|
+
if (allowance.lt(amountLiq)) {
|
|
39
|
+
const liqWrite = this.contractService.getWrite('LiqEth');
|
|
40
|
+
|
|
41
|
+
// currently requested unlimited amount - potentially change to only request up to the current amount?
|
|
42
|
+
const approveAmount = ethers.constants.MaxUint256;
|
|
43
|
+
|
|
44
|
+
console.warn(`allowance insufficient (${allowance.toString()} < ${amountLiq.toString()}); sending approve(${depositorAddr}, ${approveAmount.toString()})`);
|
|
45
|
+
|
|
46
|
+
const approveTx = await liqWrite.approve(depositorAddr, approveAmount);
|
|
47
|
+
await approveTx.wait(1);
|
|
48
|
+
|
|
49
|
+
// re-read allowance to ensure approval succeeded
|
|
50
|
+
const newAllowance = await this.contract.LiqEth.allowance(buyer, depositorAddr);
|
|
51
|
+
if (newAllowance.lt(amountLiq)) {
|
|
52
|
+
throw new Error('Approval failed or allowance still insufficient after approve');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
68
56
|
// attempt a simulation of the purchase call
|
|
69
57
|
try {
|
|
70
|
-
|
|
58
|
+
const amountLiqBN = BigNumber.from(amountLiq)
|
|
59
|
+
await this.contract.Depositor.callStatic.purchasePretokensWithLiqETH(amountLiqBN, buyer);
|
|
71
60
|
} catch (err: any) {
|
|
72
61
|
let errorObj = formatContractErrors(err);
|
|
73
62
|
|
|
@@ -124,4 +113,54 @@ export class PretokenClient {
|
|
|
124
113
|
|
|
125
114
|
|
|
126
115
|
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Purchase pretokens by sending ETH to the Depositor.purchasePretokensWithETH(buyer) payable function.
|
|
119
|
+
* Returns txHash, receipt and parsed PurchasedWithETH event (if present).
|
|
120
|
+
async purchasePretokensWithETH(amountWei: BigNumber, buyer: string): Promise<any> {
|
|
121
|
+
// attempt a simulation of the purchase call
|
|
122
|
+
try {
|
|
123
|
+
await this.contract.Depositor.callStatic.purchasePretokensWithETH(buyer, { value: amountWei });
|
|
124
|
+
} catch (err: any) {
|
|
125
|
+
let errorObj = formatContractErrors(err);
|
|
126
|
+
throw new Error(errorObj.name ?? errorObj.raw)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// attempt the real purchase call
|
|
130
|
+
let tx, receipt;
|
|
131
|
+
try {
|
|
132
|
+
tx = await this.contract.Depositor.purchasePretokensWithETH(buyer, { value: amountWei });
|
|
133
|
+
receipt = await tx.wait(1);
|
|
134
|
+
} catch (err: any) {
|
|
135
|
+
let errorObj = formatContractErrors(err);
|
|
136
|
+
throw new Error(errorObj.name ?? errorObj.raw)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
// Attempt to parse PurchasedWithETH event
|
|
141
|
+
let purchased: any | undefined;
|
|
142
|
+
const ev = receipt.events?.find((e) => e.event === 'PurchasedWithETH' || e.event === 'PurchasedWithETH(address,uint256,uint256,uint256)');
|
|
143
|
+
|
|
144
|
+
if (ev && ev.args) {
|
|
145
|
+
// event signature: PurchasedWithETH(address indexed user, uint256 ethIn, uint256 shares, uint256 tokenId)
|
|
146
|
+
const { user, ethIn, shares, tokenId } = ev.args as any;
|
|
147
|
+
|
|
148
|
+
purchased = {
|
|
149
|
+
buyer: user,
|
|
150
|
+
amount: BigNumber.from(ethIn),
|
|
151
|
+
shares: BigNumber.from(shares),
|
|
152
|
+
tokenId: BigNumber.from(tokenId),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
txHash: tx.hash,
|
|
158
|
+
receipt,
|
|
159
|
+
purchased,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
127
166
|
}
|