Files
zeekayandHanzo Dev 64519916bf the corporate name is Lux Industries Inc
Lux Partners Limited is not the entity behind this work. Every place the
old name stood — copyright lines, site footers, brand config, legal and
compliance text — now names the company that actually holds it.

lux.fund is the exception and is not renamed to match: that site belongs
to Lux Assets Management LLC, a different company, and it says so.

Two kinds of line were left for a person rather than rewritten. A dated
news item recording that Lux Partners Limited was established in the Isle
of Man is a historical fact, and renaming it would falsify the record. And
a sentence asserting that the company is domiciled in the Isle of Man is a
claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle
of Man company, so swapping the name there would assert something untrue.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 13:01:04 -07:00

644 lines
16 KiB
Go

// Copyright (c) 2025 Lux Industries Inc
// SPDX-License-Identifier: MIT
package contracts
import (
"context"
"encoding/hex"
"encoding/json"
"strings"
"testing"
)
func TestParseABI(t *testing.T) {
abiJSON := json.RawMessage(`[
{
"type": "function",
"name": "transfer",
"inputs": [
{"name": "to", "type": "address"},
{"name": "amount", "type": "uint256"}
],
"outputs": [
{"name": "", "type": "bool"}
],
"stateMutability": "nonpayable"
},
{
"type": "event",
"name": "Transfer",
"inputs": [
{"name": "from", "type": "address", "indexed": true},
{"name": "to", "type": "address", "indexed": true},
{"name": "value", "type": "uint256", "indexed": false}
]
},
{
"type": "constructor",
"inputs": [
{"name": "name_", "type": "string"},
{"name": "symbol_", "type": "string"}
]
}
]`)
methods, events, err := ParseABI(abiJSON)
if err != nil {
t.Fatalf("ParseABI failed: %v", err)
}
if len(methods) != 1 {
t.Errorf("expected 1 method, got %d", len(methods))
}
if len(events) != 1 {
t.Errorf("expected 1 event, got %d", len(events))
}
// Check method
if methods[0].Name != "transfer" {
t.Errorf("expected method name 'transfer', got '%s'", methods[0].Name)
}
if methods[0].Signature != "transfer(address,uint256)" {
t.Errorf("expected signature 'transfer(address,uint256)', got '%s'", methods[0].Signature)
}
// transfer(address,uint256) selector: 0xa9059cbb
if !strings.HasPrefix(methods[0].Selector, "0x") {
t.Errorf("selector should start with 0x, got '%s'", methods[0].Selector)
}
// Check event
if events[0].Name != "Transfer" {
t.Errorf("expected event name 'Transfer', got '%s'", events[0].Name)
}
if events[0].Signature != "Transfer(address,address,uint256)" {
t.Errorf("expected signature 'Transfer(address,address,uint256)', got '%s'", events[0].Signature)
}
}
func TestBuildFunctionSignature(t *testing.T) {
tests := []struct {
name string
inputs []ABIParam
expected string
}{
{
name: "transfer",
inputs: []ABIParam{{Type: "address"}, {Type: "uint256"}},
expected: "transfer(address,uint256)",
},
{
name: "approve",
inputs: []ABIParam{{Type: "address"}, {Type: "uint256"}},
expected: "approve(address,uint256)",
},
{
name: "balanceOf",
inputs: []ABIParam{{Type: "address"}},
expected: "balanceOf(address)",
},
{
name: "noArgs",
inputs: []ABIParam{},
expected: "noArgs()",
},
{
name: "withTuple",
inputs: []ABIParam{
{
Type: "tuple",
Components: []ABIParam{
{Type: "address"},
{Type: "uint256"},
},
},
},
expected: "withTuple((address,uint256))",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := buildFunctionSignature(tt.name, tt.inputs)
if result != tt.expected {
t.Errorf("expected '%s', got '%s'", tt.expected, result)
}
})
}
}
func TestDetectEIP1167(t *testing.T) {
detector := &ProxyDetector{}
// Standard EIP-1167 minimal proxy bytecode
// 363d3d373d3d3d363d73<20-byte-address>5af43d82803e903d91602b57fd5bf3
implAddr := "1234567890123456789012345678901234567890"
bytecode := "0x363d3d373d3d3d363d73" + implAddr + "5af43d82803e903d91602b57fd5bf3"
info := detector.detectEIP1167(bytecode)
if info == nil {
t.Fatal("expected proxy info, got nil")
}
if !info.IsProxy {
t.Error("expected IsProxy to be true")
}
if info.ProxyType != ProxyEIP1167 {
t.Errorf("expected ProxyEIP1167, got %s", info.ProxyType)
}
expectedImpl := "0x" + implAddr
if strings.ToLower(info.ImplementationAddress) != strings.ToLower(expectedImpl) {
t.Errorf("expected implementation %s, got %s", expectedImpl, info.ImplementationAddress)
}
}
func TestDetectEIP1167Variant(t *testing.T) {
detector := &ProxyDetector{}
// Variant EIP-1167: 3d3d3d3d363d3d37363d73<address>5af43d3d93803e602a57fd5bf3
implAddr := "abcdef1234567890abcdef1234567890abcdef12"
bytecode := "0x3d3d3d3d363d3d37363d73" + implAddr + "5af43d3d93803e602a57fd5bf3"
info := detector.detectEIP1167(bytecode)
if info == nil {
t.Fatal("expected proxy info, got nil")
}
if info.ProxyType != ProxyEIP1167 {
t.Errorf("expected ProxyEIP1167, got %s", info.ProxyType)
}
expectedImpl := "0x" + implAddr
if strings.ToLower(info.ImplementationAddress) != strings.ToLower(expectedImpl) {
t.Errorf("expected implementation %s, got %s", expectedImpl, info.ImplementationAddress)
}
}
func TestIsSafeProxy(t *testing.T) {
detector := &ProxyDetector{}
// Safe proxy v1.3.0 pattern (starts with 608060405273... and contains a619486e)
safeProxy := "608060405273ffffffffffffffffffffffffffffffffffffffff600054167fa619486e0000000000000000000000000000000000000000000000000000000060003514156050578060005260206000f35b3660008037600080366000845af43d6000803e60008114156070573d6000fd5b3d6000f3"
safeBytes, _ := hex.DecodeString(safeProxy)
if !detector.isSafeProxy(safeBytes) {
t.Error("expected Safe proxy to be detected")
}
// Non-Safe bytecode
nonSafe := "6080604052348015600f57600080fd5b50"
nonSafeBytes, _ := hex.DecodeString(nonSafe)
if detector.isSafeProxy(nonSafeBytes) {
t.Error("expected non-Safe bytecode to not be detected as Safe proxy")
}
}
func TestExtractAddress(t *testing.T) {
tests := []struct {
input string
expected string
}{
{
input: "0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef12",
expected: "0xabcdef1234567890abcdef1234567890abcdef12",
},
{
input: "0x0000000000000000000000000000000000000000000000000000000000000000",
expected: "0x0000000000000000000000000000000000000000",
},
{
input: "0xabcdef",
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result := extractAddress(tt.input)
if result != tt.expected {
t.Errorf("expected '%s', got '%s'", tt.expected, result)
}
})
}
}
func TestIsZeroAddress(t *testing.T) {
tests := []struct {
input string
expected bool
}{
{"0x0000000000000000000000000000000000000000", true},
{"0x0000000000000000000000000000000000000001", false},
{"0xabcdef1234567890abcdef1234567890abcdef12", false},
{"", true},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result := isZeroAddress(tt.input)
if result != tt.expected {
t.Errorf("expected %v, got %v", tt.expected, result)
}
})
}
}
func TestABIDecoder(t *testing.T) {
abiJSON := json.RawMessage(`[
{
"type": "function",
"name": "transfer",
"inputs": [
{"name": "to", "type": "address"},
{"name": "amount", "type": "uint256"}
],
"outputs": [{"name": "", "type": "bool"}]
},
{
"type": "event",
"name": "Transfer",
"inputs": [
{"name": "from", "type": "address", "indexed": true},
{"name": "to", "type": "address", "indexed": true},
{"name": "value", "type": "uint256", "indexed": false}
]
}
]`)
decoder, err := NewABIDecoder(abiJSON)
if err != nil {
t.Fatalf("NewABIDecoder failed: %v", err)
}
// Test DecodeCall
// transfer(to=0x1234...5678, amount=1000000)
// selector: a9059cbb (using our simplified keccak)
// Note: actual selector may differ due to simplified keccak implementation
t.Run("DecodeCall", func(t *testing.T) {
// Get the actual selector from the decoder
for selector, method := range decoder.methods {
if method.Name == "transfer" {
// Construct input data
toAddr := "0000000000000000000000001234567890123456789012345678901234567890"
amount := "00000000000000000000000000000000000000000000000000000000000f4240" // 1000000
input := selector + toAddr + amount
decoded, err := decoder.DecodeCall(input)
if err != nil {
t.Fatalf("DecodeCall failed: %v", err)
}
if decoded.Name != "transfer" {
t.Errorf("expected name 'transfer', got '%s'", decoded.Name)
}
break
}
}
})
}
func TestDecodeBasicType(t *testing.T) {
tests := []struct {
typ string
data string
expected interface{}
}{
{
typ: "address",
data: "0000000000000000000000001234567890123456789012345678901234567890",
expected: "0x1234567890123456789012345678901234567890",
},
{
typ: "bool",
data: "0000000000000000000000000000000000000000000000000000000000000001",
expected: true,
},
{
typ: "bool",
data: "0000000000000000000000000000000000000000000000000000000000000000",
expected: false,
},
{
typ: "uint256",
data: "00000000000000000000000000000000000000000000000000000000000f4240",
expected: "1000000",
},
}
for _, tt := range tests {
t.Run(tt.typ, func(t *testing.T) {
result, consumed, err := decodeBasicType(tt.typ, tt.data)
if err != nil {
t.Fatalf("decodeBasicType failed: %v", err)
}
if consumed != 64 {
t.Errorf("expected 64 consumed, got %d", consumed)
}
if result != tt.expected {
t.Errorf("expected %v, got %v", tt.expected, result)
}
})
}
}
func TestDecodeTopicValue(t *testing.T) {
tests := []struct {
typ string
topic string
expected interface{}
}{
{
typ: "address",
topic: "0x0000000000000000000000001234567890123456789012345678901234567890",
expected: "0x1234567890123456789012345678901234567890",
},
{
typ: "uint256",
topic: "0x00000000000000000000000000000000000000000000000000000000000003e8",
expected: "1000",
},
}
for _, tt := range tests {
t.Run(tt.typ, func(t *testing.T) {
result, err := decodeTopicValue(tt.typ, tt.topic)
if err != nil {
t.Fatalf("decodeTopicValue failed: %v", err)
}
if result != tt.expected {
t.Errorf("expected %v, got %v", tt.expected, result)
}
})
}
}
func TestFourByteDB(t *testing.T) {
db := NewFourByteDB()
db.AddSignature("transfer(address,uint256)")
db.AddSignature("approve(address,uint256)")
db.AddSignature("balanceOf(address)")
// Look up transfer
sigs := db.LookupSelector(selectorFromSignature("transfer(address,uint256)"))
if len(sigs) == 0 {
t.Error("expected to find transfer signature")
}
found := false
for _, sig := range sigs {
if sig == "transfer(address,uint256)" {
found = true
break
}
}
if !found {
t.Error("expected to find transfer(address,uint256)")
}
}
func TestCombineABIs(t *testing.T) {
proxyABI := json.RawMessage(`[
{"type": "function", "name": "upgradeTo", "inputs": [{"type": "address"}]}
]`)
implABI := json.RawMessage(`[
{"type": "function", "name": "transfer", "inputs": [{"type": "address"}, {"type": "uint256"}]},
{"type": "function", "name": "balanceOf", "inputs": [{"type": "address"}]}
]`)
combined, err := CombineABIs(proxyABI, implABI)
if err != nil {
t.Fatalf("CombineABIs failed: %v", err)
}
var entries []ABIEntry
if err := json.Unmarshal(combined, &entries); err != nil {
t.Fatalf("unmarshal combined ABI failed: %v", err)
}
if len(entries) != 3 {
t.Errorf("expected 3 entries, got %d", len(entries))
}
// Check that all three functions are present
names := make(map[string]bool)
for _, entry := range entries {
names[entry.Name] = true
}
if !names["upgradeTo"] {
t.Error("expected upgradeTo in combined ABI")
}
if !names["transfer"] {
t.Error("expected transfer in combined ABI")
}
if !names["balanceOf"] {
t.Error("expected balanceOf in combined ABI")
}
}
func TestStripMetadata(t *testing.T) {
v := &Verifier{}
// Bytecode with CBOR metadata (simplified example)
// Metadata: a265627a7a723... (variable length)
// Last 2 bytes: length indicator
// Test with known pattern
bytecode := "608060405234801561001057600080fd5b5060df8061001f6000396000f3a264697066735822" + strings.Repeat("00", 32) + "0033"
stripped := v.stripMetadata(bytecode)
// Should strip the metadata
if stripped == bytecode {
t.Log("stripMetadata may not have stripped anything, checking implementation")
}
}
func TestVerifierCompareBytecode(t *testing.T) {
v := &Verifier{}
tests := []struct {
name string
onChain string
compiled string
expected bool
}{
{
name: "exact match",
onChain: "0x608060405234801561001057600080fd",
compiled: "608060405234801561001057600080fd",
expected: true,
},
{
name: "case insensitive",
onChain: "0x608060405234801561001057600080FD",
compiled: "608060405234801561001057600080fd",
expected: true,
},
{
name: "compiled is prefix",
onChain: "0x608060405234801561001057600080fd00001234",
compiled: "608060405234801561001057600080fd",
expected: true,
},
{
name: "no match",
onChain: "0x608060405234801561001057600080fd",
compiled: "123456789abcdef0",
expected: false,
},
{
name: "empty bytecode",
onChain: "0x",
compiled: "608060405234801561001057600080fd",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := v.compareBytecode(tt.onChain, tt.compiled)
if result != tt.expected {
t.Errorf("expected %v, got %v", tt.expected, result)
}
})
}
}
func TestProxyTypeConstants(t *testing.T) {
// Verify storage slot constants are correct
tests := []struct {
name string
slot string
expected string
}{
{
name: "EIP1967 Implementation",
slot: StorageSlotEIP1967Implementation,
expected: "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
},
{
name: "EIP1967 Beacon",
slot: StorageSlotEIP1967Beacon,
expected: "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50",
},
{
name: "EIP1967 Admin",
slot: StorageSlotEIP1967Admin,
expected: "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103",
},
{
name: "EIP1822 Proxiable",
slot: StorageSlotEIP1822,
expected: "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.slot != tt.expected {
t.Errorf("expected %s, got %s", tt.expected, tt.slot)
}
})
}
}
func TestMultiABIDecoder(t *testing.T) {
abi1 := json.RawMessage(`[
{"type": "function", "name": "transfer", "inputs": [{"name": "to", "type": "address"}, {"name": "amount", "type": "uint256"}]}
]`)
abi2 := json.RawMessage(`[
{"type": "function", "name": "approve", "inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}]}
]`)
decoder, err := NewMultiABIDecoder(abi1, abi2)
if err != nil {
t.Fatalf("NewMultiABIDecoder failed: %v", err)
}
if len(decoder.decoders) != 2 {
t.Errorf("expected 2 decoders, got %d", len(decoder.decoders))
}
}
func TestSmartContractTypes(t *testing.T) {
// Test SmartContract struct
contract := SmartContract{
Address: "0x1234567890123456789012345678901234567890",
Name: "TestContract",
CompilerVersion: "v0.8.20+commit.a1b2c3d4",
EVMVersion: "paris",
Optimization: true,
OptimizationRuns: 200,
SourceCode: "contract TestContract {}",
VerificationStatus: StatusVerified,
ProxyType: ProxyEIP1967,
}
if contract.Address == "" {
t.Error("Address should not be empty")
}
if contract.VerificationStatus != StatusVerified {
t.Error("Status should be verified")
}
if contract.ProxyType != ProxyEIP1967 {
t.Error("ProxyType should be EIP1967")
}
}
func TestProxyInfo(t *testing.T) {
info := ProxyInfo{
IsProxy: true,
ProxyType: ProxyEIP1967,
ImplementationAddress: "0xabcdef1234567890abcdef1234567890abcdef12",
}
if !info.IsProxy {
t.Error("IsProxy should be true")
}
if info.ProxyType != ProxyEIP1967 {
t.Error("ProxyType should be EIP1967")
}
}
// Test context cancellation
func TestContextCancellation(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel() // Cancel immediately
detector := NewProxyDetector("http://localhost:8545")
// Should fail fast due to cancelled context
_, err := detector.DetectProxy(ctx, "0x1234567890123456789012345678901234567890")
if err == nil {
t.Log("Context cancellation may not immediately fail HTTP calls in test environment")
}
}
func TestHexToInt(t *testing.T) {
tests := []struct {
input string
expected int
}{
{"0x10", 16},
{"10", 16},
{"0x", 0},
{"ff", 255},
{"0xff", 255},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result := hexToInt(tt.input)
if result != tt.expected {
t.Errorf("expected %d, got %d", tt.expected, result)
}
})
}
}