Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/account/Calibur/Calibur7702Account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,17 @@ export class Calibur7702Account
const hook = BigInt(settings.hook ?? ZeroAddress);
const expiration = BigInt(settings.expiration ?? 0);
const isAdmin = settings.isAdmin ? 1n : 0n;
if (expiration < 0n || expiration >= 1n << 40n) {
// the on-chain field is uint40; an oversized value (e.g. a millisecond
// timestamp) would bleed into the isAdmin bit at position 200
throw new RangeError(
"expiration must be a unix timestamp in seconds that fits in 40 bits, " +
`got ${expiration}. Did you pass milliseconds instead of seconds?`,
);
}
if (hook < 0n || hook >= 1n << 160n) {
throw new RangeError("hook must be a valid 20-byte address.");
}
return (isAdmin << 200n) | (expiration << 160n) | hook;
}

Expand Down
28 changes: 28 additions & 0 deletions test/calibur/packKeySettings.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Offline tests for Calibur key-settings packing: the on-chain layout is
// (isAdmin << 200) | (uint40 expiration << 160) | (uint160 hook), so an
// oversized expiration must be rejected rather than silently setting the
// isAdmin bit.

const ak = require('../../dist/index.cjs');

const Calibur = ak.Calibur7702Account;

describe('Calibur7702Account.packKeySettings', () => {
test('packs a valid seconds timestamp without touching the isAdmin bit', () => {
const expiration = 1786000000n; // seconds, fits in uint40
const packed = Calibur.packKeySettings({ expiration, isAdmin: false });
expect((packed >> 160n) & ((1n << 40n) - 1n)).toBe(expiration);
expect((packed >> 200n) & 1n).toBe(0n);
});

test('rejects a milliseconds timestamp instead of leaking into isAdmin', () => {
const ms = 1786000000000n; // >= 2^40: would set bit 200 (isAdmin)
expect(() => Calibur.packKeySettings({ expiration: ms })).toThrow(RangeError);
});
Comment on lines +18 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the actionable expiration error hint.

This test only checks RangeError; it would still pass if the promised “milliseconds instead of seconds” guidance were removed.

Suggested assertion
-        expect(() => Calibur.packKeySettings({ expiration: ms })).toThrow(RangeError);
+        expect(() => Calibur.packKeySettings({ expiration: ms })).toThrow(
+            /milliseconds instead of seconds/i,
+        );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('rejects a milliseconds timestamp instead of leaking into isAdmin', () => {
const ms = 1786000000000n; // >= 2^40: would set bit 200 (isAdmin)
expect(() => Calibur.packKeySettings({ expiration: ms })).toThrow(RangeError);
});
test('rejects a milliseconds timestamp instead of leaking into isAdmin', () => {
const ms = 1786000000000n; // >= 2^40: would set bit 200 (isAdmin)
expect(() => Calibur.packKeySettings({ expiration: ms })).toThrow(
/milliseconds instead of seconds/i,
);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/calibur/packKeySettings.test.js` around lines 18 - 21, Update the test
case “rejects a milliseconds timestamp instead of leaking into isAdmin” to
assert that the thrown RangeError message includes the actionable guidance
indicating the expiration value must use seconds rather than milliseconds. Keep
the existing RangeError assertion and verify the message content on the same
thrown error.


test('rejects an out-of-range hook value', () => {
expect(() => Calibur.packKeySettings({ hook: '0x' + 'ff'.repeat(21) })).toThrow(
RangeError,
);
});
});
Loading