mirror of
https://github.com/bdnugget/rs-sdk.git
synced 2026-11-07 09:07:30 +00:00
fix(sdk): allow bank withdraw/deposit amounts above 10k
validateActionQuantity's 10k cap exists to bound actions that expand into repeated packets, but bank withdraw/deposit serialize the whole amount into a single Withdraw-X/Deposit-X count-dialog packet. Applying the cap there made withdrawItem(coins, 20000) return invalid_amount without sending anything - an ordinary stackable withdrawal silently no-opped (bug report msolats3, 2026-08-11). Bank ops now validate against MAX_BANK_ACTION_QUANTITY (2^31-2; 2^31-1 is the client's Withdraw-All encoding, so it stays unreachable). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ca88d2142d
commit
c4c22b395c
4 files changed
+29
-2
No files matched your search
@@ -498,6 +498,10 @@ interface OpFeedbackState {
|
||||
```typescript
|
||||
interface BotWorldState {
|
||||
tick: number;
|
||||
/** Tick on which an updated Lite runner already emitted policy-gated KBD op2. */
|
||||
fastKbdAttackTick?: number;
|
||||
/** Prayer-off packets emitted in-process on the KBD-removal publication. */
|
||||
fastKbdPrayerOff?: { tick: number; prayerIndices: number[] };
|
||||
/** Monotonic state publication cursor; advances even for multiple publications in one game tick. */
|
||||
revision?: number;
|
||||
inGame: boolean;
|
||||
|
||||
@@ -137,6 +137,14 @@ export function countItems(
|
||||
|
||||
/** Upper bound for quantities that expand into repeated shop/bank packets. */
|
||||
export const MAX_ACTION_QUANTITY = 10_000;
|
||||
/**
|
||||
* Bank withdrawals/deposits serialize the whole amount into one Withdraw-X /
|
||||
* Deposit-X count-dialog packet — nothing expands per item — so the only real
|
||||
* bound is the wire's: the client encodes amounts >= 2^31-1 as the "All"
|
||||
* button. Capping these at MAX_ACTION_QUANTITY silently no-opped ordinary
|
||||
* stackable withdrawals (e.g. 20k coins from a 125M stack).
|
||||
*/
|
||||
export const MAX_BANK_ACTION_QUANTITY = 2_147_483_646;
|
||||
/** Shops accept Buy/Sell-10 at most, so 1000 items is already 100 packets. */
|
||||
export const MAX_SHOP_ACTION_QUANTITY = 1_000;
|
||||
/** Hard cap on packets a single shop call may emit, independent of quantity. */
|
||||
|
||||
+3
-2
@@ -72,6 +72,7 @@ import {
|
||||
resolveSkillDialogProduct,
|
||||
skillDialogProductLabels,
|
||||
validateActionQuantity,
|
||||
MAX_BANK_ACTION_QUANTITY,
|
||||
MAX_SHOP_ACTION_PACKETS,
|
||||
MAX_SHOP_ACTION_QUANTITY,
|
||||
SHOP_ACTION_DEADLINE_MS,
|
||||
@@ -1646,7 +1647,7 @@ export class BotActions {
|
||||
|
||||
/** Deposit an item into the bank. Use -1 for all. */
|
||||
async depositItem(target: InventoryItem | string | RegExp, amount: number = -1): Promise<BankDepositResult> {
|
||||
const validated = validateActionQuantity(amount, { allowAll: true });
|
||||
const validated = validateActionQuantity(amount, { allowAll: true, max: MAX_BANK_ACTION_QUANTITY });
|
||||
if (!validated.valid) {
|
||||
return { success: false, message: validated.message, reason: 'invalid_amount' };
|
||||
}
|
||||
@@ -1701,7 +1702,7 @@ export class BotActions {
|
||||
|
||||
/** Withdraw an item from the bank by slot number. */
|
||||
async withdrawItem(target: BankItem | string | RegExp | number, amount: number = 1): Promise<BankWithdrawResult> {
|
||||
const validated = validateActionQuantity(amount, { allowAll: true });
|
||||
const validated = validateActionQuantity(amount, { allowAll: true, max: MAX_BANK_ACTION_QUANTITY });
|
||||
if (!validated.valid) {
|
||||
return { success: false, message: validated.message, reason: 'invalid_amount' };
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
skillDialogProductLabels,
|
||||
validateActionQuantity,
|
||||
MAX_ACTION_QUANTITY,
|
||||
MAX_BANK_ACTION_QUANTITY,
|
||||
MAX_SHOP_ACTION_QUANTITY,
|
||||
} from '../action-quantity';
|
||||
import type { DialogOption, InterfaceOption, InventoryItem } from '../types';
|
||||
@@ -195,6 +196,19 @@ describe('validateActionQuantity', () => {
|
||||
expect(validateActionQuantity(-1, { allowAll: true })).toEqual({ valid: true, amount: -1 });
|
||||
expect(validateActionQuantity(-1).valid).toBe(false);
|
||||
});
|
||||
|
||||
// Bank withdraw/deposit send ONE count-dialog packet whatever the amount,
|
||||
// so the 10k anti-packet-loop cap must not apply: capping there silently
|
||||
// no-opped 20k-coin withdrawals from large stacks.
|
||||
test('bank quantities above 10k are valid up to the wire limit', () => {
|
||||
expect(validateActionQuantity(20_000, { allowAll: true, max: MAX_BANK_ACTION_QUANTITY }))
|
||||
.toEqual({ valid: true, amount: 20_000 });
|
||||
expect(validateActionQuantity(120_000_000, { allowAll: true, max: MAX_BANK_ACTION_QUANTITY }).valid)
|
||||
.toBe(true);
|
||||
// 2^31-1 is the client's "All" button encoding, not a countable amount.
|
||||
expect(validateActionQuantity(MAX_BANK_ACTION_QUANTITY + 1, { allowAll: true, max: MAX_BANK_ACTION_QUANTITY }).valid)
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('nextShopStep', () => {
|
||||
|
||||
Reference in new issue
Block a user