@wireio/stake 0.4.0 → 0.4.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/lib/stake.browser.js +1802 -582
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +66 -24
- package/lib/stake.js +1885 -633
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +1802 -582
- 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 +133 -133
- package/src/networks/ethereum/types.ts +1 -0
- package/src/networks/solana/solana.ts +35 -18
- package/src/types.ts +60 -1
- package/src/networks/ethereum/clients/liq.client.ts +0 -47
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
import { BigNumber, ethers } from 'ethers';
|
|
2
|
-
import { IStakingClient, Portfolio, StakerConfig, TrancheSnapshot } from '../../types';
|
|
3
2
|
import { ChainID, EvmChainID, PublicKey as WirePubKey } from '@wireio/core';
|
|
3
|
+
import {
|
|
4
|
+
IStakingClient,
|
|
5
|
+
OPPAssertion,
|
|
6
|
+
Portfolio,
|
|
7
|
+
StakerConfig,
|
|
8
|
+
TrancheSnapshot
|
|
9
|
+
} from '../../types';
|
|
4
10
|
import { EthereumContractService } from './contract';
|
|
5
11
|
import { preLaunchReceipt } from './types';
|
|
6
|
-
import {
|
|
12
|
+
import { buildEthereumTrancheSnapshot } from './utils';
|
|
13
|
+
import { ConvertClient } from './clients/convert.client';
|
|
7
14
|
import { StakeClient } from './clients/stake.client';
|
|
8
15
|
import { PretokenClient } from './clients/pretoken.client';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
import { OPPClient } from './clients/opp.client';
|
|
17
|
+
import { ReceiptClient } from './clients/receipt.client';
|
|
14
18
|
|
|
15
19
|
export class EthereumStakingClient implements IStakingClient {
|
|
20
|
+
private readonly provider: ethers.providers.Web3Provider | ethers.providers.JsonRpcProvider;
|
|
16
21
|
public readonly pubKey?: WirePubKey;
|
|
17
|
-
private readonly
|
|
18
|
-
private readonly signer: ethers.Signer;
|
|
22
|
+
private readonly signer?: ethers.Signer;
|
|
19
23
|
private readonly contractService: EthereumContractService;
|
|
20
24
|
|
|
21
|
-
private
|
|
22
|
-
private liqClient: LiqClient;
|
|
25
|
+
private convertClient: ConvertClient;
|
|
23
26
|
private pretokenClient: PretokenClient;
|
|
24
27
|
private stakeClient: StakeClient;
|
|
28
|
+
private oppClient: OPPClient;
|
|
29
|
+
private receiptClient: ReceiptClient;
|
|
25
30
|
|
|
26
31
|
|
|
27
32
|
get contract() { return this.contractService.contract; }
|
|
@@ -29,28 +34,32 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
29
34
|
|
|
30
35
|
constructor(private config: StakerConfig) {
|
|
31
36
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
if (config.provider) {
|
|
38
|
+
this.provider = config.provider as ethers.providers.Web3Provider;
|
|
39
|
+
this.signer = this.provider.getSigner();
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.provider = new ethers.providers.JsonRpcProvider(config.network.rpcUrls[0]);
|
|
43
|
+
}
|
|
34
44
|
this.pubKey = config.pubKey;
|
|
35
|
-
|
|
45
|
+
|
|
36
46
|
this.contractService = new EthereumContractService({
|
|
37
47
|
provider: this.provider,
|
|
38
48
|
signer: this.signer,
|
|
39
49
|
});
|
|
40
|
-
|
|
41
|
-
this.
|
|
42
|
-
this.liqClient = new LiqClient(this.contractService);
|
|
50
|
+
|
|
51
|
+
this.convertClient = new ConvertClient(this.contractService);
|
|
43
52
|
this.pretokenClient = new PretokenClient(this.contractService);
|
|
44
53
|
this.stakeClient = new StakeClient(this.contractService);
|
|
45
|
-
|
|
54
|
+
this.oppClient = new OPPClient(this.contractService);
|
|
55
|
+
this.receiptClient = new ReceiptClient(this.contractService);
|
|
56
|
+
}
|
|
46
57
|
catch (error) {
|
|
47
58
|
// console.error('Error initializing EthereumStakingClient:', error);
|
|
48
59
|
throw error;
|
|
49
|
-
}
|
|
60
|
+
}
|
|
50
61
|
}
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
|
|
54
63
|
// ---------------------------------------------------------------------
|
|
55
64
|
// Public IStakingClient Interface Methods
|
|
56
65
|
// ---------------------------------------------------------------------
|
|
@@ -62,40 +71,41 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
62
71
|
* @returns transaction hash
|
|
63
72
|
*/
|
|
64
73
|
async deposit(amount: number | string | bigint | BigNumber): Promise<string> {
|
|
74
|
+
this.ensureUser();
|
|
75
|
+
|
|
65
76
|
const amountWei = BigNumber.isBigNumber(amount)
|
|
66
77
|
? amount
|
|
67
78
|
: BigNumber.from(amount);
|
|
68
79
|
|
|
69
|
-
const result = await this.
|
|
80
|
+
const result = await this.convertClient.performDeposit(amountWei);
|
|
70
81
|
return result.txHash;
|
|
71
82
|
}
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
|
|
75
84
|
/**
|
|
76
85
|
* Withdraw native ETH from the liqETH protocol via the liqeth safeBurn function, which burns the LiqETH and adds the user to the withdrawal queue.
|
|
77
86
|
* @param amount Amount in wei (or something convertible to BigNumber).
|
|
78
87
|
* @returns transaction hash
|
|
79
88
|
*/
|
|
80
89
|
async withdraw(amount: bigint): Promise<string> {
|
|
81
|
-
|
|
90
|
+
this.ensureUser();
|
|
91
|
+
|
|
92
|
+
const address = await this.signer!.getAddress();
|
|
82
93
|
const amountWei = BigNumber.from(amount);
|
|
83
|
-
// const chainId = this.network?.chainId ?? (await this.provider.getNetwork()).chainId;
|
|
84
|
-
// const result = await this.depositClient.requestWithdraw(amountWei, this.signer, chainId);
|
|
85
94
|
|
|
86
|
-
const result = await this.
|
|
95
|
+
const result = await this.convertClient.performWithdraw(address, amountWei)
|
|
87
96
|
return result.txHash;
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
|
|
91
100
|
/**
|
|
92
101
|
* Stake liqETH via DepositManager.
|
|
93
|
-
* @param amount Amount in wei
|
|
94
|
-
* Keep this as a bigint / string in the caller; avoid JS floats.
|
|
102
|
+
* @param amount Amount in wei - Keep this as a bigint / string in the caller; avoid JS floats.
|
|
95
103
|
* @returns transaction hash
|
|
96
104
|
*/
|
|
97
105
|
async stake(amount: bigint): Promise<string> {
|
|
98
|
-
|
|
106
|
+
this.ensureUser();
|
|
107
|
+
|
|
108
|
+
const walletAddress = await this.signer!.getAddress();
|
|
99
109
|
const amountWei = BigNumber.from(amount);
|
|
100
110
|
|
|
101
111
|
const result = await this.stakeClient.performStake(amountWei, walletAddress);
|
|
@@ -116,96 +126,27 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
116
126
|
* @returns the transaction hash
|
|
117
127
|
*/
|
|
118
128
|
async unstakePrelaunch(tokenId: bigint, recipient: string): Promise<string> {
|
|
129
|
+
this.ensureUser();
|
|
130
|
+
|
|
119
131
|
const tokenIdBigNum = BigNumber.from(tokenId)
|
|
120
132
|
const result = await this.stakeClient.performWithdrawStake(tokenIdBigNum, recipient);
|
|
121
133
|
return result.txHash;
|
|
122
134
|
}
|
|
123
135
|
|
|
124
136
|
|
|
125
|
-
|
|
126
137
|
async buy(amount: bigint): Promise<string> {
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
this.ensureUser();
|
|
139
|
+
|
|
140
|
+
const buyer = await this.signer!.getAddress();
|
|
129
141
|
|
|
130
142
|
// ! Hoodi only - check if the mock aggregator price is stale, and if so, update it before submitting the buy request
|
|
131
143
|
await this.updateMockAggregatorPrice();
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const bal = await this.contract.LiqEth.balanceOf(buyer);
|
|
135
|
-
const paused = await this.contract.Depositor.paused();
|
|
136
|
-
if(paused) {
|
|
137
|
-
throw new Error("Error - Depositor is in a paused state");
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// if current liq balance is less than the requested buy amount, throw an error
|
|
141
|
-
if (bal.lt(amount)) {
|
|
142
|
-
throw new Error(`Balance insufficient for pre-token purchase`);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
//check that the contract has allowance for the token
|
|
146
|
-
const depositorAddr = this.contract.Depositor.address;
|
|
147
|
-
const allowance = await this.contract.LiqEth.allowance(buyer, depositorAddr);
|
|
148
|
-
|
|
149
|
-
// if allowance is less than the requested stake amount, request permission to spend LiqEth
|
|
150
|
-
if (allowance.lt(amount)) {
|
|
151
|
-
const liqWrite = this.contractService.getWrite('LiqEth');
|
|
152
|
-
|
|
153
|
-
// currently requested unlimited amount - potentially change to only request up to the current amount?
|
|
154
|
-
const approveAmount = ethers.constants.MaxUint256;
|
|
155
|
-
|
|
156
|
-
console.warn(`allowance insufficient (${allowance.toString()} < ${amount.toString()}); sending approve(${depositorAddr}, ${approveAmount.toString()})`);
|
|
157
|
-
|
|
158
|
-
const approveTx = await liqWrite.approve(depositorAddr, approveAmount);
|
|
159
|
-
await approveTx.wait(1);
|
|
160
|
-
|
|
161
|
-
// re-read allowance to ensure approval succeeded
|
|
162
|
-
const newAllowance = await this.contract.LiqEth.allowance(buyer, depositorAddr);
|
|
163
|
-
if (newAllowance.lt(amount)) {
|
|
164
|
-
throw new Error('Approval failed or allowance still insufficient after approve');
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
let result = await this.pretokenClient.purchasePretokensWithLiqETH(amountBigNum, buyer);
|
|
170
144
|
|
|
145
|
+
let result = await this.pretokenClient.purchasePretokensWithLiqETH(amount, buyer);
|
|
171
146
|
return result && result.txHash ? result.txHash : "Error - no resulting txHash";
|
|
172
147
|
}
|
|
173
148
|
|
|
174
149
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
async getOPPStatus(): Promise<any> {
|
|
178
|
-
return await this.stakeClient.getOppStatus();
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* ETH Prelaunch function to list the ReceiptNFTs owned by a specific user
|
|
185
|
-
* @param address address to query the receipts for
|
|
186
|
-
* @returns array of receipts
|
|
187
|
-
*/
|
|
188
|
-
async fetchPrelaunchReceipts(address?: string): Promise<preLaunchReceipt[]> {
|
|
189
|
-
if(address === undefined) address = await this.signer.getAddress();
|
|
190
|
-
|
|
191
|
-
let receipts = await this.stakeClient.fetchPreLaunchReceipts(address);
|
|
192
|
-
return receipts
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
async getEthStats(): Promise<any> {
|
|
198
|
-
let withdrawDelay = await this.contract.DepositManager.withdrawDelay();
|
|
199
|
-
let minDeposit = await this.contract.DepositManager.minDeposit();
|
|
200
|
-
let rewardCooldown = await this.contract.DepositManager.rewardCooldown();
|
|
201
|
-
|
|
202
|
-
return {
|
|
203
|
-
withdrawDelay,
|
|
204
|
-
minDeposit,
|
|
205
|
-
rewardCooldown,
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
150
|
/**
|
|
210
151
|
* Resolve the user's ETH + liqETH balances.
|
|
211
152
|
*
|
|
@@ -214,9 +155,9 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
214
155
|
* tracked = liqETH tracked balance (protocol/accounting view)
|
|
215
156
|
*/
|
|
216
157
|
async getPortfolio(): Promise<Portfolio> {
|
|
217
|
-
|
|
218
|
-
// console.log('getPortfolio() wallet address', walletAddress)
|
|
158
|
+
this.ensureUser();
|
|
219
159
|
|
|
160
|
+
const walletAddress = await this.signer!.getAddress();
|
|
220
161
|
|
|
221
162
|
// 1) Native ETH balance
|
|
222
163
|
const nativeBalance = await this.provider.getBalance(walletAddress);
|
|
@@ -227,9 +168,15 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
227
168
|
const liqBalance: ethers.BigNumber = await this.contract.LiqEth.balanceOf(walletAddress);
|
|
228
169
|
const liqSymbol = 'Liq' + (this.network?.nativeCurrency?.symbol ?? 'ETH');
|
|
229
170
|
|
|
230
|
-
// 3) staked liqEth ERC-20 balance (
|
|
231
|
-
|
|
171
|
+
// 3) staked liqEth ERC-20 balance (calculate from receipts)
|
|
172
|
+
let stakeReceipts = await this.receiptClient.stakeReceipts(walletAddress);
|
|
173
|
+
let stakeBalanceBN = BigNumber.from(0);
|
|
174
|
+
for (let r of stakeReceipts) {
|
|
175
|
+
stakeBalanceBN = stakeBalanceBN.add(BigNumber.from(r.receipt.principal.amount));
|
|
176
|
+
}
|
|
232
177
|
|
|
178
|
+
// 4) WIRE pretoken balance
|
|
179
|
+
const wireBalance: ethers.BigNumber = await this.contract.Pretoken.balanceOf(walletAddress);
|
|
233
180
|
|
|
234
181
|
const portfolio: Portfolio = {
|
|
235
182
|
native: {
|
|
@@ -243,13 +190,13 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
243
190
|
symbol: liqSymbol,
|
|
244
191
|
},
|
|
245
192
|
staked: {
|
|
246
|
-
amount:
|
|
193
|
+
amount: stakeBalanceBN.toBigInt(),
|
|
247
194
|
decimals: nativeDecimals,
|
|
248
195
|
symbol: liqSymbol,
|
|
249
196
|
},
|
|
250
197
|
wire: {
|
|
251
|
-
amount:
|
|
252
|
-
decimals:
|
|
198
|
+
amount: wireBalance.toBigInt(),
|
|
199
|
+
decimals: 18,
|
|
253
200
|
symbol: '$WIRE',
|
|
254
201
|
},
|
|
255
202
|
chainID: this.network.chainId
|
|
@@ -257,9 +204,74 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
257
204
|
return portfolio;
|
|
258
205
|
}
|
|
259
206
|
|
|
207
|
+
/**
|
|
208
|
+
* ETH Prelaunch function to list the Stake ReceiptNFTs owned by a specific user
|
|
209
|
+
* @param address address to query the receipts for
|
|
210
|
+
* @returns array of receipts
|
|
211
|
+
*/
|
|
212
|
+
async fetchPrelaunchReceipts(address?: string): Promise<preLaunchReceipt[]> {
|
|
213
|
+
this.ensureUser();
|
|
214
|
+
|
|
215
|
+
if (address === undefined) address = await this.signer!.getAddress();
|
|
216
|
+
|
|
217
|
+
//default to stake receipts
|
|
218
|
+
return await this.receiptClient.stakeReceipts(address);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async getOPPMessages(address?: string): Promise<OPPAssertion[]> {
|
|
222
|
+
this.ensureUser();
|
|
223
|
+
|
|
224
|
+
if (!address) address = await this.signer!.getAddress();
|
|
225
|
+
|
|
226
|
+
return await this.oppClient.getMessages(address);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Ensure that signer wallet is available for write operations
|
|
230
|
+
private ensureUser() {
|
|
231
|
+
if (!this.signer) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
'EthereumStakingClient: write operation requires a wallet-connected Web3 provider',
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
// ---------------------------------------------------------------------
|
|
240
|
+
// READ-ONLY Public Methods
|
|
241
|
+
// ---------------------------------------------------------------------
|
|
242
|
+
|
|
243
|
+
// Estimated total APY for staking yeild
|
|
244
|
+
getSystemAPY(): Promise<number> {
|
|
245
|
+
// TODO
|
|
246
|
+
return Promise.resolve(0);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Protocol fee charged for deposit from Native to LIQ
|
|
250
|
+
getDepositFee(amount: bigint): Promise<bigint> {
|
|
251
|
+
// TODO
|
|
252
|
+
return Promise.resolve(BigInt(0));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async getOPPStatus(): Promise<any> {
|
|
256
|
+
return await this.oppClient.getStatus();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async getEthStats(): Promise<any> {
|
|
260
|
+
let withdrawDelay = await this.contract.DepositManager.withdrawDelay();
|
|
261
|
+
let minDeposit = await this.contract.DepositManager.minDeposit();
|
|
262
|
+
let rewardCooldown = await this.contract.DepositManager.rewardCooldown();
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
withdrawDelay,
|
|
266
|
+
minDeposit,
|
|
267
|
+
rewardCooldown,
|
|
268
|
+
}
|
|
269
|
+
}
|
|
260
270
|
|
|
261
271
|
/**
|
|
262
272
|
* Program-level prelaunch WIRE / tranche snapshot for Ethereum
|
|
273
|
+
*
|
|
274
|
+
* SUPPORTS READ-ONLY ACcESS
|
|
263
275
|
*/
|
|
264
276
|
async getTrancheSnapshot(options?: {
|
|
265
277
|
chainID?: ChainID;
|
|
@@ -296,25 +308,25 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
296
308
|
|
|
297
309
|
|
|
298
310
|
// fetch price and timestamp from aggregator
|
|
299
|
-
const [
|
|
311
|
+
const [roundId, answer, startedAt, updatedAt, answeredInRound] = await this.contract.Aggregator.latestRoundData();
|
|
300
312
|
let ethPriceUsd: bigint = BigInt(answer.toString());
|
|
301
313
|
let nativePriceTimestamp: number = Number(updatedAt);
|
|
302
314
|
|
|
303
315
|
// ! Placeholder from hoodi deployment - don't think this can be fetched dynamically
|
|
304
|
-
const initialTrancheSupply = BigInt(60000) * BigInt(1e8);
|
|
316
|
+
const initialTrancheSupply = BigInt(60000) * BigInt(1e8);
|
|
305
317
|
|
|
306
318
|
return buildEthereumTrancheSnapshot({
|
|
307
319
|
chainID,
|
|
308
|
-
totalSharesBn,
|
|
309
|
-
indexBn,
|
|
320
|
+
totalSharesBn,
|
|
321
|
+
indexBn,
|
|
310
322
|
trancheNumberBn,
|
|
311
|
-
currentTrancheSupply,
|
|
312
|
-
tranchePriceWadBn,
|
|
323
|
+
currentTrancheSupply,
|
|
324
|
+
tranchePriceWadBn,
|
|
313
325
|
totalTrancheSupply,
|
|
314
326
|
initialTrancheSupply,
|
|
315
327
|
supplyGrowthBps,
|
|
316
328
|
priceGrowthBps,
|
|
317
|
-
minPriceUsd,
|
|
329
|
+
minPriceUsd,
|
|
318
330
|
maxPriceUsd,
|
|
319
331
|
|
|
320
332
|
ethPriceUsd,
|
|
@@ -324,16 +336,10 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
324
336
|
});
|
|
325
337
|
}
|
|
326
338
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
339
|
// ---------------------------------------------------------------------
|
|
331
340
|
// Internal ETH Staking client helper functions
|
|
332
341
|
// ---------------------------------------------------------------------
|
|
333
342
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
343
|
// ! This is a temporary measure for Hoodi testnet because there is no aggregator deployed
|
|
338
344
|
private async updateMockAggregatorPrice() {
|
|
339
345
|
const aggregator = this.contract.Aggregator;
|
|
@@ -349,7 +355,7 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
349
355
|
// safety check - only run in non-production contexts
|
|
350
356
|
const network = await this.provider.getNetwork();
|
|
351
357
|
const chainId = network.chainId;
|
|
352
|
-
const allowedTestChains = new Set([560048]);
|
|
358
|
+
const allowedTestChains = new Set([560048]);
|
|
353
359
|
if (!allowedTestChains.has(chainId)) {
|
|
354
360
|
console.warn(`MockAggregator is stale (${ageSec}s) but chainId ${chainId} is not a test/local network — skipping update.`);
|
|
355
361
|
return;
|
|
@@ -380,10 +386,4 @@ export class EthereumStakingClient implements IStakingClient {
|
|
|
380
386
|
console.log(`MockAggregator updated ${ageSec}s ago — no update needed`);
|
|
381
387
|
}
|
|
382
388
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
// TODO: implement claimRewards, etc.
|
|
389
389
|
}
|
|
@@ -193,7 +193,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
193
193
|
* Handles tx build, sign, send, and confirmation.
|
|
194
194
|
*/
|
|
195
195
|
async deposit(amountLamports: bigint): Promise<string> {
|
|
196
|
-
this.
|
|
196
|
+
this.ensureUser();
|
|
197
197
|
if (amountLamports <= BigInt(0)) {
|
|
198
198
|
throw new Error('Deposit amount must be greater than zero.');
|
|
199
199
|
}
|
|
@@ -219,7 +219,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
219
219
|
* Actual SOL payout happens later via the operator-side flow.
|
|
220
220
|
*/
|
|
221
221
|
async withdraw(amountLamports: bigint): Promise<string> {
|
|
222
|
-
this.
|
|
222
|
+
this.ensureUser();
|
|
223
223
|
if (amountLamports <= BigInt(0)) {
|
|
224
224
|
throw new Error('Withdraw amount must be greater than zero.');
|
|
225
225
|
}
|
|
@@ -238,7 +238,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
238
238
|
* Stake liqSOL into Outpost (liqSOL → pool) via liqsol_core::synd.
|
|
239
239
|
*/
|
|
240
240
|
async stake(amountLamports: bigint): Promise<string> {
|
|
241
|
-
this.
|
|
241
|
+
this.ensureUser();
|
|
242
242
|
|
|
243
243
|
if (!amountLamports || amountLamports <= BigInt(0)) {
|
|
244
244
|
throw new Error('Stake amount must be greater than zero.');
|
|
@@ -261,7 +261,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
261
261
|
* Unstake liqSOL from Outpost (pool → liqSOL) via liqsol_core::desynd.
|
|
262
262
|
*/
|
|
263
263
|
async unstake(amountLamports: bigint): Promise<string> {
|
|
264
|
-
this.
|
|
264
|
+
this.ensureUser();
|
|
265
265
|
|
|
266
266
|
if (!amountLamports || amountLamports <= BigInt(0)) {
|
|
267
267
|
throw new Error('Unstake amount must be greater than zero.');
|
|
@@ -287,7 +287,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
287
287
|
* instruction under the new IDL (no more native-SOL purchase).
|
|
288
288
|
*/
|
|
289
289
|
async buy(amountLamports: bigint): Promise<string> {
|
|
290
|
-
this.
|
|
290
|
+
this.ensureUser();
|
|
291
291
|
if (!amountLamports || amountLamports <= BigInt(0)) {
|
|
292
292
|
throw new Error('liqSOL pretoken purchase requires a positive amount.');
|
|
293
293
|
}
|
|
@@ -453,6 +453,32 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
453
453
|
};
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Convenience helper to fetch the distribution userRecord for the current user.
|
|
458
|
+
* Used by balance-correction flows and debugging.
|
|
459
|
+
*/
|
|
460
|
+
async getUserRecord() {
|
|
461
|
+
if (!this.pubKey) throw new Error('User pubKey is undefined');
|
|
462
|
+
return this.distributionClient.getUserRecord(this.solPubKey);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
// ---------------------------------------------------------------------
|
|
467
|
+
// READ-ONLY Public Methods
|
|
468
|
+
// ---------------------------------------------------------------------
|
|
469
|
+
|
|
470
|
+
// Estimated total APY for staking yeild
|
|
471
|
+
getSystemAPY(): Promise<number> {
|
|
472
|
+
// TODO
|
|
473
|
+
return Promise.resolve(0);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Protocol fee charged for deposit from Native to LIQ
|
|
477
|
+
getDepositFee(amount: bigint): Promise<bigint> {
|
|
478
|
+
// TODO
|
|
479
|
+
return Promise.resolve(BigInt(0));
|
|
480
|
+
}
|
|
481
|
+
|
|
456
482
|
/**
|
|
457
483
|
* Unified, chain-agnostic tranche snapshot for Solana.
|
|
458
484
|
*
|
|
@@ -493,15 +519,6 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
493
519
|
});
|
|
494
520
|
}
|
|
495
521
|
|
|
496
|
-
/**
|
|
497
|
-
* Convenience helper to fetch the distribution userRecord for the current user.
|
|
498
|
-
* Used by balance-correction flows and debugging.
|
|
499
|
-
*/
|
|
500
|
-
async getUserRecord() {
|
|
501
|
-
if (!this.pubKey) throw new Error('User pubKey is undefined');
|
|
502
|
-
return this.distributionClient.getUserRecord(this.solPubKey);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
522
|
// ---------------------------------------------------------------------
|
|
506
523
|
// Tx helpers
|
|
507
524
|
// ---------------------------------------------------------------------
|
|
@@ -514,7 +531,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
514
531
|
signed: SolanaTransaction,
|
|
515
532
|
ctx: { blockhash: string; lastValidBlockHeight: number },
|
|
516
533
|
): Promise<string> {
|
|
517
|
-
this.
|
|
534
|
+
this.ensureUser();
|
|
518
535
|
|
|
519
536
|
const signature = await this.connection.sendRawTransaction(
|
|
520
537
|
signed.serialize(),
|
|
@@ -549,7 +566,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
549
566
|
async signTransaction(
|
|
550
567
|
tx: SolanaTransaction,
|
|
551
568
|
): Promise<SolanaTransaction> {
|
|
552
|
-
this.
|
|
569
|
+
this.ensureUser();
|
|
553
570
|
return this.anchor.wallet.signTransaction(tx);
|
|
554
571
|
}
|
|
555
572
|
|
|
@@ -560,7 +577,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
560
577
|
async sendTransaction(
|
|
561
578
|
signed: SolanaTransaction,
|
|
562
579
|
): Promise<TransactionSignature> {
|
|
563
|
-
this.
|
|
580
|
+
this.ensureUser();
|
|
564
581
|
return this.anchor.sendAndConfirm(signed);
|
|
565
582
|
}
|
|
566
583
|
|
|
@@ -586,7 +603,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
586
603
|
* Guard for all write operations (deposit/withdraw/stake/unstake/buy).
|
|
587
604
|
* Ensures we have a Wire pubKey and an Anchor wallet pubKey, and that they match.
|
|
588
605
|
*/
|
|
589
|
-
|
|
606
|
+
ensureUser() {
|
|
590
607
|
if (!this.pubKey || !this.anchor.wallet.publicKey) {
|
|
591
608
|
throw new Error('User Authorization required: pubKey is undefined');
|
|
592
609
|
}
|
package/src/types.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { ethers } from 'ethers';
|
|
|
5
5
|
|
|
6
6
|
export type StakerConfig = {
|
|
7
7
|
network: ExternalNetwork;
|
|
8
|
-
provider
|
|
8
|
+
provider?: BaseSignerWalletAdapter | ethers.providers.Web3Provider;
|
|
9
9
|
pubKey?: PublicKey;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -23,6 +23,12 @@ export interface IStakingClient {
|
|
|
23
23
|
/** Fetch the complete user portfolio */
|
|
24
24
|
getPortfolio(): Promise<Portfolio>;
|
|
25
25
|
|
|
26
|
+
// Estimated total APY for staking yeild
|
|
27
|
+
getSystemAPY(): Promise<number>;
|
|
28
|
+
|
|
29
|
+
// Protocol fee charged for deposit from Native to LIQ
|
|
30
|
+
getDepositFee(amount: bigint): Promise<bigint>;
|
|
31
|
+
|
|
26
32
|
/**
|
|
27
33
|
* Program-level prelaunch WIRE/tranche snapshot for this chain.
|
|
28
34
|
*
|
|
@@ -216,4 +222,57 @@ export interface TrancheSnapshot {
|
|
|
216
222
|
* Used directly by the frontend for ladder graphs.
|
|
217
223
|
*/
|
|
218
224
|
ladder: TrancheLadderItem[];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
// Enum describing which asset is being used to buy pretoken
|
|
229
|
+
export enum PurchaseAsset {
|
|
230
|
+
SOL = 'SOL',
|
|
231
|
+
LIQSOL = 'LIQSOL',
|
|
232
|
+
ETH = 'ETH',
|
|
233
|
+
LIQETH = 'LIQETH',
|
|
234
|
+
YIELD = 'YIELD',
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface PurchaseQuote {
|
|
238
|
+
purchaseAsset: PurchaseAsset;
|
|
239
|
+
amountIn: bigint; // lamports / wei / token units
|
|
240
|
+
|
|
241
|
+
/** Expected pretoken “shares” (pretokens) and decimals */
|
|
242
|
+
wireShares: bigint; // 1e8 scale
|
|
243
|
+
wireDecimals: number; // always 8 for now
|
|
244
|
+
|
|
245
|
+
/** Current price + notional in USD (1e8 scale) */
|
|
246
|
+
wirePriceUsd: bigint;
|
|
247
|
+
notionalUsd: bigint;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// export interface OPPEvent {
|
|
251
|
+
// type: 'liq_pretoken_purchase' | 'yield_pretoken_purchase' | 'pretoken_purchase' | 'stake' | 'unstake' | 'unknown';
|
|
252
|
+
// amount: bigint | null;
|
|
253
|
+
// chain: 'ETH' | 'SOL';
|
|
254
|
+
// timestamp: number | null;
|
|
255
|
+
// from: string | null;
|
|
256
|
+
// to: string | null;
|
|
257
|
+
// txHash: string;
|
|
258
|
+
// raw: any;
|
|
259
|
+
// assertions: OPPAssertion[]
|
|
260
|
+
// }
|
|
261
|
+
|
|
262
|
+
export interface OPPAssertion {
|
|
263
|
+
type: 'liq_pretoken_purchase' | 'yield_pretoken_purchase' | 'pretoken_purchase' | 'stake' | 'unstake' | 'bonded_actor' | 'unbonded_actor' | 'unknown',
|
|
264
|
+
data: any
|
|
265
|
+
chain: 'ETH' | 'SOL';
|
|
266
|
+
timestamp: number | null;
|
|
267
|
+
from: string | null;
|
|
268
|
+
to: string | null;
|
|
269
|
+
txHash: string;
|
|
270
|
+
raw: any;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
export enum ReceiptNFTKind {
|
|
276
|
+
STAKE = 0,
|
|
277
|
+
PRETOKEN_PURCHASE = 1,
|
|
219
278
|
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { BigNumber, ethers } from "ethers";
|
|
2
|
-
import { preLaunchReceipt, SharesBurnedEvent, StakedEvent, WithdrawnStakeEvent, WithdrawnStakeResult } from "../types";
|
|
3
|
-
import { EthereumContractService } from "../contract";
|
|
4
|
-
import { formatContractErrors } from "../utils";
|
|
5
|
-
|
|
6
|
-
export class LiqClient {
|
|
7
|
-
|
|
8
|
-
private readonly contractService: EthereumContractService;
|
|
9
|
-
|
|
10
|
-
get contract() { return this.contractService.contract; }
|
|
11
|
-
|
|
12
|
-
constructor(contract: EthereumContractService) {
|
|
13
|
-
this.contractService = contract;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
public async safeBurn(signerAddress: string, amountWei: BigNumber): Promise<any> {
|
|
18
|
-
let tx, receipt;
|
|
19
|
-
try {
|
|
20
|
-
tx = await this.contract.LiqEth.safeBurn(signerAddress, amountWei);
|
|
21
|
-
receipt = await tx.wait(1);
|
|
22
|
-
} catch (err: any) {
|
|
23
|
-
let errorObj = formatContractErrors(err);
|
|
24
|
-
throw new Error(errorObj.name ?? errorObj.raw)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Parse SharesBurned event if present
|
|
28
|
-
let event: SharesBurnedEvent | undefined;
|
|
29
|
-
const ev = receipt.events?.find((e) => e.event === 'SharesBurned');
|
|
30
|
-
|
|
31
|
-
if (ev && ev.args) {
|
|
32
|
-
const { from, shares, tokenValue } = ev.args;
|
|
33
|
-
event = {
|
|
34
|
-
from,
|
|
35
|
-
shares: BigNumber.from(shares),
|
|
36
|
-
tokenValue: BigNumber.from(tokenValue),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
txHash: tx.hash,
|
|
42
|
-
receipt,
|
|
43
|
-
event,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
}
|