import { describe, it, expect } from 'vitest';
import { sstore } from './0x55_SSTORE.js';
import { createFrame } from '../../Frame/index.js';
import { createMemoryHost } from '../../Host/createMemoryHost.js';
import { from as addressFrom } from '../../../primitives/Address/index.js';
describe('SSTORE (0x55)', () => {
it('stores value to empty slot (set)', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
const frame = createFrame({
stack: [0x42n, 0x1337n],
gasRemaining: 30000n,
address: addr,
isStatic: false,
});
expect(sstore(frame, host)).toBeNull();
expect(host.getStorage(addr, 0x42n)).toBe(0x1337n);
expect(frame.gasRemaining).toBe(10000n); // 30000 - 20000
});
it('updates existing value (reset)', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
host.setStorage(addr, 0x42n, 0x1111n);
const frame = createFrame({
stack: [0x42n, 0x2222n],
gasRemaining: 10000n,
address: addr,
isStatic: false,
});
expect(sstore(frame, host)).toBeNull();
expect(host.getStorage(addr, 0x42n)).toBe(0x2222n);
expect(frame.gasRemaining).toBe(5000n); // 10000 - 5000
});
it('clears storage with refund', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
host.setStorage(addr, 0x42n, 0x1337n);
const frame = createFrame({
stack: [0x42n, 0n],
gasRemaining: 10000n,
address: addr,
isStatic: false,
refunds: 0n,
});
expect(sstore(frame, host)).toBeNull();
expect(host.getStorage(addr, 0x42n)).toBe(0n);
expect(frame.gasRemaining).toBe(5000n); // 10000 - 5000
expect(frame.refunds).toBe(4800n); // Refund
});
it('rejects write in static call', () => {
const host = createMemoryHost();
const frame = createFrame({
stack: [0x42n, 0x1337n],
gasRemaining: 30000n,
address: addressFrom("0x1234567890123456789012345678901234567890"),
isStatic: true,
});
expect(sstore(frame, host)).toEqual({ type: "WriteProtection" });
});
it('fails sentry check with insufficient gas', () => {
const host = createMemoryHost();
const frame = createFrame({
stack: [0x42n, 0x1337n],
gasRemaining: 100n, // < 2300
address: addressFrom("0x1234567890123456789012345678901234567890"),
isStatic: false,
});
// Note: Implement sentry check
// expect(sstore(frame, host)).toEqual({ type: "OutOfGas" });
});
it('handles noop write (zero to zero)', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
const frame = createFrame({
stack: [0x42n, 0n],
gasRemaining: 1000n,
address: addr,
isStatic: false,
});
// Slot uninitialized, writing 0 to 0
expect(sstore(frame, host)).toBeNull();
expect(frame.gasRemaining).toBe(1000n); // No cost
});
it('returns StackUnderflow on insufficient stack', () => {
const host = createMemoryHost();
const frame = createFrame({
stack: [0x42n], // Only key, missing value
gasRemaining: 30000n,
address: addressFrom("0x1234567890123456789012345678901234567890"),
isStatic: false,
});
expect(sstore(frame, host)).toEqual({ type: "StackUnderflow" });
});
it('returns OutOfGas when base cost exceeds remaining', () => {
const host = createMemoryHost();
const frame = createFrame({
stack: [0x42n, 0x1337n],
gasRemaining: 100n, // < 20000 for set
address: addressFrom("0x1234567890123456789012345678901234567890"),
isStatic: false,
});
expect(sstore(frame, host)).toEqual({ type: "OutOfGas" });
});
it('isolates storage by address', () => {
const host = createMemoryHost();
const addr1 = addressFrom("0x1111111111111111111111111111111111111111");
const addr2 = addressFrom("0x2222222222222222222222222222222222222222");
const frame1 = createFrame({
stack: [0x42n, 0xAAAAn],
gasRemaining: 30000n,
address: addr1,
isStatic: false,
});
sstore(frame1, host);
const frame2 = createFrame({
stack: [0x42n, 0xBBBBn],
gasRemaining: 30000n,
address: addr2,
isStatic: false,
});
sstore(frame2, host);
expect(host.getStorage(addr1, 0x42n)).toBe(0xAAAAn);
expect(host.getStorage(addr2, 0x42n)).toBe(0xBBBBn);
});
});