@wireio/stake 0.1.0 → 0.1.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/README.md +57 -0
- package/lib/stake.browser.js +4623 -3451
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +372 -537
- package/lib/stake.js +4801 -3574
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +4623 -3451
- package/lib/stake.m.js.map +1 -1
- package/package.json +1 -1
- package/src/assets/solana/idl/liqsol_core.json +4239 -0
- package/src/assets/solana/idl/liqsol_token.json +183 -0
- package/src/assets/solana/idl/validator_leaderboard.json +270 -265
- package/src/assets/solana/types/liqsol_core.ts +4245 -0
- package/src/assets/solana/types/liqsol_token.ts +189 -0
- package/src/assets/solana/types/validator_leaderboard.ts +270 -265
- package/src/index.ts +1 -3
- package/src/networks/ethereum/contract.ts +138 -36
- package/src/networks/ethereum/ethereum.ts +167 -38
- package/src/networks/ethereum/types.ts +32 -1
- package/src/networks/solana/clients/deposit.client.ts +71 -109
- package/src/networks/solana/clients/distribution.client.ts +256 -383
- package/src/networks/solana/clients/leaderboard.client.ts +38 -133
- package/src/networks/solana/constants.ts +214 -130
- package/src/networks/solana/program.ts +25 -38
- package/src/networks/solana/solana.ts +100 -89
- package/src/networks/solana/types.ts +37 -47
- package/src/networks/solana/utils.ts +551 -0
- package/src/scripts/tsconfig.json +17 -0
- package/src/staker/staker.ts +5 -4
- package/src/staker/types.ts +2 -2
- package/src/assets/solana/idl/deposit.json +0 -296
- package/src/assets/solana/idl/distribution.json +0 -768
- package/src/assets/solana/idl/liq_sol_token.json +0 -298
- package/src/assets/solana/idl/mint_helper.json +0 -110
- package/src/assets/solana/idl/read_tracked_balance.json +0 -140
- package/src/assets/solana/idl/stake_controller.json +0 -2149
- package/src/assets/solana/idl/treasury.json +0 -110
- package/src/assets/solana/idl/validator_registry.json +0 -487
- package/src/assets/solana/idl/yield_oracle.json +0 -32
- package/src/assets/solana/types/deposit.ts +0 -302
- package/src/assets/solana/types/distribution.ts +0 -774
- package/src/assets/solana/types/liq_sol_token.ts +0 -304
- package/src/assets/solana/types/mint_helper.ts +0 -116
- package/src/assets/solana/types/read_tracked_balance.ts +0 -146
- package/src/assets/solana/types/stake_controller.ts +0 -2155
- package/src/assets/solana/types/stake_registry.ts +0 -441
- package/src/assets/solana/types/treasury.ts +0 -116
- package/src/assets/solana/types/validator_registry.ts +0 -493
- package/src/assets/solana/types/yield_oracle.ts +0 -38
- package/src/common/utils.ts +0 -9
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module EthereumContractService
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Provides a unified service for interacting with Ethereum smart contracts using ethers.js.
|
|
5
|
-
*
|
|
6
|
-
* This module includes:
|
|
7
|
-
* - ABI imports for ERC20, ERC721, and ERC1155 token standards.
|
|
8
|
-
* - Address book and contract configuration types.
|
|
9
|
-
* - A service class for managing contract instances, interfaces, and utility methods for read/write access.
|
|
10
|
-
* - Utility for decoding custom errors and events using a combined interface of all known ABIs.
|
|
11
|
-
*
|
|
12
|
-
* @remarks
|
|
13
|
-
* - Update the `ADDRESSES` and `CONTRACTS` objects to match your deployment.
|
|
14
|
-
* - The service supports both read-only and write-enabled contract handles, depending on the presence of a signer.
|
|
15
|
-
* - The `omniInterface` property allows decoding of errors and events across all included ABIs.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* const service = new EthereumContractService({ provider, signer });
|
|
20
|
-
* const stakeContract = service.getContract('Stake');
|
|
21
|
-
* const address = service.getAddress('Stake');
|
|
22
|
-
* ```
|
|
23
5
|
*/
|
|
6
|
+
// TODO: adjust these import paths to your actual artifact layout
|
|
7
|
+
// import DepositManagerArtifact from '../../assets/ethereum/ABI/liqeth/DepositManager.sol/DepositManager.json';
|
|
8
|
+
// import LiqEthArtifact from '../../assets/ethereum/ABI/liqeth/liqEth.sol/liqEth.json';
|
|
9
|
+
// import LiqEthMintArtifact from '../../assets/ethereum/ABI/liqeth/liqEthMint.sol/liqEthMint.json';
|
|
10
|
+
// import LiqEthBurnArtifact from '../../assets/ethereum/ABI/liqeth/liqEthBurn.sol/liqEthBurn.json';
|
|
11
|
+
// import LiqEthTreasuryArtifact from '../../assets/ethereum/ABI/liqeth/liqEthTreasury.sol/liqEthTreasury.json';
|
|
12
|
+
// import LiqEthCommonArtifact from '../../assets/ethereum/ABI/liqeth/LiqEthCommon.sol/LiqEthCommon.json';
|
|
13
|
+
// import RewardsERC20Artifact from '../../assets/ethereum/ABI/liqeth/RewardsERC20.sol/RewardsERC20.json';
|
|
14
|
+
// import ValidatorBalanceVerifierArtifact from '../../assets/ethereum/ABI/liqeth/ValidatorBalanceVerifier.sol/ValidatorBalanceVerifier.json';
|
|
15
|
+
// import YieldArtifact from '../../assets/ethereum/ABI/liqeth/Yield.sol/Yield.json';
|
|
16
|
+
// import AccountingArtifact from '../../assets/ethereum/ABI/liqeth/accounting.sol/accounting.json';
|
|
17
|
+
// import StakingModuleArtifact from '../../assets/ethereum/ABI/liqeth/stakingModule.sol/stakingModule.json';
|
|
18
|
+
// import WithdrawalQueueArtifact from '../../assets/ethereum/ABI/liqeth/withdrawalQueue.sol/withdrawalQueue.json';
|
|
19
|
+
// import WithdrawalVaultArtifact from '../../assets/ethereum/ABI/liqeth/withdrawalVault.sol/withdrawalVault.json';
|
|
24
20
|
|
|
25
21
|
import ERC20Artifact from '../../assets/ethereum/ABI/token/ERC20Token.sol/ERC20Token.json';
|
|
26
22
|
import ERC721Artifact from '../../assets/ethereum/ABI/token/ERC721Token.sol/ERC721Token.json';
|
|
@@ -34,9 +30,29 @@ export const ERC20Abi = ERC20Artifact.abi;
|
|
|
34
30
|
export const ERC721Abi = ERC721Artifact.abi;
|
|
35
31
|
export const ERC1155Abi = ERC1155Artifact.abi;
|
|
36
32
|
|
|
37
|
-
//
|
|
33
|
+
// -----------------------------------------------------------------------------
|
|
34
|
+
// Address book
|
|
35
|
+
// -----------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
// Make sure ContractName in ./types includes all of these keys.
|
|
38
38
|
export const ADDRESSES: AddressBook = {
|
|
39
|
-
Stake
|
|
39
|
+
// Outpost/Stake entrypoint
|
|
40
|
+
Stake: '0xEeaCBa95852324eAc63fE2711aFe0283adf3bC93', // TODO
|
|
41
|
+
|
|
42
|
+
// liqETH protocol suite
|
|
43
|
+
DepositManager: '0xDepositManagerAddress',
|
|
44
|
+
LiqEth: '0xLiqEthTokenAddress',
|
|
45
|
+
LiqEthMint: '0xLiqEthMintAddress',
|
|
46
|
+
LiqEthBurn: '0xLiqEthBurnAddress',
|
|
47
|
+
LiqEthTreasury: '0xLiqEthTreasuryAddress',
|
|
48
|
+
LiqEthCommon: '0xLiqEthCommonAddress',
|
|
49
|
+
RewardsERC20: '0xRewardsErc20Address',
|
|
50
|
+
ValidatorBalanceVerifier: '0xValidatorBalanceVerifierAddress',
|
|
51
|
+
Yield: '0xYieldAddress',
|
|
52
|
+
Accounting: '0xAccountingAddress',
|
|
53
|
+
StakingModule: '0xStakingModuleAddress',
|
|
54
|
+
WithdrawalQueue: '0xWithdrawalQueueAddress',
|
|
55
|
+
WithdrawalVault: '0xWithdrawalVaultAddress',
|
|
40
56
|
};
|
|
41
57
|
|
|
42
58
|
export type Contracts<T extends string = ContractName> = Record<T, ContractConfig>;
|
|
@@ -44,12 +60,83 @@ export type Contracts<T extends string = ContractName> = Record<T, ContractConfi
|
|
|
44
60
|
export type ContractConfig = {
|
|
45
61
|
address: string;
|
|
46
62
|
abi: JsonFragment[];
|
|
47
|
-
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// -----------------------------------------------------------------------------
|
|
66
|
+
// Contract config
|
|
67
|
+
// -----------------------------------------------------------------------------
|
|
48
68
|
|
|
49
69
|
export const CONTRACTS: Contracts<ContractName> = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
// Outpost/Stake (wire ABI when available)
|
|
71
|
+
Stake: { address: ADDRESSES.Stake, abi: undefined as any }, // TODO: Stake artifact
|
|
72
|
+
|
|
73
|
+
// liqETH suite
|
|
74
|
+
LiqEth: {
|
|
75
|
+
address: ADDRESSES.LiqEth,
|
|
76
|
+
abi: undefined as any,
|
|
77
|
+
// abi: LiqEthArtifact.abi as JsonFragment[],
|
|
78
|
+
},
|
|
79
|
+
DepositManager: {
|
|
80
|
+
address: ADDRESSES.DepositManager,
|
|
81
|
+
abi: undefined as any,
|
|
82
|
+
// abi: DepositManagerArtifact.abi as JsonFragment[],
|
|
83
|
+
},
|
|
84
|
+
LiqEthMint: {
|
|
85
|
+
address: ADDRESSES.LiqEthMint,
|
|
86
|
+
abi: undefined as any,
|
|
87
|
+
// abi: LiqEthMintArtifact.abi as JsonFragment[],
|
|
88
|
+
},
|
|
89
|
+
LiqEthBurn: {
|
|
90
|
+
address: ADDRESSES.LiqEthBurn,
|
|
91
|
+
abi: undefined as any,
|
|
92
|
+
// abi: LiqEthBurnArtifact.abi as JsonFragment[],
|
|
93
|
+
},
|
|
94
|
+
LiqEthTreasury: {
|
|
95
|
+
address: ADDRESSES.LiqEthTreasury,
|
|
96
|
+
abi: undefined as any,
|
|
97
|
+
// abi: LiqEthTreasuryArtifact.abi as JsonFragment[],
|
|
98
|
+
},
|
|
99
|
+
LiqEthCommon: {
|
|
100
|
+
address: ADDRESSES.LiqEthCommon,
|
|
101
|
+
abi: undefined as any,
|
|
102
|
+
// abi: LiqEthCommonArtifact.abi as JsonFragment[],
|
|
103
|
+
},
|
|
104
|
+
RewardsERC20: {
|
|
105
|
+
address: ADDRESSES.RewardsERC20,
|
|
106
|
+
abi: undefined as any,
|
|
107
|
+
// abi: RewardsERC20Artifact.abi as JsonFragment[],
|
|
108
|
+
},
|
|
109
|
+
ValidatorBalanceVerifier: {
|
|
110
|
+
address: ADDRESSES.ValidatorBalanceVerifier,
|
|
111
|
+
abi: undefined as any,
|
|
112
|
+
// abi: ValidatorBalanceVerifierArtifact.abi as JsonFragment[],
|
|
113
|
+
},
|
|
114
|
+
Yield: {
|
|
115
|
+
address: ADDRESSES.Yield,
|
|
116
|
+
abi: undefined as any,
|
|
117
|
+
// abi: YieldArtifact.abi as JsonFragment[],
|
|
118
|
+
},
|
|
119
|
+
Accounting: {
|
|
120
|
+
address: ADDRESSES.Accounting,
|
|
121
|
+
abi: undefined as any,
|
|
122
|
+
// abi: AccountingArtifact.abi as JsonFragment[],
|
|
123
|
+
},
|
|
124
|
+
StakingModule: {
|
|
125
|
+
address: ADDRESSES.StakingModule,
|
|
126
|
+
abi: undefined as any,
|
|
127
|
+
// abi: StakingModuleArtifact.abi as JsonFragment[],
|
|
128
|
+
},
|
|
129
|
+
WithdrawalQueue: {
|
|
130
|
+
address: ADDRESSES.WithdrawalQueue,
|
|
131
|
+
abi: undefined as any,
|
|
132
|
+
// abi: WithdrawalQueueArtifact.abi as JsonFragment[],
|
|
133
|
+
},
|
|
134
|
+
WithdrawalVault: {
|
|
135
|
+
address: ADDRESSES.WithdrawalVault,
|
|
136
|
+
abi: undefined as any,
|
|
137
|
+
// abi: WithdrawalVaultArtifact.abi as JsonFragment[],
|
|
138
|
+
},
|
|
139
|
+
};
|
|
53
140
|
|
|
54
141
|
export interface ContractOptions {
|
|
55
142
|
/** RPC endpoint or injected EIP-1193 provider */
|
|
@@ -58,7 +145,6 @@ export interface ContractOptions {
|
|
|
58
145
|
signer?: Signer;
|
|
59
146
|
}
|
|
60
147
|
|
|
61
|
-
|
|
62
148
|
export class EthereumContractService {
|
|
63
149
|
private provider?: providers.Provider;
|
|
64
150
|
private signer?: Signer;
|
|
@@ -73,7 +159,11 @@ export class EthereumContractService {
|
|
|
73
159
|
(Object.keys(CONTRACTS) as ContractName[]).forEach((name) => {
|
|
74
160
|
const { address, abi } = CONTRACTS[name];
|
|
75
161
|
this.iface[name] = new ethers.utils.Interface(abi);
|
|
76
|
-
this.contract[name] = new ethers.Contract(
|
|
162
|
+
this.contract[name] = new ethers.Contract(
|
|
163
|
+
address,
|
|
164
|
+
abi,
|
|
165
|
+
this.signer ?? this.provider
|
|
166
|
+
);
|
|
77
167
|
});
|
|
78
168
|
}
|
|
79
169
|
|
|
@@ -83,13 +173,13 @@ export class EthereumContractService {
|
|
|
83
173
|
}
|
|
84
174
|
|
|
85
175
|
/** Utility: get a read-only handle even if signer is present */
|
|
86
|
-
getReadOnly(name: ContractName): Contract {
|
|
176
|
+
public getReadOnly(name: ContractName): Contract {
|
|
87
177
|
const { address, abi } = CONTRACTS[name];
|
|
88
178
|
return new Contract(address, abi, this.provider);
|
|
89
179
|
}
|
|
90
180
|
|
|
91
181
|
/** Utility: get a write-enabled handle (throws if no signer) */
|
|
92
|
-
getWrite(name: ContractName): Contract {
|
|
182
|
+
public getWrite(name: ContractName): Contract {
|
|
93
183
|
if (!this.signer) throw new Error('No signer available for write calls');
|
|
94
184
|
const { address, abi } = CONTRACTS[name];
|
|
95
185
|
return new Contract(address, abi, this.signer);
|
|
@@ -119,27 +209,37 @@ export class EthereumContractService {
|
|
|
119
209
|
return ctr;
|
|
120
210
|
}
|
|
121
211
|
|
|
122
|
-
/** A unified Interface containing all ABIs
|
|
123
|
-
* to parse custom errors or events. */
|
|
212
|
+
/** A unified Interface containing all ABIs to parse custom errors or events. */
|
|
124
213
|
public get omniInterface(): ethers.utils.Interface {
|
|
125
214
|
const all: any[] = [];
|
|
126
215
|
|
|
127
|
-
// Add all artifacts to the interface
|
|
128
216
|
const allArtifacts = [
|
|
129
217
|
ERC20Artifact,
|
|
130
218
|
ERC721Artifact,
|
|
131
219
|
ERC1155Artifact,
|
|
132
|
-
|
|
220
|
+
|
|
221
|
+
// liqETH suite
|
|
222
|
+
// DepositManagerArtifact,
|
|
223
|
+
// LiqEthArtifact,
|
|
224
|
+
// LiqEthMintArtifact,
|
|
225
|
+
// LiqEthBurnArtifact,
|
|
226
|
+
// LiqEthTreasuryArtifact,
|
|
227
|
+
// LiqEthCommonArtifact,
|
|
228
|
+
// RewardsERC20Artifact,
|
|
229
|
+
// ValidatorBalanceVerifierArtifact,
|
|
230
|
+
// YieldArtifact,
|
|
231
|
+
// AccountingArtifact,
|
|
232
|
+
// StakingModuleArtifact,
|
|
233
|
+
// WithdrawalQueueArtifact,
|
|
234
|
+
// WithdrawalVaultArtifact,
|
|
133
235
|
];
|
|
134
236
|
|
|
135
|
-
// Add all ABIs to the omnibus interface
|
|
136
237
|
allArtifacts.forEach(artifact => {
|
|
137
238
|
if (artifact && artifact.abi) {
|
|
138
239
|
all.push(...artifact.abi);
|
|
139
240
|
}
|
|
140
241
|
});
|
|
141
242
|
|
|
142
|
-
// dedupe
|
|
143
243
|
const seen = new Set<string>();
|
|
144
244
|
const dedup: any[] = [];
|
|
145
245
|
for (const item of all) {
|
|
@@ -150,7 +250,9 @@ export class EthereumContractService {
|
|
|
150
250
|
seen.add(key);
|
|
151
251
|
dedup.push(item);
|
|
152
252
|
}
|
|
153
|
-
} catch {
|
|
253
|
+
} catch {
|
|
254
|
+
// ignore bad fragments
|
|
255
|
+
}
|
|
154
256
|
}
|
|
155
257
|
|
|
156
258
|
return new ethers.utils.Interface(dedup);
|
|
@@ -1,64 +1,193 @@
|
|
|
1
1
|
// src/networks/ethereum/ethereum.ts
|
|
2
2
|
|
|
3
|
-
import { ethers } from 'ethers';
|
|
4
|
-
import { IStakingClient, StakerConfig } from '../../staker/types';
|
|
3
|
+
import { BigNumber, BigNumberish, ethers } from 'ethers';
|
|
4
|
+
import { IStakingClient, Portfolio, StakerConfig } from '../../staker/types';
|
|
5
5
|
import { PublicKey as WirePubKey } from '@wireio/core';
|
|
6
|
-
import { ERC20Abi } from './contract'; // TODO replace with staking contract ABI
|
|
7
|
-
|
|
8
|
-
// — replace with your actual staking contract ABI & address —
|
|
9
|
-
const STAKING_CONTRACT_ADDRESS = '0xYourStakingContractAddress';
|
|
6
|
+
import { ERC20Abi, EthereumContractService } from './contract'; // TODO replace with staking contract ABI
|
|
7
|
+
import { DepositEvent, DepositResult } from './types';
|
|
10
8
|
|
|
11
9
|
// TODO extend to implement ISTAKINGCLIENT
|
|
12
|
-
export class EthereumStakingClient {
|
|
10
|
+
export class EthereumStakingClient implements IStakingClient {
|
|
13
11
|
public readonly pubKey: WirePubKey;
|
|
14
12
|
private readonly provider: ethers.providers.Web3Provider;
|
|
15
13
|
private readonly signer: ethers.Signer;
|
|
16
|
-
private readonly
|
|
14
|
+
private readonly contractService: EthereumContractService;
|
|
15
|
+
|
|
16
|
+
get contract() { return this.contractService.contract; }
|
|
17
|
+
get network() { return this.config.network; }
|
|
17
18
|
|
|
18
19
|
constructor(private config: StakerConfig) {
|
|
19
|
-
|
|
20
|
-
if (!ethers.providers.Web3Provider.isProvider(config.provider as any)) {
|
|
20
|
+
if (!ethers.providers.Web3Provider.isProvider(config.provider)) {
|
|
21
21
|
throw new Error('Expected a Web3Provider for Ethereum');
|
|
22
22
|
}
|
|
23
23
|
this.provider = config.provider as ethers.providers.Web3Provider;
|
|
24
|
-
|
|
25
|
-
// 2) signer
|
|
26
24
|
this.signer = this.provider.getSigner();
|
|
27
25
|
this.pubKey = config.pubKey as WirePubKey;
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.signer
|
|
34
|
-
);
|
|
27
|
+
this.contractService = new EthereumContractService({
|
|
28
|
+
provider: this.provider,
|
|
29
|
+
signer: this.signer,
|
|
30
|
+
});
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
33
|
+
// ---------------------------------------------------------------------
|
|
34
|
+
// Public IStakingClient.deposit
|
|
35
|
+
// ---------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Deposit native ETH into the liqETH protocol via DepositManager.
|
|
39
|
+
* @param amount Amount in wei (or something convertible to BigNumber).
|
|
40
|
+
* Keep this as a bigint / string in the caller; avoid JS floats.
|
|
41
|
+
* @returns transaction hash
|
|
42
|
+
*/
|
|
43
|
+
async deposit(amount: number | string | bigint | BigNumber): Promise<string> {
|
|
44
|
+
const amountWei = BigNumber.isBigNumber(amount)
|
|
45
|
+
? amount
|
|
46
|
+
: BigNumber.from(amount);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
const result = await this.performDeposit(amountWei);
|
|
49
|
+
// For now, IStakingClient contract is: just return tx hash.
|
|
50
|
+
// If/when you extend the interface, you can surface more here.
|
|
51
|
+
return result.txHash;
|
|
51
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Simulate a deposit via callStatic.
|
|
55
|
+
*
|
|
56
|
+
* Useful for pre-flight checks; will throw with the same revert
|
|
57
|
+
* reason as a real tx if it would fail.
|
|
58
|
+
*/
|
|
59
|
+
async simulateDeposit(amount: number | string | bigint | BigNumber): Promise<void> {
|
|
60
|
+
const amountWei = BigNumber.isBigNumber(amount)
|
|
61
|
+
? amount
|
|
62
|
+
: BigNumber.from(amount);
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
data: this.contract.interface.encodeFunctionData('deposit', [
|
|
58
|
-
ethers.BigNumber.from(amount)
|
|
59
|
-
]),
|
|
64
|
+
// callStatic executes the function locally without sending a tx.
|
|
65
|
+
// deposit() doesn't return anything, so we only care if it reverts.
|
|
66
|
+
await this.contract.DepositManager.callStatic.deposit({
|
|
67
|
+
value: amountWei,
|
|
60
68
|
});
|
|
61
69
|
}
|
|
62
70
|
|
|
71
|
+
private async performDeposit(amountWei: BigNumber): Promise<DepositResult> {
|
|
72
|
+
// Pre-check minDeposit
|
|
73
|
+
const minDeposit: BigNumber = await this.contract.DepositManager.minDeposit();
|
|
74
|
+
if (amountWei.lt(minDeposit)) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Deposit amount below minDeposit: ` +
|
|
77
|
+
`amount=${ethers.utils.formatEther(amountWei)} ETH, ` +
|
|
78
|
+
`min=${ethers.utils.formatEther(minDeposit)} ETH`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Send the payable tx
|
|
83
|
+
const tx = await this.contract.DepositManager.deposit({
|
|
84
|
+
value: amountWei,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Wait for 1 confirmation
|
|
88
|
+
const receipt = await tx.wait(1);
|
|
89
|
+
|
|
90
|
+
// Parse Deposited event if present
|
|
91
|
+
let deposited: DepositEvent | undefined;
|
|
92
|
+
const ev = receipt.events?.find((e) => e.event === 'Deposited');
|
|
93
|
+
|
|
94
|
+
if (ev && ev.args) {
|
|
95
|
+
const { user, netEth, fee, shares } = ev.args;
|
|
96
|
+
deposited = {
|
|
97
|
+
user,
|
|
98
|
+
netEth: BigNumber.from(netEth),
|
|
99
|
+
fee: BigNumber.from(fee),
|
|
100
|
+
shares: BigNumber.from(shares),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
txHash: tx.hash,
|
|
106
|
+
receipt,
|
|
107
|
+
deposited,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Build, sign, and submit a single transaction that:
|
|
113
|
+
* - Corrects other users (if needed) to free available balance, then
|
|
114
|
+
* - Registers the caller’s untracked liqSOL.
|
|
115
|
+
*
|
|
116
|
+
* @param amount Optional: register a smaller amount than your full untracked balance.
|
|
117
|
+
* @returns signature string
|
|
118
|
+
*/
|
|
119
|
+
async register(amount?: bigint): Promise<string> {
|
|
120
|
+
try {
|
|
121
|
+
return 'Not implemented yet for Ethereum';
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error('Error in register:', error);
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resolve the user's ETH + liqETH balances.
|
|
130
|
+
*
|
|
131
|
+
* native = ETH in wallet
|
|
132
|
+
* actual = liqETH token balance (ERC-20)
|
|
133
|
+
* tracked = liqETH tracked balance (protocol/accounting view)
|
|
134
|
+
*/
|
|
135
|
+
async getPortfolio(): Promise<Portfolio> {
|
|
136
|
+
const walletAddress = await this.signer.getAddress();
|
|
137
|
+
|
|
138
|
+
// 1) Native ETH balance
|
|
139
|
+
const nativeBalance = await this.provider.getBalance(walletAddress);
|
|
140
|
+
const nativeDecimals =
|
|
141
|
+
this.network?.nativeCurrency?.decimals ?? 18;
|
|
142
|
+
const nativeSymbol =
|
|
143
|
+
this.network?.nativeCurrency?.symbol ?? 'ETH';
|
|
144
|
+
|
|
145
|
+
// 2) liqETH ERC-20 balance (actual)
|
|
146
|
+
const actualBalance: ethers.BigNumber = await this.contract.LiqEth.balanceOf(walletAddress);
|
|
147
|
+
const liqDecimals = 18; // if you want, read decimals() once and cache
|
|
148
|
+
const liqSymbol = 'liqETH';
|
|
149
|
+
|
|
150
|
+
// 3) Protocol "tracked" balance (Accounting)
|
|
151
|
+
let trackedBalance: ethers.BigNumber = actualBalance;
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
// Adjust this to your real view method:
|
|
155
|
+
// e.g. function getUserBalance(address user)
|
|
156
|
+
const result = await this.contract.Accounting.getUserBalance(walletAddress);
|
|
157
|
+
|
|
158
|
+
// Support either struct or plain uint256
|
|
159
|
+
if (result && typeof result === 'object' && 'amount' in result) {
|
|
160
|
+
trackedBalance = result.amount;
|
|
161
|
+
// if result.decimals exists and differs, override liqDecimals if desired
|
|
162
|
+
} else if (result != null) {
|
|
163
|
+
trackedBalance = result;
|
|
164
|
+
}
|
|
165
|
+
} catch (err) {
|
|
166
|
+
console.warn(
|
|
167
|
+
'[EthereumStakingClient] getPortfolio: Accounting view unavailable, using actual balance as tracked.',
|
|
168
|
+
err
|
|
169
|
+
);
|
|
170
|
+
trackedBalance = actualBalance;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
native: {
|
|
175
|
+
amount: nativeBalance,
|
|
176
|
+
decimals: nativeDecimals,
|
|
177
|
+
symbol: nativeSymbol,
|
|
178
|
+
},
|
|
179
|
+
actual: {
|
|
180
|
+
amount: actualBalance,
|
|
181
|
+
decimals: liqDecimals,
|
|
182
|
+
symbol: liqSymbol,
|
|
183
|
+
},
|
|
184
|
+
tracked: {
|
|
185
|
+
amount: trackedBalance,
|
|
186
|
+
decimals: liqDecimals,
|
|
187
|
+
symbol: liqSymbol,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
63
192
|
// TODO: implement withdraw, claimRewards, etc.
|
|
64
193
|
}
|
|
@@ -1,6 +1,37 @@
|
|
|
1
|
+
import { BigNumber, ethers } from 'ethers';
|
|
2
|
+
|
|
1
3
|
export const CONTRACT_NAMES = [
|
|
2
4
|
'Stake',
|
|
5
|
+
'LiqEth',
|
|
6
|
+
'DepositManager',
|
|
7
|
+
'LiqEthMint',
|
|
8
|
+
'LiqEthBurn',
|
|
9
|
+
'LiqEthTreasury',
|
|
10
|
+
'LiqEthCommon',
|
|
11
|
+
'RewardsERC20',
|
|
12
|
+
'ValidatorBalanceVerifier',
|
|
13
|
+
'Yield',
|
|
14
|
+
'Accounting',
|
|
15
|
+
'StakingModule',
|
|
16
|
+
'WithdrawalQueue',
|
|
17
|
+
'WithdrawalVault',
|
|
3
18
|
] as const;
|
|
4
19
|
|
|
5
20
|
export type ContractName = typeof CONTRACT_NAMES[number];
|
|
6
|
-
export type AddressBook = Record<ContractName, string>;
|
|
21
|
+
export type AddressBook = Record<ContractName, string>;
|
|
22
|
+
|
|
23
|
+
export interface DepositEvent {
|
|
24
|
+
user: string;
|
|
25
|
+
netEth: BigNumber;
|
|
26
|
+
fee: BigNumber;
|
|
27
|
+
shares: BigNumber;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DepositResult {
|
|
31
|
+
/** EVM transaction hash */
|
|
32
|
+
txHash: string;
|
|
33
|
+
/** Full receipt, if you want it */
|
|
34
|
+
receipt: ethers.providers.TransactionReceipt;
|
|
35
|
+
/** Parsed Deposited event, if present */
|
|
36
|
+
deposited?: DepositEvent;
|
|
37
|
+
}
|