import { describe, it, expect } from 'vitest';
import { tstore } from './0x5d_TSTORE.js';
import { tload } from './0x5c_TLOAD.js';
import { createFrame } from '../../Frame/index.js';
import { createMemoryHost } from '../../Host/createMemoryHost.js';
import { from as addressFrom } from '../../../primitives/Address/index.js';
describe('TSTORE (0x5d)', () => {
it('stores value to transient storage', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
const frame = createFrame({
stack: [0x42n, 0x1337n],
gasRemaining: 1000n,
address: addr,
isStatic: false,
});
expect(tstore(frame, host)).toBeNull();
expect(frame.gasRemaining).toBe(900n); // 1000 - 100
expect(frame.pc).toBe(1);
// Verify stored
const readFrame = createFrame({
stack: [0x42n],
gasRemaining: 1000n,
address: addr,
});
expect(tload(readFrame, host)).toBeNull();
expect(readFrame.stack).toEqual([0x1337n]);
});
it('overwrites existing transient value', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
// First write
let frame = createFrame({
stack: [0x42n, 0x1111n],
gasRemaining: 1000n,
address: addr,
isStatic: false,
});
expect(tstore(frame, host)).toBeNull();
// Overwrite
frame = createFrame({
stack: [0x42n, 0x2222n],
gasRemaining: 1000n,
address: addr,
isStatic: false,
});
expect(tstore(frame, host)).toBeNull();
// Verify overwritten
const readFrame = createFrame({
stack: [0x42n],
gasRemaining: 1000n,
address: addr,
});
expect(tload(readFrame, host)).toBeNull();
expect(readFrame.stack).toEqual([0x2222n]);
});
it('consumes fixed 100 gas', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
const frame = createFrame({
stack: [0x42n, 0x1337n],
gasRemaining: 5000n,
address: addr,
isStatic: false,
});
expect(tstore(frame, host)).toBeNull();
expect(frame.gasRemaining).toBe(4900n); // 5000 - 100 (always)
});
it('rejects write in static call', () => {
const host = createMemoryHost();
const frame = createFrame({
stack: [0x42n, 0x1337n],
gasRemaining: 1000n,
address: addressFrom("0x1234567890123456789012345678901234567890"),
isStatic: true,
});
expect(tstore(frame, host)).toEqual({ type: "WriteProtection" });
expect(frame.pc).toBe(0); // Not executed
});
it('returns StackUnderflow on insufficient stack', () => {
const host = createMemoryHost();
const frame = createFrame({
stack: [0x42n], // Missing value
gasRemaining: 1000n,
address: addressFrom("0x1234567890123456789012345678901234567890"),
isStatic: false,
});
expect(tstore(frame, host)).toEqual({ type: "StackUnderflow" });
});
it('returns OutOfGas when insufficient gas', () => {
const host = createMemoryHost();
const frame = createFrame({
stack: [0x42n, 0x1337n],
gasRemaining: 50n,
address: addressFrom("0x1234567890123456789012345678901234567890"),
isStatic: false,
});
expect(tstore(frame, host)).toEqual({ type: "OutOfGas" });
});
it('isolates transient storage by address', () => {
const host = createMemoryHost();
const addr1 = addressFrom("0x1111111111111111111111111111111111111111");
const addr2 = addressFrom("0x2222222222222222222222222222222222222222");
// Write to addr1
let frame = createFrame({
stack: [0x42n, 0xAAAAn],
gasRemaining: 1000n,
address: addr1,
isStatic: false,
});
expect(tstore(frame, host)).toBeNull();
// Write to addr2 (same key)
frame = createFrame({
stack: [0x42n, 0xBBBBn],
gasRemaining: 1000n,
address: addr2,
isStatic: false,
});
expect(tstore(frame, host)).toBeNull();
// Verify isolation
let readFrame = createFrame({
stack: [0x42n],
gasRemaining: 1000n,
address: addr1,
});
expect(tload(readFrame, host)).toBeNull();
expect(readFrame.stack).toEqual([0xAAAAn]);
readFrame = createFrame({
stack: [0x42n],
gasRemaining: 1000n,
address: addr2,
});
expect(tload(readFrame, host)).toBeNull();
expect(readFrame.stack).toEqual([0xBBBBn]);
});
it('stores max uint256 value', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
const MAX = (1n << 256n) - 1n;
const frame = createFrame({
stack: [0x42n, MAX],
gasRemaining: 1000n,
address: addr,
isStatic: false,
});
expect(tstore(frame, host)).toBeNull();
// Verify
const readFrame = createFrame({
stack: [0x42n],
gasRemaining: 1000n,
address: addr,
});
expect(tload(readFrame, host)).toBeNull();
expect(readFrame.stack).toEqual([MAX]);
});
it('stores zero value', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
const frame = createFrame({
stack: [0x42n, 0n],
gasRemaining: 1000n,
address: addr,
isStatic: false,
});
expect(tstore(frame, host)).toBeNull();
expect(frame.gasRemaining).toBe(900n); // Still costs 100
});
it('clears at transaction boundary', () => {
const host = createMemoryHost();
const addr = addressFrom("0x1234567890123456789012345678901234567890");
// Write value
let frame = createFrame({
stack: [0x42n, 0x1337n],
gasRemaining: 1000n,
address: addr,
isStatic: false,
});
expect(tstore(frame, host)).toBeNull();
// Read value (same transaction)
let readFrame = createFrame({
stack: [0x42n],
gasRemaining: 1000n,
address: addr,
});
expect(tload(readFrame, host)).toBeNull();
expect(readFrame.stack).toEqual([0x1337n]);
// After transaction boundary (auto-clear)
// host.endTransaction();
// readFrame = createFrame({
// stack: [0x42n],
// gasRemaining: 1000n,
// address: addr,
// });
// expect(tload(readFrame, host)).toBeNull();
// expect(readFrame.stack).toEqual([0n]);
});
});