nos-rescue 0.1.0
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/LICENSE +21 -0
- package/README.md +171 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +196 -0
- package/dist/cli.js.map +1 -0
- package/dist/lib/browserWallet.d.ts +8 -0
- package/dist/lib/browserWallet.js +19 -0
- package/dist/lib/browserWallet.js.map +1 -0
- package/dist/lib/constants.d.ts +20 -0
- package/dist/lib/constants.js +24 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/index.d.ts +7 -0
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/rescue.d.ts +85 -0
- package/dist/lib/rescue.js +293 -0
- package/dist/lib/rescue.js.map +1 -0
- package/dist/lib/rewards.d.ts +48 -0
- package/dist/lib/rewards.js +105 -0
- package/dist/lib/rewards.js.map +1 -0
- package/dist/lib/stake.d.ts +61 -0
- package/dist/lib/stake.js +111 -0
- package/dist/lib/stake.js.map +1 -0
- package/dist/lib/sweep.d.ts +34 -0
- package/dist/lib/sweep.js +129 -0
- package/dist/lib/sweep.js.map +1 -0
- package/dist/lib/wallets.d.ts +14 -0
- package/dist/lib/wallets.js +65 -0
- package/dist/lib/wallets.js.map +1 -0
- package/dist/lib/watch.d.ts +41 -0
- package/dist/lib/watch.js +140 -0
- package/dist/lib/watch.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { findAssociatedTokenPda } from '@solana-program/token';
|
|
2
|
+
import { NOS_DECIMALS, NOS_MINT, TOKEN_PROGRAM } from './constants.js';
|
|
3
|
+
import { buildCloseStakeInstruction, buildUnstakePlan, buildWithdrawInstruction, fetchStakeInfo, } from './stake.js';
|
|
4
|
+
import { buildSweepInstructions } from './sweep.js';
|
|
5
|
+
import { createWaker, watchWalletActivity } from './watch.js';
|
|
6
|
+
/** below this the fee payer cannot cover the two signatures of a rescue transaction reliably — pause and alert */
|
|
7
|
+
const FEE_PAYER_PAUSE_LAMPORTS = 20000n;
|
|
8
|
+
/** below 0.01 SOL start nagging every pass (an ATA creation alone costs ~0.002 SOL in rent) */
|
|
9
|
+
const FEE_PAYER_WARN_LAMPORTS = 10000000n;
|
|
10
|
+
export function formatNos(amount) {
|
|
11
|
+
const d = 10n ** BigInt(NOS_DECIMALS);
|
|
12
|
+
return `${amount / d}.${(amount % d).toString().padStart(NOS_DECIMALS, '0')} NOS`;
|
|
13
|
+
}
|
|
14
|
+
export function formatSol(lamports) {
|
|
15
|
+
return `${Number(lamports) / 1e9} SOL`;
|
|
16
|
+
}
|
|
17
|
+
/** Derive (and if needed create, rent paid by the rescue payer) the safe wallet's NOS ATA. */
|
|
18
|
+
export async function ensureSafeNosAta(ctx) {
|
|
19
|
+
const mint = NOS_MINT[ctx.network];
|
|
20
|
+
const [ata] = await findAssociatedTokenPda({
|
|
21
|
+
mint,
|
|
22
|
+
owner: ctx.safe,
|
|
23
|
+
tokenProgram: TOKEN_PROGRAM,
|
|
24
|
+
});
|
|
25
|
+
const createIx = await ctx.client.solana.getCreateATAInstructionIfNeeded(ata, mint, ctx.safe, ctx.payer);
|
|
26
|
+
if (createIx) {
|
|
27
|
+
ctx.log(`Creating safe wallet NOS token account ${ata} (rent paid by fee payer)...`);
|
|
28
|
+
await send(ctx, [createIx]);
|
|
29
|
+
}
|
|
30
|
+
return ata;
|
|
31
|
+
}
|
|
32
|
+
async function send(ctx, instructions) {
|
|
33
|
+
if (ctx.dryRun)
|
|
34
|
+
return simulateOnly(ctx, instructions);
|
|
35
|
+
const signature = await ctx.client.solana.buildSignAndSend(instructions, {
|
|
36
|
+
feePayer: ctx.payer,
|
|
37
|
+
commitment: 'confirmed',
|
|
38
|
+
});
|
|
39
|
+
return signature;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Dry-run path: build and sign the exact transaction that would be sent, but
|
|
43
|
+
* run it through simulateTransaction instead of broadcasting. Throws (like a
|
|
44
|
+
* failed send) when the simulation errors, so callers report it identically.
|
|
45
|
+
*/
|
|
46
|
+
async function simulateOnly(ctx, instructions) {
|
|
47
|
+
const message = await ctx.client.solana.buildTransaction(instructions, { feePayer: ctx.payer });
|
|
48
|
+
const signed = await ctx.client.solana.signTransaction(message);
|
|
49
|
+
const encoded = ctx.client.solana.serializeTransaction(signed);
|
|
50
|
+
const sim = await ctx.client.solana.rpc
|
|
51
|
+
.simulateTransaction(encoded, { encoding: 'base64', sigVerify: true })
|
|
52
|
+
.send();
|
|
53
|
+
if (sim.value.err) {
|
|
54
|
+
const logs = (sim.value.logs ?? []).slice(-4).join(' | ');
|
|
55
|
+
throw new Error(`dry-run simulation failed: ${JSON.stringify(sim.value.err)}${logs ? ` — ${logs}` : ''}`);
|
|
56
|
+
}
|
|
57
|
+
return `dry-run: simulated OK (${sim.value.unitsConsumed ?? 0n} CU), nothing sent`;
|
|
58
|
+
}
|
|
59
|
+
/** Sweep all SOL + SPL balances out of the hacked wallet. Returns number of assets moved. */
|
|
60
|
+
export async function sweepOnce(ctx) {
|
|
61
|
+
const items = await buildSweepInstructions(ctx.client, {
|
|
62
|
+
hacked: ctx.hacked,
|
|
63
|
+
safe: ctx.safe,
|
|
64
|
+
payer: ctx.payer,
|
|
65
|
+
});
|
|
66
|
+
for (const item of items) {
|
|
67
|
+
try {
|
|
68
|
+
const sig = await send(ctx, item.instructions);
|
|
69
|
+
ctx.log(`Swept ${item.label} → ${ctx.safe} (${sig})`);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
ctx.log(`Sweep of ${item.label} failed: ${errMsg(err)}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return items.length;
|
|
76
|
+
}
|
|
77
|
+
/** Claim rewards, close the rewards account and unstake — atomically. */
|
|
78
|
+
export async function unstakeNow(ctx, stake, safeNosAta) {
|
|
79
|
+
const plan = await buildUnstakePlan(ctx.client, {
|
|
80
|
+
stake,
|
|
81
|
+
authority: ctx.hacked,
|
|
82
|
+
safeNosAta,
|
|
83
|
+
});
|
|
84
|
+
if (plan.reward) {
|
|
85
|
+
ctx.log(`Reward account found with ~${formatNos(plan.pendingRewards)} pending; claiming to safe wallet, closing it, then unstaking (one transaction).`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
ctx.log('No reward account; sending unstake.');
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const sig = await send(ctx, plan.instructions);
|
|
92
|
+
ctx.log(`Unstake transaction confirmed: ${sig}`);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (plan.instructions.length === plan.fallbackInstructions.length)
|
|
96
|
+
throw err;
|
|
97
|
+
ctx.log(`Unstake with rewards claim failed (${errMsg(err)}); retrying without claim...`);
|
|
98
|
+
const sig = await send(ctx, plan.fallbackInstructions);
|
|
99
|
+
ctx.log(`Unstake transaction confirmed (rewards claim skipped): ${sig}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Withdraw all currently-released NOS from the (unstaked) vault straight to the
|
|
104
|
+
* safe wallet, and — if the stake is fully vested — close the stake account to
|
|
105
|
+
* release its rent (which the sweep then collects). Safe to call any time; it
|
|
106
|
+
* fetches fresh stake state and no-ops when there's nothing to do. This is the
|
|
107
|
+
* single path shared by the loop, the CLI `withdraw` command, and the web button.
|
|
108
|
+
*/
|
|
109
|
+
export async function withdrawReleased(ctx, safeNosAta, opts = {}) {
|
|
110
|
+
const minWithdraw = opts.minWithdraw ?? 0n;
|
|
111
|
+
const stake = await fetchStakeInfo(ctx.client, ctx.hacked.address);
|
|
112
|
+
if (!stake)
|
|
113
|
+
return { withdrawn: 0n, closed: false, stake: null };
|
|
114
|
+
if (stake.timeUnstake === 0n) {
|
|
115
|
+
ctx.log('Stake is still staked (not unstaked); nothing to withdraw yet.');
|
|
116
|
+
return { withdrawn: 0n, closed: false, stake };
|
|
117
|
+
}
|
|
118
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
119
|
+
const fullyVested = stake.fullyVestedAt !== null && now > stake.fullyVestedAt;
|
|
120
|
+
let withdrawn = 0n;
|
|
121
|
+
if (stake.withdrawable > 0n && (fullyVested || stake.withdrawable >= minWithdraw)) {
|
|
122
|
+
const ix = buildWithdrawInstruction({ stake, authority: ctx.hacked, safeNosAta });
|
|
123
|
+
const sig = await send(ctx, [ix]);
|
|
124
|
+
withdrawn = stake.withdrawable;
|
|
125
|
+
ctx.log(`Withdrew ~${formatNos(withdrawn)} to safe wallet (${sig})`);
|
|
126
|
+
}
|
|
127
|
+
let closed = false;
|
|
128
|
+
if (fullyVested) {
|
|
129
|
+
// The vault is drained after the withdraw above; close to release the rent.
|
|
130
|
+
try {
|
|
131
|
+
const ix = buildCloseStakeInstruction({ stake, authority: ctx.hacked, safeNosAta });
|
|
132
|
+
const sig = await send(ctx, [ix]);
|
|
133
|
+
ctx.log(`Stake account closed, rent released (${sig}). Staked NOS fully rescued.`);
|
|
134
|
+
closed = true;
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
ctx.log(`Close stake failed (will retry next pass): ${errMsg(err)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { withdrawn, closed, stake };
|
|
141
|
+
}
|
|
142
|
+
function errMsg(err) {
|
|
143
|
+
return err instanceof Error ? err.message : String(err);
|
|
144
|
+
}
|
|
145
|
+
function sleep(ms, signal) {
|
|
146
|
+
return new Promise((resolve) => {
|
|
147
|
+
const t = setTimeout(done, ms);
|
|
148
|
+
function done() {
|
|
149
|
+
signal?.removeEventListener('abort', done);
|
|
150
|
+
clearTimeout(t);
|
|
151
|
+
resolve();
|
|
152
|
+
}
|
|
153
|
+
signal?.addEventListener('abort', done);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The full rescue daemon:
|
|
158
|
+
* 1. sweep whatever is in the hacked wallet right now
|
|
159
|
+
* 2. unstake immediately (claim + close rewards + unstake, atomic)
|
|
160
|
+
* 3. loop: keep sweeping incoming SOL/tokens (the defense — this starves the
|
|
161
|
+
* attacker of the SOL they'd need to move anything), and, if auto-withdraw
|
|
162
|
+
* is on, withdraw released NOS to the safe wallet on the configured cadence
|
|
163
|
+
* (not every slice — a slice accrues every second)
|
|
164
|
+
* 4. re-unstake if the attacker restakes to grief the vest
|
|
165
|
+
* 5. at full vest: withdraw the remainder, close the stake account, sweep the rent
|
|
166
|
+
*
|
|
167
|
+
* Sweeping is event-driven: a websocket watcher on the wallet and its token
|
|
168
|
+
* accounts wakes the loop the moment value lands (sub-second reaction); the
|
|
169
|
+
* sweepInterval polling is the backup when the websocket is down.
|
|
170
|
+
*
|
|
171
|
+
* Runs until aborted; after the stake is fully rescued it keeps sweeping.
|
|
172
|
+
*/
|
|
173
|
+
export async function runRescue(ctx, options = {}) {
|
|
174
|
+
const sweepInterval = (options.sweepIntervalSeconds ?? 15) * 1000;
|
|
175
|
+
const autoWithdraw = options.autoWithdraw ?? true;
|
|
176
|
+
const withdrawIntervalMs = (options.withdrawIntervalSeconds ?? 3600) * 1000;
|
|
177
|
+
const minWithdraw = options.minWithdraw ?? 0n;
|
|
178
|
+
const signal = options.signal;
|
|
179
|
+
if (ctx.dryRun) {
|
|
180
|
+
ctx.log('DRY RUN: every transaction is simulated instead of sent; one pass, then exit.');
|
|
181
|
+
}
|
|
182
|
+
ctx.log(`Compromised wallet: ${ctx.hacked.address}`);
|
|
183
|
+
ctx.log(`Safe wallet: ${ctx.safe}`);
|
|
184
|
+
ctx.log(`Fee payer: ${ctx.payer.address}`);
|
|
185
|
+
ctx.log(autoWithdraw
|
|
186
|
+
? `Auto-withdraw: every ${withdrawIntervalMs === 0 ? 'full-vest only' : formatDuration(withdrawIntervalMs / 1000)} (plus at full vest)`
|
|
187
|
+
: 'Auto-withdraw: off (manual withdraw only)');
|
|
188
|
+
const payerBalance = await ctx.client.solana.getBalance(ctx.payer.address);
|
|
189
|
+
ctx.log(`Fee payer balance: ${formatSol(payerBalance)}`);
|
|
190
|
+
const safeNosAta = await ensureSafeNosAta(ctx);
|
|
191
|
+
// Event-driven defense: wake the loop the instant the websocket sees value
|
|
192
|
+
// land in the wallet, instead of waiting out the polling interval.
|
|
193
|
+
const waker = createWaker();
|
|
194
|
+
let wokenByEvent = false;
|
|
195
|
+
if (!ctx.dryRun) {
|
|
196
|
+
watchWalletActivity({
|
|
197
|
+
client: ctx.client,
|
|
198
|
+
owner: ctx.hacked.address,
|
|
199
|
+
log: ctx.log,
|
|
200
|
+
signal,
|
|
201
|
+
onActivity: (source) => {
|
|
202
|
+
ctx.log(`Websocket: incoming value detected (${source}); sweeping immediately`);
|
|
203
|
+
wokenByEvent = true;
|
|
204
|
+
waker.wake();
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
let stakeClosed = false;
|
|
209
|
+
let lastWithdrawMs = 0;
|
|
210
|
+
let payerPaused = false;
|
|
211
|
+
async function rescuePass(eventPass) {
|
|
212
|
+
// The fee payer is the lifeline: with it dry, every transaction fails.
|
|
213
|
+
// Check every pass; pause sends and keep alerting until it is funded again.
|
|
214
|
+
const payerLamports = await ctx.client.solana.getBalance(ctx.payer.address);
|
|
215
|
+
if (payerLamports < FEE_PAYER_PAUSE_LAMPORTS) {
|
|
216
|
+
ctx.log(`ALERT: fee payer ${ctx.payer.address} is down to ${formatSol(payerLamports)} — it cannot pay for a transaction. ` +
|
|
217
|
+
'Rescue is PAUSED (no sweeps or withdrawals) until you fund it.');
|
|
218
|
+
payerPaused = true;
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (payerPaused) {
|
|
222
|
+
payerPaused = false;
|
|
223
|
+
ctx.log(`Fee payer funded again (${formatSol(payerLamports)}); resuming rescue.`);
|
|
224
|
+
}
|
|
225
|
+
else if (payerLamports < FEE_PAYER_WARN_LAMPORTS) {
|
|
226
|
+
ctx.log(`WARNING: fee payer balance is low (${formatSol(payerLamports)}); top it up — every rescue transaction depends on it.`);
|
|
227
|
+
}
|
|
228
|
+
// Sweep first — this is the race against the attacker.
|
|
229
|
+
let moved = await sweepOnce(ctx);
|
|
230
|
+
if (eventPass && moved === 0) {
|
|
231
|
+
// The notification fires at 'processed'; the balances we sweep are read
|
|
232
|
+
// at 'confirmed' and can lag it by a slot or two. Retry briefly.
|
|
233
|
+
for (let i = 0; i < 3 && moved === 0 && !signal?.aborted; i++) {
|
|
234
|
+
await sleep(750, signal);
|
|
235
|
+
moved = await sweepOnce(ctx);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (!stakeClosed) {
|
|
239
|
+
const stake = await fetchStakeInfo(ctx.client, ctx.hacked.address);
|
|
240
|
+
if (!stake) {
|
|
241
|
+
ctx.log('No stake account (closed or never existed); continuing in sweep-only mode.');
|
|
242
|
+
stakeClosed = true;
|
|
243
|
+
}
|
|
244
|
+
else if (stake.timeUnstake === 0n) {
|
|
245
|
+
// still staked — either first run or the attacker restaked to grief us
|
|
246
|
+
await unstakeNow(ctx, stake, safeNosAta);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
250
|
+
const fullyVested = stake.fullyVestedAt !== null && now > stake.fullyVestedAt;
|
|
251
|
+
const nowMs = Date.now();
|
|
252
|
+
const cadenceDue = withdrawIntervalMs > 0 && nowMs - lastWithdrawMs >= withdrawIntervalMs;
|
|
253
|
+
if (autoWithdraw && (fullyVested || cadenceDue)) {
|
|
254
|
+
const res = await withdrawReleased(ctx, safeNosAta, { minWithdraw });
|
|
255
|
+
lastWithdrawMs = nowMs;
|
|
256
|
+
if (res.closed)
|
|
257
|
+
stakeClosed = true;
|
|
258
|
+
}
|
|
259
|
+
else if (stake.fullyVestedAt !== null) {
|
|
260
|
+
const remaining = Number(stake.fullyVestedAt - now);
|
|
261
|
+
ctx.log(`Vault: ${formatNos(stake.vaultBalance)}, withdrawable now: ${formatNos(stake.withdrawable)}, fully vested in ${formatDuration(remaining)}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
while (!signal?.aborted) {
|
|
267
|
+
const eventPass = wokenByEvent;
|
|
268
|
+
wokenByEvent = false;
|
|
269
|
+
try {
|
|
270
|
+
await rescuePass(eventPass);
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
ctx.log(`Loop error: ${errMsg(err)} (retrying in ${sweepInterval / 1000}s)`);
|
|
274
|
+
}
|
|
275
|
+
if (ctx.dryRun) {
|
|
276
|
+
ctx.log('Dry run complete — everything above was simulated, nothing was sent.');
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
await waker.wait(sweepInterval, signal);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
export function formatDuration(seconds) {
|
|
283
|
+
if (seconds <= 0)
|
|
284
|
+
return '0s';
|
|
285
|
+
const d = Math.floor(seconds / 86400);
|
|
286
|
+
const h = Math.floor((seconds % 86400) / 3600);
|
|
287
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
288
|
+
const s = Math.floor(seconds % 60);
|
|
289
|
+
return [d && `${d}d`, h && `${h}h`, m && `${m}m`, (s || (!d && !h && !m)) && `${s}s`]
|
|
290
|
+
.filter(Boolean)
|
|
291
|
+
.join(' ');
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=rescue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rescue.js","sourceRoot":"","sources":["../../src/lib/rescue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type Address, type Instruction, type TransactionSigner } from '@solana/kit';
|
|
2
|
+
import type { NosanaClient } from '@nosana/kit';
|
|
3
|
+
export interface RewardAccountData {
|
|
4
|
+
address: Address;
|
|
5
|
+
authority: Address;
|
|
6
|
+
reflection: bigint;
|
|
7
|
+
xnos: bigint;
|
|
8
|
+
}
|
|
9
|
+
export interface ReflectionAccountData {
|
|
10
|
+
address: Address;
|
|
11
|
+
rate: bigint;
|
|
12
|
+
totalReflection: bigint;
|
|
13
|
+
totalXnos: bigint;
|
|
14
|
+
vault: Address;
|
|
15
|
+
}
|
|
16
|
+
/** PDA of a user's reward account: ["reward", authority] */
|
|
17
|
+
export declare function rewardPda(authority: Address): Promise<Address>;
|
|
18
|
+
/** PDA of the global reflection account: ["reflection"] */
|
|
19
|
+
export declare function reflectionPda(): Promise<Address>;
|
|
20
|
+
/** Fetch and decode the user's RewardAccount, or null if it doesn't exist (already closed). */
|
|
21
|
+
export declare function fetchRewardAccount(client: NosanaClient, authority: Address): Promise<RewardAccountData | null>;
|
|
22
|
+
/** Fetch and decode the global ReflectionAccount. */
|
|
23
|
+
export declare function fetchReflectionAccount(client: NosanaClient): Promise<ReflectionAccountData>;
|
|
24
|
+
/** Pending (claimable) rewards in NOS base units: reflection / rate - xnos */
|
|
25
|
+
export declare function pendingRewards(reward: RewardAccountData, reflection: ReflectionAccountData): bigint;
|
|
26
|
+
/**
|
|
27
|
+
* nosana-rewards `claim`: pays out pending rewards from the rewards vault to `user`.
|
|
28
|
+
* `user` is any NOS token account — we point it at the SAFE wallet's ATA so
|
|
29
|
+
* rewards never touch the compromised wallet.
|
|
30
|
+
* Only valid while the stake is NOT unstaked (claim before unstaking).
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildClaimRewardsInstruction(params: {
|
|
33
|
+
user: Address;
|
|
34
|
+
rewardsVault: Address;
|
|
35
|
+
reflection: Address;
|
|
36
|
+
reward: Address;
|
|
37
|
+
stake: Address;
|
|
38
|
+
authority: TransactionSigner;
|
|
39
|
+
}): Instruction;
|
|
40
|
+
/**
|
|
41
|
+
* nosana-rewards `close`: closes the user's reward account (required before `unstake`).
|
|
42
|
+
* Rent is returned to the (compromised) authority — the sweep loop picks it up.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildCloseRewardsInstruction(params: {
|
|
45
|
+
reflection: Address;
|
|
46
|
+
reward: Address;
|
|
47
|
+
authority: TransactionSigner;
|
|
48
|
+
}): Instruction;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { AccountRole, getAddressEncoder, getAddressDecoder, getProgramDerivedAddress, getBase64Encoder, } from '@solana/kit';
|
|
2
|
+
import { ANCHOR_DISCRIMINATORS, REWARDS_PROGRAM, TOKEN_PROGRAM } from './constants.js';
|
|
3
|
+
const addressEncoder = getAddressEncoder();
|
|
4
|
+
const addressDecoder = getAddressDecoder();
|
|
5
|
+
/** PDA of a user's reward account: ["reward", authority] */
|
|
6
|
+
export async function rewardPda(authority) {
|
|
7
|
+
const [addr] = await getProgramDerivedAddress({
|
|
8
|
+
programAddress: REWARDS_PROGRAM,
|
|
9
|
+
seeds: ['reward', addressEncoder.encode(authority)],
|
|
10
|
+
});
|
|
11
|
+
return addr;
|
|
12
|
+
}
|
|
13
|
+
/** PDA of the global reflection account: ["reflection"] */
|
|
14
|
+
export async function reflectionPda() {
|
|
15
|
+
const [addr] = await getProgramDerivedAddress({
|
|
16
|
+
programAddress: REWARDS_PROGRAM,
|
|
17
|
+
seeds: ['reflection'],
|
|
18
|
+
});
|
|
19
|
+
return addr;
|
|
20
|
+
}
|
|
21
|
+
function readU128LE(bytes, offset) {
|
|
22
|
+
let v = 0n;
|
|
23
|
+
for (let i = 15; i >= 0; i--)
|
|
24
|
+
v = (v << 8n) | BigInt(bytes[offset + i]);
|
|
25
|
+
return v;
|
|
26
|
+
}
|
|
27
|
+
async function fetchRawAccount(client, addr) {
|
|
28
|
+
const res = await client.solana.rpc.getAccountInfo(addr, { encoding: 'base64' }).send();
|
|
29
|
+
if (!res.value)
|
|
30
|
+
return null;
|
|
31
|
+
return new Uint8Array(getBase64Encoder().encode(res.value.data[0]));
|
|
32
|
+
}
|
|
33
|
+
/** Fetch and decode the user's RewardAccount, or null if it doesn't exist (already closed). */
|
|
34
|
+
export async function fetchRewardAccount(client, authority) {
|
|
35
|
+
const addr = await rewardPda(authority);
|
|
36
|
+
const data = await fetchRawAccount(client, addr);
|
|
37
|
+
if (!data)
|
|
38
|
+
return null;
|
|
39
|
+
// discriminator(8) | authority(32) | bump(1) | reflection(u128) | xnos(u128)
|
|
40
|
+
return {
|
|
41
|
+
address: addr,
|
|
42
|
+
authority: addressDecoder.decode(data.subarray(8, 40)),
|
|
43
|
+
reflection: readU128LE(data, 41),
|
|
44
|
+
xnos: readU128LE(data, 57),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Fetch and decode the global ReflectionAccount. */
|
|
48
|
+
export async function fetchReflectionAccount(client) {
|
|
49
|
+
const addr = await reflectionPda();
|
|
50
|
+
const data = await fetchRawAccount(client, addr);
|
|
51
|
+
if (!data)
|
|
52
|
+
throw new Error('Rewards reflection account not found on this network');
|
|
53
|
+
// discriminator(8) | rate(u128) | totalReflection(u128) | totalXnos(u128) | vault(32) | vaultBump(1)
|
|
54
|
+
return {
|
|
55
|
+
address: addr,
|
|
56
|
+
rate: readU128LE(data, 8),
|
|
57
|
+
totalReflection: readU128LE(data, 24),
|
|
58
|
+
totalXnos: readU128LE(data, 40),
|
|
59
|
+
vault: addressDecoder.decode(data.subarray(56, 88)),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** Pending (claimable) rewards in NOS base units: reflection / rate - xnos */
|
|
63
|
+
export function pendingRewards(reward, reflection) {
|
|
64
|
+
const amount = reward.reflection / reflection.rate - reward.xnos;
|
|
65
|
+
return amount > 0n ? amount : 0n;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* nosana-rewards `claim`: pays out pending rewards from the rewards vault to `user`.
|
|
69
|
+
* `user` is any NOS token account — we point it at the SAFE wallet's ATA so
|
|
70
|
+
* rewards never touch the compromised wallet.
|
|
71
|
+
* Only valid while the stake is NOT unstaked (claim before unstaking).
|
|
72
|
+
*/
|
|
73
|
+
export function buildClaimRewardsInstruction(params) {
|
|
74
|
+
const accounts = [
|
|
75
|
+
{ address: params.user, role: AccountRole.WRITABLE },
|
|
76
|
+
{ address: params.rewardsVault, role: AccountRole.WRITABLE },
|
|
77
|
+
{ address: params.reflection, role: AccountRole.WRITABLE },
|
|
78
|
+
{ address: params.reward, role: AccountRole.WRITABLE },
|
|
79
|
+
{ address: params.stake, role: AccountRole.READONLY },
|
|
80
|
+
{ address: params.authority.address, role: AccountRole.WRITABLE_SIGNER, signer: params.authority },
|
|
81
|
+
{ address: TOKEN_PROGRAM, role: AccountRole.READONLY },
|
|
82
|
+
];
|
|
83
|
+
return {
|
|
84
|
+
programAddress: REWARDS_PROGRAM,
|
|
85
|
+
accounts,
|
|
86
|
+
data: ANCHOR_DISCRIMINATORS.claim,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* nosana-rewards `close`: closes the user's reward account (required before `unstake`).
|
|
91
|
+
* Rent is returned to the (compromised) authority — the sweep loop picks it up.
|
|
92
|
+
*/
|
|
93
|
+
export function buildCloseRewardsInstruction(params) {
|
|
94
|
+
const accounts = [
|
|
95
|
+
{ address: params.reflection, role: AccountRole.WRITABLE },
|
|
96
|
+
{ address: params.reward, role: AccountRole.WRITABLE },
|
|
97
|
+
{ address: params.authority.address, role: AccountRole.WRITABLE_SIGNER, signer: params.authority },
|
|
98
|
+
];
|
|
99
|
+
return {
|
|
100
|
+
programAddress: REWARDS_PROGRAM,
|
|
101
|
+
accounts,
|
|
102
|
+
data: ANCHOR_DISCRIMINATORS.close,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=rewards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewards.js","sourceRoot":"","sources":["../../src/lib/rewards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Address, Instruction, TransactionSigner } from '@solana/kit';
|
|
2
|
+
import { type NosanaClient } from '@nosana/kit';
|
|
3
|
+
import { type RewardAccountData } from './rewards.js';
|
|
4
|
+
export interface StakeInfo {
|
|
5
|
+
address: Address;
|
|
6
|
+
authority: Address;
|
|
7
|
+
/** total staked NOS in base units */
|
|
8
|
+
amount: bigint;
|
|
9
|
+
/** unstake release period in seconds */
|
|
10
|
+
duration: bigint;
|
|
11
|
+
/** unix timestamp of unstake, 0n when still staked */
|
|
12
|
+
timeUnstake: bigint;
|
|
13
|
+
vault: Address;
|
|
14
|
+
/** NOS currently in the stake vault (base units) */
|
|
15
|
+
vaultBalance: bigint;
|
|
16
|
+
/** NOS withdrawable right now (base units) */
|
|
17
|
+
withdrawable: bigint;
|
|
18
|
+
/** unix timestamp when the full amount is released (null while staked) */
|
|
19
|
+
fullyVestedAt: bigint | null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Port of StakeAccount::withdraw from nosana-staking state.rs.
|
|
23
|
+
* Linear release of `amount` over `duration` starting at `timeUnstake`,
|
|
24
|
+
* minus what was already withdrawn (amount - vaultBalance).
|
|
25
|
+
*/
|
|
26
|
+
export declare function withdrawableAmount(amount: bigint, duration: bigint, timeUnstake: bigint, vaultBalance: bigint, now: bigint): bigint;
|
|
27
|
+
/** Fetch the stake account (with vault balance and live withdrawable amount), or null if none exists. */
|
|
28
|
+
export declare function fetchStakeInfo(client: NosanaClient, owner: Address): Promise<StakeInfo | null>;
|
|
29
|
+
export interface UnstakePlan {
|
|
30
|
+
/** instructions to execute atomically: [claim?, closeRewards?, unstake] */
|
|
31
|
+
instructions: Instruction[];
|
|
32
|
+
/** same plan without the claim instruction, as fallback if claim constraints fail */
|
|
33
|
+
fallbackInstructions: Instruction[];
|
|
34
|
+
reward: RewardAccountData | null;
|
|
35
|
+
pendingRewards: bigint;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the atomic unstake transaction. The on-chain `unstake` requires the
|
|
39
|
+
* rewards account to be closed, so this packs (in one transaction):
|
|
40
|
+
* 1. claim pending rewards → SAFE wallet ATA (if a reward account exists)
|
|
41
|
+
* 2. close the reward account
|
|
42
|
+
* 3. unstake
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildUnstakePlan(client: NosanaClient, params: {
|
|
45
|
+
stake: StakeInfo;
|
|
46
|
+
authority: TransactionSigner;
|
|
47
|
+
/** NOS token account (safe wallet ATA) that receives claimed rewards */
|
|
48
|
+
safeNosAta: Address;
|
|
49
|
+
}): Promise<UnstakePlan>;
|
|
50
|
+
/** Withdraw released NOS from the stake vault directly to the SAFE wallet's NOS ATA. */
|
|
51
|
+
export declare function buildWithdrawInstruction(params: {
|
|
52
|
+
stake: StakeInfo;
|
|
53
|
+
authority: TransactionSigner;
|
|
54
|
+
safeNosAta: Address;
|
|
55
|
+
}): Instruction;
|
|
56
|
+
/** Close the (empty, fully vested) stake account; rent goes to the compromised authority and is swept. */
|
|
57
|
+
export declare function buildCloseStakeInstruction(params: {
|
|
58
|
+
stake: StakeInfo;
|
|
59
|
+
authority: TransactionSigner;
|
|
60
|
+
safeNosAta: Address;
|
|
61
|
+
}): Instruction;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { StakingClient } from '@nosana/kit';
|
|
2
|
+
import { buildClaimRewardsInstruction, buildCloseRewardsInstruction, fetchReflectionAccount, fetchRewardAccount, pendingRewards, reflectionPda, rewardPda, } from './rewards.js';
|
|
3
|
+
const U64_MAX = 18446744073709551615n;
|
|
4
|
+
/**
|
|
5
|
+
* Port of StakeAccount::withdraw from nosana-staking state.rs.
|
|
6
|
+
* Linear release of `amount` over `duration` starting at `timeUnstake`,
|
|
7
|
+
* minus what was already withdrawn (amount - vaultBalance).
|
|
8
|
+
*/
|
|
9
|
+
export function withdrawableAmount(amount, duration, timeUnstake, vaultBalance, now) {
|
|
10
|
+
if (timeUnstake === 0n || vaultBalance === 0n)
|
|
11
|
+
return 0n;
|
|
12
|
+
const elapsed = now - timeUnstake;
|
|
13
|
+
if (elapsed >= duration)
|
|
14
|
+
return vaultBalance;
|
|
15
|
+
const max = amount > elapsed ? amount : elapsed;
|
|
16
|
+
const precision = U64_MAX / max - 1n;
|
|
17
|
+
const released = ((elapsed * precision) / duration) * amount / precision;
|
|
18
|
+
const available = released - (amount - vaultBalance);
|
|
19
|
+
return available > 0n ? available : 0n;
|
|
20
|
+
}
|
|
21
|
+
/** Fetch the stake account (with vault balance and live withdrawable amount), or null if none exists. */
|
|
22
|
+
export async function fetchStakeInfo(client, owner) {
|
|
23
|
+
const stakeAddress = await client.stake.getAddress(owner);
|
|
24
|
+
const maybe = await StakingClient.fetchMaybeStakeAccount(client.solana.rpc, stakeAddress);
|
|
25
|
+
if (!maybe.exists)
|
|
26
|
+
return null;
|
|
27
|
+
const acc = maybe.data;
|
|
28
|
+
let vaultBalance = 0n;
|
|
29
|
+
try {
|
|
30
|
+
const bal = await client.solana.rpc.getTokenAccountBalance(acc.vault).send();
|
|
31
|
+
vaultBalance = BigInt(bal.value.amount);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// vault already closed
|
|
35
|
+
}
|
|
36
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
37
|
+
return {
|
|
38
|
+
address: stakeAddress,
|
|
39
|
+
authority: acc.authority,
|
|
40
|
+
amount: acc.amount,
|
|
41
|
+
duration: acc.duration,
|
|
42
|
+
timeUnstake: acc.timeUnstake,
|
|
43
|
+
vault: acc.vault,
|
|
44
|
+
vaultBalance,
|
|
45
|
+
withdrawable: withdrawableAmount(acc.amount, acc.duration, acc.timeUnstake, vaultBalance, now),
|
|
46
|
+
fullyVestedAt: acc.timeUnstake === 0n ? null : acc.timeUnstake + acc.duration,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Build the atomic unstake transaction. The on-chain `unstake` requires the
|
|
51
|
+
* rewards account to be closed, so this packs (in one transaction):
|
|
52
|
+
* 1. claim pending rewards → SAFE wallet ATA (if a reward account exists)
|
|
53
|
+
* 2. close the reward account
|
|
54
|
+
* 3. unstake
|
|
55
|
+
*/
|
|
56
|
+
export async function buildUnstakePlan(client, params) {
|
|
57
|
+
const reward = await fetchRewardAccount(client, params.stake.authority);
|
|
58
|
+
const reflection = await reflectionPda();
|
|
59
|
+
const unstakeIx = StakingClient.getUnstakeInstruction({
|
|
60
|
+
stake: params.stake.address,
|
|
61
|
+
reward: await rewardPda(params.stake.authority),
|
|
62
|
+
authority: params.authority,
|
|
63
|
+
});
|
|
64
|
+
if (!reward) {
|
|
65
|
+
return {
|
|
66
|
+
instructions: [unstakeIx],
|
|
67
|
+
fallbackInstructions: [unstakeIx],
|
|
68
|
+
reward: null,
|
|
69
|
+
pendingRewards: 0n,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const reflectionAccount = await fetchReflectionAccount(client);
|
|
73
|
+
const closeIx = buildCloseRewardsInstruction({
|
|
74
|
+
reflection,
|
|
75
|
+
reward: reward.address,
|
|
76
|
+
authority: params.authority,
|
|
77
|
+
});
|
|
78
|
+
const claimIx = buildClaimRewardsInstruction({
|
|
79
|
+
user: params.safeNosAta,
|
|
80
|
+
rewardsVault: reflectionAccount.vault,
|
|
81
|
+
reflection,
|
|
82
|
+
reward: reward.address,
|
|
83
|
+
stake: params.stake.address,
|
|
84
|
+
authority: params.authority,
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
instructions: [claimIx, closeIx, unstakeIx],
|
|
88
|
+
fallbackInstructions: [closeIx, unstakeIx],
|
|
89
|
+
reward,
|
|
90
|
+
pendingRewards: pendingRewards(reward, reflectionAccount),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Withdraw released NOS from the stake vault directly to the SAFE wallet's NOS ATA. */
|
|
94
|
+
export function buildWithdrawInstruction(params) {
|
|
95
|
+
return StakingClient.getWithdrawInstruction({
|
|
96
|
+
user: params.safeNosAta,
|
|
97
|
+
vault: params.stake.vault,
|
|
98
|
+
stake: params.stake.address,
|
|
99
|
+
authority: params.authority,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** Close the (empty, fully vested) stake account; rent goes to the compromised authority and is swept. */
|
|
103
|
+
export function buildCloseStakeInstruction(params) {
|
|
104
|
+
return StakingClient.getCloseInstruction({
|
|
105
|
+
user: params.safeNosAta,
|
|
106
|
+
stake: params.stake.address,
|
|
107
|
+
vault: params.stake.vault,
|
|
108
|
+
authority: params.authority,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=stake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stake.js","sourceRoot":"","sources":["../../src/lib/stake.ts"],"names":[],"mappings":"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"}
|