Macros
Macros are compile-time constructs that allow you to write conditional code paths and optimize performance in your obfuscated scripts.
Quick Reference
| Macro | Type | Purpose |
|---|---|---|
WYNF_OBFUSCATED | boolean | Detect if script is obfuscated |
WYNF_NO_VIRTUALIZE | function wrapper | Mark function for native execution (unprotected) |
WYNF_CRASH | function call | Securely crash and corrupt the VM (destructive) |
WYNF_IS_CALLER_WYNFUSCATE | function call | Detect external callers (security) |
WYNF_ENC_STRING | string wrapper | Extra encryption for sensitive strings (security) |
WYNF_ENC_NUM | number wrapper | Extra encryption for sensitive numbers (security) |
WYNF_LINE | function call | Get source line number at compile time (debug) |
WYNF_NO_UPVALUES | function wrapper | Compatibility wrapper for callbacks (compatibility) |
WYNF_SECURE_CALL | function wrapper | Restrict function to VM-only callers (security) |
WYNF_SECURE_CALLBACK | function wrapper | Secure wrapper for event callbacks (security) |
WYNF_ENC_FUNC | function wrapper | Runtime-decrypted function with key server support (security) |
WYNF_OBFUSCATED
booleanA compile-time constant that evaluates to true in obfuscated builds. In plain Lua (non-obfuscated), it behaves like a normal global (typically nil/false).
Dead Code Pruning
When used as an if condition, the compiler removes unreachable branches from the output entirely. This means dev-only stubs and fallback code gated by WYNF_OBFUSCATED are not present in the obfuscated bytecode at all — not just skipped at runtime.
Basic Example
Loading...Stripping Dev Code from Output
This pattern ensures dev fallback code is never shipped in the obfuscated file:
Loading...Development Stubs
Define stubs for macros so your code runs in development without errors. These stubs are completely removed from obfuscated output:
Loading...- Use to strip dev-only stubs from output
- Use to gate debug/profiling code
- Use for feature flags between dev/prod
- Use for verbose logging in dev only
- Assuming it prevents debugging/tampering
- Relying on it as sole protection for secrets
- Using for license enforcement alone
WYNF_NO_VIRTUALIZE
performanceWARNING: Code Exposure
Code wrapped with WYNF_NO_VIRTUALIZE is NOT protected through virtualization. Your code will be minified (local names, upvalue names, comments, and line information are stripped), but raw strings and logic will still be exposed.
Only use this for performance-critical code that does not contain sensitive logic, API keys, URLs, or any code you need to keep private.
Marks a function to run as native Lua instead of inside the VM. This bypasses VM overhead for performance-critical code like hot loops or heavy math/table/string operations.
Correct Usage
Use for performance-critical code that contains no sensitive information:
Loading...Incorrect Usage
Never use for code containing sensitive information - it will be exposed:
Loading...Upvalues
Functions can capture variables from the parent scope (upvalues). While upvalue names are stripped, the values themselves are not secure. Avoid referencing upvalues that contain sensitive information.
Loading...Development Stub
To run un-obfuscated scripts without errors, add this stub at the top of your file:
Loading...This stub is automatically ignored when the script is obfuscated.
- Use for hot loops or heavy computations
- Use for math/table/string operations
- Use only for non-sensitive code
- Keep function body self-contained
- Test in both obfuscated and plain runs
- Sensitive strings (API keys, URLs)
- License/auth validation logic
- Proprietary algorithms
- Recursive functions
- Nesting inside another no-virt function
Limitations
- Self-recursion: Not supported - the function cannot call itself
- Nesting: Cannot use WYNF_NO_VIRTUALIZE inside another no-virt function
- Mutual recursion: Two no-virt functions cannot call each other
WYNF_CRASH
securityImmediately and securely crashes the VM, corrupting the VM context to prevent recovery or analysis. Use this as a last-resort defense when tampering or unauthorized access is detected.
Correct Usage
Loading...Incorrect Usage
Loading...Important: Direct Calls Only
WYNF_CRASH() must be called directly - it cannot be stored in a variable, passed as a callback, or referenced indirectly. The macro is detected at compile time and replaced with a secure crash sequence.
Behavior
When executed, the VM is immediately corrupted and enters an unrecoverable state. No code after WYNF_CRASH() will execute. This makes it ideal for anti-tamper checks where you want to ensure the script cannot be analyzed or recovered after a violation is detected.
Development Stub
To run un-obfuscated scripts without errors, add this stub at the top of your file:
Loading...This stub is automatically ignored when the script is obfuscated. In development, it throws a standard Lua error to simulate the crash behavior.
- Use for anti-tamper responses
- Call directly in conditionals
- Use as last line of defense
- Combine with WYNF_OBFUSCATED checks
- Storing in variables
- Passing as function arguments
- Using in table values
- Indirect references of any kind
WYNF_IS_CALLER_WYNFUSCATE
securityReturns true if the current function was called from within your obfuscated code, and false if it was called from an external source like an exploit script. This protects your functions from being hijacked or invoked by malicious code, even when exploiters have access to function references.
Basic Example
Loading...Protecting RemoteEvent Handlers
Loading...Protecting Module Functions
Loading...Correct Usage
Check once at the entry point, then proceed with your logic:
Loading...Incorrect Usage
Loading...Development Stub
To run un-obfuscated scripts without errors, add this stub at the top of your file:
Loading...The stub always returns true, bypassing protection during development. The real protection activates when you obfuscate.
CLI/Development Environments
In CLI or development environments, this macro always returns true. This is expected - use Studio or production environments to test external caller detection.
- Use at the beginning of sensitive functions
- Protect functions exposed via RemoteEvents
- Combine with other validation layers
- Test logic works when macro returns true
- Using as your only security measure
- Calling in tight loops (expensive)
- Assuming it catches all attack vectors
- Expecting it to work in CLI environments
WYNF_ENC_STRING
securityApplies additional encryption layers to a string literal, providing extra protection for highly sensitive values like API keys, passwords, and encryption keys.
Basic Example
Loading...When to Use
- API Keys — External service credentials
- Encryption Keys — Keys used to encrypt/decrypt game data
- License Validation — Strings used in license checking logic
- Admin Passwords — Hardcoded fallback credentials
- Secret URLs — Hidden API endpoints
When NOT to Use
- Frequently accessed strings — Each access has overhead
- Strings in tight loops — Performance impact multiplies
- Non-sensitive UI text — Regular encryption is sufficient
- Large strings — Encryption overhead increases with size
Performance Consideration
WYNF_ENC_STRING adds additional encryption layers which has a performance cost on each access. For strings accessed once or twice per session, this is negligible. For strings accessed frequently, consider using regular string encryption instead.
Example: Protecting an API Client
Loading...Syntax Rules
Loading...Development Stub
To run your script without obfuscation during development, add this stub at the top:
Loading...This allows your code to run normally in Studio or CLI while developing. When obfuscated, the real macro takes over and provides the additional encryption.
- Use for your most sensitive secrets
- Place at module scope or function entry
- Combine with other security measures
- Test code works before obfuscating
- Using in tight loops
- Using for every string
- Assuming strings are impossible to extract
- Using with variables or expressions
WYNF_ENC_NUM
securityProtects a numeric literal by encoding it in an encrypted form, preventing magic numbers from being trivially found via memory scanning or static analysis.
Basic Example
Loading...When to Use
- Product IDs — Asset or game pass identifiers
- Feature Flags — Numeric flags for premium features
- Salts — Values used in hashing or validation
- Thresholds — Limits you do not want easily modified
- Magic Numbers — Any constant you want hidden
Correct Usage
Loading...Incorrect Usage
Loading...Performance Consideration
WYNF_ENC_NUM has overhead on each access. For best performance, assign once to a local variable and reuse that local throughout your code.
Development Stub
To run your script without obfuscation during development, add this stub at the top:
Loading...This allows your code to run normally in Studio or CLI while developing. When obfuscated, the real macro takes over and provides the additional encryption.
- Use for sensitive magic numbers
- Assign to a local once, reuse it
- Use numeric literals only
- Combine with other security measures
- Using variables as arguments
- Using expressions
- Calling repeatedly in tight loops
- Assuming numbers are impossible to extract
WYNF_LINE
debugExpands to the current source line number at compile time. This provides a stable line marker for logs and error messages without relying on VM debug info, which is stripped during obfuscation.
Basic Example
Loading...When to Use
- Error Messages — Include line numbers in error reports
- Debug Logging — Track execution flow with stable markers
- Support Tickets — Help users report issues with line references
- Assertions — Add context to assertion failures
Correct Usage
Loading...Incorrect Usage
Loading...Development Stub
To run your script without obfuscation during development, add this stub at the top:
Loading...This uses the Lua debug library to get the actual line number during development. When obfuscated, the macro is replaced with the compile-time line number.
- Use for error and debug messages
- Call directly as a global function
- Use to help with support tickets
- Combine with other context info
- Shadowing with a local variable
- Storing the function reference
- Expecting runtime line tracking
- Using for security purposes
WYNF_NO_UPVALUES
compatibilityCreates a lightweight wrapper around your function for compatibility with certain environments or APIs that have issues with virtualized functions used as callbacks. Your function remains fully protected — only the wrapper is simplified for compatibility.
Basic Example
Loading...When to Use
- Signal Callbacks — Functions connected to events or signals
- Hook APIs — Functions passed to hooking or interception APIs
- Third-Party Libraries — Callbacks passed to external libraries
- Environment Compatibility — When you encounter errors with virtualized callbacks
Correct Usage
Loading...Compatibility, Not Security
This macro is for compatibility purposes only. Your function body remains virtualized and protected, but the wrapper itself is simplified. Only use this when you encounter compatibility issues with callbacks — for normal functions, standard virtualization provides stronger protection.
Development Stub
To run your script without obfuscation during development, add this stub at the top:
Loading...This simply returns the function as-is during development. When obfuscated, the macro creates the compatibility wrapper while keeping your code protected.
- Use for callbacks and signal handlers
- Use when you hit compatibility issues
- Wrap function literals directly
- Test in your target environment
- Using for all functions (unnecessary)
- Using when standard virtualization works
- Expecting extra security benefits
- Using for non-callback functions
WYNF_SECURE_CALL
securityMarks a function as VM-only callable. Calls to this function will only succeed when they originate from inside your obfuscated code. If an external script obtains a reference to this function and tries to call it directly, the call will be rejected.
Basic Example
Loading...When to Use
- Internal Helpers — Functions that should never be invoked from outside
- Decoders — Functions that decode or decrypt sensitive data
- Guard Logic — Internal security checks and validation functions
- Sensitive Operations — Functions that would be dangerous if called out of band
When NOT to Use
- Roblox Events — RemoteEvents, Signals, and UI callbacks are invoked by the engine, not the VM
- Engine Callbacks — Any function passed to Roblox APIs that will call it externally
For callbacks and event handlers, use WYNF_SECURE_CALLBACK instead.
Fail-Closed Behavior
When an external caller attempts to invoke a WYNF_SECURE_CALL function, the call is rejected immediately (fail-closed). This is intentional — use this for functions that should never be called from outside under any circumstances.
Development Stub
To run your script without obfuscation during development, add this stub at the top:
Loading...The stub returns the function as-is during development. When obfuscated, the real caller validation takes effect.
- Use for internal-only functions
- Use for decoders and sensitive helpers
- Use for functions that must never be called externally
- Combine with other security measures
- Using for event handlers or callbacks
- Using for functions called by Roblox APIs
- Using when you need soft rejection
- Using as your only security measure
WYNF_SECURE_CALLBACK
securityReturns a wrapped callback that performs a caller gate before invoking your function. Intended for event handlers and callbacks that are called by external systems like Roblox signals, RemoteEvents, and UI events.
Basic Example
Loading...Roblox Examples
Loading...Fail-Quiet Behavior
Unlike WYNF_SECURE_CALL, this macro uses fail-quiet behavior. If the caller is external, the callback simply returns nil instead of crashing. This is appropriate for callbacks where you want silent rejection rather than hard failure.
When to Use
- RemoteEvent Handlers — OnServerEvent, OnClientEvent callbacks
- BindableEvent Handlers — Internal signal callbacks
- UI Callbacks — Button clicks, input handlers
- RunService Connections — Heartbeat, RenderStepped callbacks
- Any Engine Callback — Functions passed to Roblox APIs that will invoke them
Development Stub
To run your script without obfuscation during development, add this stub at the top:
Loading...The stub returns the function as-is during development. When obfuscated, the wrapper provides caller validation while maintaining compatibility with engine callbacks.
- Use for event handlers and callbacks
- Use for functions passed to Roblox APIs
- Use when you need soft rejection
- Wrap directly before passing to Connect()
- Using for internal-only functions (use WYNF_SECURE_CALL)
- Using when you need fail-closed behavior
- Using as your only security measure
- Expecting it to prevent all callback attacks
WYNF_ENC_FUNC
securityEncrypts a function body at build time and returns a callable wrapper that decrypts and executes the function at runtime using a key provided by your server. This is designed for whitelist/key-server workflows where the correct decryption key is only available at runtime from a trusted source.
How It Works
- Build time: The function is extracted, encrypted with your key, and stored as a separate payload
- Runtime: On first call, the wrapper decrypts the function using the runtime key, patches the VM, and executes
- Security: The encryption key never appears in the obfuscated output — it must come from your server
Signature
Loading...Generating a Key
You need a secure 64-character hex string to use as your encryption/decryption key. We provide a built-in generator in the dashboard:
- Go to Dashboard → Settings
- Scroll down to the Developer Tools section
- Click Generate to create a cryptographically secure 64-character hex key
- Copy the key and store it securely on your server
- Use this same key for both
encKeyHex64in your script and as the runtimedecKeyreturned by your server
Correct Usage
Loading...Incorrect Usage
Loading...Key Model
The runtime decKey must be the same 64-character hex string as the build-time encKeyHex64. Your server stores this key and returns it to authorized clients at runtime. If the server returns a different key (or the key is tampered with in transit), the decrypt/decode will fail and the wrapper will trap.
Key Security
The encryption key (encKeyHex64) is a compile-time input only — it is never emitted in the obfuscated output. The decryption key must come from your server at runtime, not hardcoded in your script. If you hardcode the decryption key, you defeat the entire purpose of this macro.
Fail-Closed on Wrong Key
If the decryption key is wrong or the payload is tampered with, the function will fail closed — crash or poison the VM, not return garbage. This ensures attackers cannot probe for partial decryption or use incorrect keys.
Current Limitations
- The protected function must be a constant function literal (not a variable)
- The protected function must not capture upvalues from outer scopes
- The protected function must not contain nested function literals
Performance Notes
- First call: Incurs decrypt + load overhead (cold path)
- Subsequent calls: The dispatcher is cached for fast execution
- No persistent cache: Decrypted proto is not stored in a globally dumpable structure
Development Stub
To run your script without obfuscation during development, add this stub at the top:
Loading...This ignores the encryption key arguments and returns the function as-is during development. When obfuscated, the real macro encrypts the function and requires the correct runtime key.
- Fetch decryption key from your server
- Use for whitelist/license workflows
- Use constant function literals only
- Keep protected functions self-contained
- Handle key fetch failures gracefully
- Hardcoding the decryption key
- Using variables for the function argument
- Capturing upvalues in protected function
- Nesting functions inside protected function
- Using variables for the encryption key
When to Use What
WYNF_OBFUSCATED
Use to strip dev-only code from obfuscated output. Unreachable branches are completely removed at compile time — not just skipped at runtime. Good for dev stubs, debug logging, and environment-specific code paths.
WYNF_NO_VIRTUALIZE
Use only for performance-critical code that runs frequently and contains no sensitive information. The function runs as native Lua (minified only), bypassing VM protection entirely. Code inside is exposed - use for math, loops, and non-secret operations only.
WYNF_CRASH
Use as a last-resort defense when tampering or unauthorized access is detected. Immediately and securely crashes the VM, making recovery impossible. Must be called directly - cannot be stored or passed indirectly.
WYNF_IS_CALLER_WYNFUSCATE
Use to protect sensitive functions from being called by external code (exploits). Returns true if called from your obfuscated code, false otherwise. Check once at entry point - avoid using in loops due to stack walking overhead.
WYNF_ENC_STRING
Use for highly sensitive strings like API keys, passwords, and encryption keys. Adds additional encryption layers for extra protection. Avoid using in tight loops or for frequently accessed strings due to performance overhead.
WYNF_ENC_NUM
Use for sensitive magic numbers like product IDs, feature flags, salts, and thresholds. Prevents numbers from being trivially found via scanning. Assign once to a local and reuse for best performance.
WYNF_LINE
Use for debugging and error reporting. Returns the source line number at compile time, providing stable line markers for logs and error messages even after obfuscation.
WYNF_NO_UPVALUES
Use for callback compatibility when virtualized functions cause issues with certain APIs or environments. Creates a compatible wrapper while keeping your code protected.
WYNF_SECURE_CALL
Use for internal-only functions that should never be called from outside your obfuscated code. External calls are rejected (fail-closed). For callbacks passed to Roblox APIs, use WYNF_SECURE_CALLBACK instead.
WYNF_SECURE_CALLBACK
Use for event handlers and callbacks passed to Roblox APIs (RemoteEvents, signals, UI events). Unlike WYNF_SECURE_CALL, uses fail-quiet behavior (returns nil) for compatibility with engine-invoked callbacks.
WYNF_ENC_FUNC
Use for whitelist/key-server workflows where critical functions should only work with a valid runtime key from your server. The function is encrypted at build time and decrypted at runtime using a key that must be fetched from your backend — never hardcoded.