@wireio/stake 0.7.1 → 0.7.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 +43 -34
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +4 -1
- package/lib/stake.js +50 -38
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +43 -34
- package/lib/stake.m.js.map +1 -1
- package/package.json +1 -1
- package/src/networks/solana/solana.ts +75 -52
package/package.json
CHANGED
|
@@ -677,7 +677,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
677
677
|
* in the wallet so that a MAX deposit (balance - buffer) will succeed.
|
|
678
678
|
*
|
|
679
679
|
* It accounts for:
|
|
680
|
-
* - Runtime tx fees (via
|
|
680
|
+
* - Runtime tx fees (via cached dummy self-transfer fee)
|
|
681
681
|
* - Protocol deposit fee (via getDepositFee(amount))
|
|
682
682
|
*
|
|
683
683
|
* Intended UI usage:
|
|
@@ -696,7 +696,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
696
696
|
const payer = this.solPubKey;
|
|
697
697
|
|
|
698
698
|
// -------------------------------------------------------------
|
|
699
|
-
// 1) Current wallet balance
|
|
699
|
+
// 1) Current wallet balance (prefer caller override)
|
|
700
700
|
// -------------------------------------------------------------
|
|
701
701
|
const balanceLamports: bigint =
|
|
702
702
|
options?.balanceOverrideLamports ??
|
|
@@ -707,32 +707,12 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
707
707
|
}
|
|
708
708
|
|
|
709
709
|
// -------------------------------------------------------------
|
|
710
|
-
// 2) Estimate gas buffer
|
|
710
|
+
// 2) Estimate gas buffer (using cached single-tx fee)
|
|
711
711
|
// -------------------------------------------------------------
|
|
712
712
|
let gasBuffer = BigInt(0);
|
|
713
713
|
|
|
714
714
|
try {
|
|
715
|
-
const
|
|
716
|
-
fromPubkey: payer,
|
|
717
|
-
toPubkey: payer,
|
|
718
|
-
lamports: 0,
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
const tx = new Transaction().add(dummyIx);
|
|
722
|
-
const { blockhash } = await this.connection.getLatestBlockhash(
|
|
723
|
-
commitment,
|
|
724
|
-
);
|
|
725
|
-
tx.recentBlockhash = blockhash;
|
|
726
|
-
tx.feePayer = payer;
|
|
727
|
-
|
|
728
|
-
const message = tx.compileMessage();
|
|
729
|
-
const feeInfo = await this.connection.getFeeForMessage(
|
|
730
|
-
message,
|
|
731
|
-
commitment,
|
|
732
|
-
);
|
|
733
|
-
|
|
734
|
-
// Fallback to a conservative default if RPC doesn't give a value.
|
|
735
|
-
const singleTxFeeLamports = BigInt(feeInfo.value ?? 5000);
|
|
715
|
+
const singleTxFeeLamports = await this.getSingleTxFeeLamports();
|
|
736
716
|
|
|
737
717
|
const txCount = BigInt(options?.txCount ?? 2);
|
|
738
718
|
const safetyMultiplier = options?.safetyMultiplier ?? 3;
|
|
@@ -752,12 +732,11 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
752
732
|
|
|
753
733
|
gasBuffer = buf;
|
|
754
734
|
} catch {
|
|
755
|
-
// If fee estimation fails, fall back to
|
|
756
|
-
// (we will still leave room for protocol fee below).
|
|
735
|
+
// If fee estimation fails, just fall back to "no gas buffer".
|
|
757
736
|
gasBuffer = BigInt(0);
|
|
758
737
|
}
|
|
759
738
|
|
|
760
|
-
// If
|
|
739
|
+
// If gas buffer alone eats the whole balance, we just keep everything.
|
|
761
740
|
if (balanceLamports <= gasBuffer) {
|
|
762
741
|
return balanceLamports;
|
|
763
742
|
}
|
|
@@ -772,35 +751,43 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
772
751
|
}
|
|
773
752
|
|
|
774
753
|
// -------------------------------------------------------------
|
|
775
|
-
// 4)
|
|
776
|
-
//
|
|
777
|
-
//
|
|
754
|
+
// 4) Approximate principal using linear fee model from ONE RPC call
|
|
755
|
+
//
|
|
756
|
+
// fee(a) ≈ k * a
|
|
757
|
+
// want: a + k*a <= spendable → a <= spendable / (1 + k)
|
|
758
|
+
//
|
|
759
|
+
// We estimate k from fee(spendable) / spendable.
|
|
778
760
|
// -------------------------------------------------------------
|
|
779
|
-
let
|
|
780
|
-
let hi = spendable;
|
|
781
|
-
|
|
782
|
-
for (let i = 0; i < 20 && hi - lo > BigInt(1); i++) {
|
|
783
|
-
const mid = (lo + hi) / BigInt(2);
|
|
784
|
-
|
|
785
|
-
let fee: bigint;
|
|
786
|
-
try {
|
|
787
|
-
fee = await this.getDepositFee(mid);
|
|
788
|
-
} catch {
|
|
789
|
-
// If fee lookup fails mid-search, fall back to gas-only buffer.
|
|
790
|
-
return gasBuffer;
|
|
791
|
-
}
|
|
761
|
+
let feeAtSpendable: bigint;
|
|
792
762
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
763
|
+
try {
|
|
764
|
+
feeAtSpendable = await this.getDepositFee(spendable);
|
|
765
|
+
} catch {
|
|
766
|
+
// If protocol fee lookup fails, fall back to gas-only buffer
|
|
767
|
+
return gasBuffer;
|
|
798
768
|
}
|
|
799
769
|
|
|
800
|
-
//
|
|
801
|
-
|
|
770
|
+
// If there is effectively no protocol fee, keep only gas buffer.
|
|
771
|
+
if (feeAtSpendable <= BigInt(0)) {
|
|
772
|
+
return gasBuffer;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
const s = spendable;
|
|
776
|
+
const f = feeAtSpendable;
|
|
777
|
+
|
|
778
|
+
// denom = s + f = s * (1 + k) (since f ≈ k*s)
|
|
779
|
+
const denom = s + f;
|
|
780
|
+
if (denom === BigInt(0)) {
|
|
781
|
+
return gasBuffer;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// a ≈ floor( s^2 / (s + f) )
|
|
785
|
+
let a = (s * s) / denom;
|
|
786
|
+
|
|
787
|
+
// Tiny safety haircut to avoid edge off-by-one lamports
|
|
788
|
+
const fudge = BigInt(10_000); // ≈ 0.00001 SOL
|
|
802
789
|
let effectivePrincipal =
|
|
803
|
-
|
|
790
|
+
a > fudge ? a - fudge : a;
|
|
804
791
|
|
|
805
792
|
if (effectivePrincipal < BigInt(0)) {
|
|
806
793
|
effectivePrincipal = BigInt(0);
|
|
@@ -810,10 +797,46 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
810
797
|
? balanceLamports - effectivePrincipal
|
|
811
798
|
: balanceLamports;
|
|
812
799
|
|
|
813
|
-
// Ensure we never
|
|
800
|
+
// Ensure we never under-reserve gas.
|
|
814
801
|
return buffer < gasBuffer ? gasBuffer : buffer;
|
|
815
802
|
}
|
|
816
803
|
|
|
804
|
+
private cachedTxFee?: { value: bigint; fetchedAt: number };
|
|
805
|
+
private static readonly FEE_CACHE_TTL_MS = 60_000; // 60s
|
|
806
|
+
|
|
807
|
+
private async getSingleTxFeeLamports(): Promise<bigint> {
|
|
808
|
+
const now = Date.now();
|
|
809
|
+
|
|
810
|
+
if (this.cachedTxFee && now - this.cachedTxFee.fetchedAt < SolanaStakingClient.FEE_CACHE_TTL_MS) {
|
|
811
|
+
return this.cachedTxFee.value;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
const payer = this.solPubKey;
|
|
815
|
+
|
|
816
|
+
const dummyIx = SystemProgram.transfer({
|
|
817
|
+
fromPubkey: payer,
|
|
818
|
+
toPubkey: payer,
|
|
819
|
+
lamports: 0,
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
const tx = new Transaction().add(dummyIx);
|
|
823
|
+
const { blockhash } = await this.connection.getLatestBlockhash(commitment);
|
|
824
|
+
tx.recentBlockhash = blockhash;
|
|
825
|
+
tx.feePayer = payer;
|
|
826
|
+
|
|
827
|
+
const message = tx.compileMessage();
|
|
828
|
+
const feeInfo = await this.connection.getFeeForMessage(message, commitment);
|
|
829
|
+
|
|
830
|
+
const singleTxFeeLamports = BigInt(feeInfo.value ?? 5000);
|
|
831
|
+
|
|
832
|
+
this.cachedTxFee = {
|
|
833
|
+
value: singleTxFeeLamports,
|
|
834
|
+
fetchedAt: now,
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
return singleTxFeeLamports;
|
|
838
|
+
}
|
|
839
|
+
|
|
817
840
|
// ---------------------------------------------------------------------
|
|
818
841
|
// Tx helpers
|
|
819
842
|
// ---------------------------------------------------------------------
|