Repository: vyperlang/vyper

Issue:
Events with list arguments don't work when list is not 1st arg
And when list arg is initialized from a variable.

Created a pull request with tests that expose the issue:
https://github.com/ethereum/vyper/pull/729

Test cases:

```python
def test_2nd_var_list_packing(t, get_last_log, get_contract_with_gas_estimation, chain):
    t.s = chain
    code = """
Bar: event({arg1: int128, arg2: int128[4]})

@public
def foo():
    a: int128[4] = [1, 2, 3, 4]
    log.Bar(10, a)
    """
    c = get_contract_with_gas_estimation(code)

    c.foo()
    assert get_last_log(t, c)["arg2"] == [1, 2, 3, 4]


def test_2nd_var_storage_list_packing(t, get_last_log, get_contract_with_gas_estimation, chain):
    t.s = chain
    code = """
Bar: event({arg1: int128, arg2: int128[4]})
x: int128[4]

@public
def foo():
    log.Bar(10, self.x)

@public
def set_list():
    self.x = [1, 2, 3, 4]
    """
    c = get_contract_with_gas_estimation(code)

    c.foo()
    assert get_last_log(t, c)["arg2"] == [0, 0, 0, 0]
    c.set_list()
    c.foo()
    assert get_last_log(t, c)["arg2"] == [1, 2, 3, 4]

```


Discussion:
Starting on this now.

Resolving patch:
diff --git a/vyper/parser/parser.py b/vyper/parser/parser.py
--- a/vyper/parser/parser.py
+++ b/vyper/parser/parser.py
@@ -733,7 +733,8 @@ def check_list_type_match(provided):  # Check list types match.
             for offset in range(0, size):
                 arg2 = LLLnode.from_list(['sload', ['add', ['sha3_32', Expr(arg, context).lll_node], offset]],
                                          typ=typ)
-                holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32)))
+                p_holder = context.new_placeholder(BaseType(32)) if offset > 0 else placeholder
+                holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, p_holder)
         # List from variable.
         elif isinstance(arg, ast.Name):
             size = context.vars[arg.id].size
@@ -742,7 +743,8 @@ def check_list_type_match(provided):  # Check list types match.
             for i in range(0, size):
                 offset = 32 * i
                 arg2 = LLLnode.from_list(pos + offset, typ=typ, location='memory')
-                holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32)))
+                p_holder = context.new_placeholder(BaseType(32)) if i > 0 else placeholder
+                holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, p_holder)
         # is list literal.
         else:
             holder, maxlen = pack_args_by_32(holder, maxlen, arg.elts[0], typ, context, placeholder)
@@ -759,8 +761,12 @@ def pack_logging_data(expected_data, args, context):
         return ['seq'], 0, None, 0
     holder = ['seq']
     maxlen = len(args) * 32  # total size of all packed args (upper limit)
-    dynamic_offset_counter = context.new_placeholder(BaseType(32))
-    dynamic_placeholder = context.new_placeholder(BaseType(32))
+    requires_dynamic_offset = any([isinstance(data.typ, ByteArrayType) for data in expected_data])
+    if requires_dynamic_offset:
+        dynamic_offset_counter = context.new_placeholder(BaseType(32))
+        dynamic_placeholder = context.new_placeholder(BaseType(32))
+    else:
+        dynamic_offset_counter = None
 
     # Populate static placeholders.
     placeholder_map = {}
@@ -772,7 +778,8 @@ def pack_logging_data(expected_data, args, context):
             holder, maxlen = pack_args_by_32(holder, maxlen, arg, typ, context, placeholder)
 
     # Dynamic position starts right after the static args.
-    holder.append(LLLnode.from_list(['mstore', dynamic_offset_counter, maxlen]))
+    if requires_dynamic_offset:
+        holder.append(LLLnode.from_list(['mstore', dynamic_offset_counter, maxlen]))
 
     # Calculate maximum dynamic offset placeholders, used for gas estimation.
     for i, (arg, data) in enumerate(zip(args, expected_data)):
@@ -780,25 +787,25 @@ def pack_logging_data(expected_data, args, context):
         if isinstance(typ, ByteArrayType):
             maxlen += 32 + ceil32(typ.maxlen)
 
-    # Obtain the start of the arg section.
-    if isinstance(expected_data[0].typ, ListType):
-        datamem_start = holder[1].to_list()[1][0]
-    else:
+    if requires_dynamic_offset:
         datamem_start = dynamic_placeholder + 32
+    else:
+        datamem_start = placeholder_map[0]
 
     # Copy necessary data into allocated dynamic section.
     for i, (arg, data) in enumerate(zip(args, expected_data)):
         typ = data.typ
-        pack_args_by_32(
-            holder=holder,
-            maxlen=maxlen,
-            arg=arg,
-            typ=typ,
-            context=context,
-            placeholder=placeholder_map[i],
-            datamem_start=datamem_start,
-            dynamic_offset_counter=dynamic_offset_counter
-        )
+        if isinstance(typ, ByteArrayType):
+            pack_args_by_32(
+                holder=holder,
+                maxlen=maxlen,
+                arg=arg,
+                typ=typ,
+                context=context,
+                placeholder=placeholder_map[i],
+                datamem_start=datamem_start,
+                dynamic_offset_counter=dynamic_offset_counter
+            )
 
     return holder, maxlen, dynamic_offset_counter, datamem_start
 


Test patch:
diff --git a/tests/parser/features/test_logging.py b/tests/parser/features/test_logging.py
--- a/tests/parser/features/test_logging.py
+++ b/tests/parser/features/test_logging.py
@@ -679,3 +679,79 @@ def foo(inp: bytes[33]):
     log.Bar(inp)
 """
     assert_tx_failed(lambda: get_contract_with_gas_estimation(code), TypeMismatchException)
+
+
+def test_2nd_var_list_packing(t, get_last_log, get_contract_with_gas_estimation, chain):
+    t.s = chain
+    code = """
+Bar: event({arg1: int128, arg2: int128[4]})
+
+@public
+def foo():
+    a: int128[4] = [1, 2, 3, 4]
+    log.Bar(10, a)
+    """
+    c = get_contract_with_gas_estimation(code)
+
+    c.foo()
+    assert get_last_log(t, c)["arg2"] == [1, 2, 3, 4]
+
+
+def test_2nd_var_storage_list_packing(t, get_last_log, get_contract_with_gas_estimation, chain):
+    t.s = chain
+    code = """
+Bar: event({arg1: int128, arg2: int128[4]})
+x: int128[4]
+
+@public
+def foo():
+    log.Bar(10, self.x)
+
+@public
+def set_list():
+    self.x = [1, 2, 3, 4]
+    """
+    c = get_contract_with_gas_estimation(code)
+
+    c.foo()
+    assert get_last_log(t, c)["arg2"] == [0, 0, 0, 0]
+    c.set_list()
+    c.foo()
+    assert get_last_log(t, c)["arg2"] == [1, 2, 3, 4]
+
+
+def test_mixed_var_list_packing(t, get_last_log, get_contract_with_gas_estimation, chain):
+    t.s = chain
+    code = """
+Bar: event({arg1: int128, arg2: int128[4], arg3 :bytes[4], arg4: int128[3], arg5: int128[2]})
+x: int128[4]
+y: int128[2]
+
+@public
+def __init__():
+    self.y = [1024, 2048]
+
+@public
+def foo():
+    v: int128[3] = [7, 8, 9]
+    log.Bar(10, self.x, "test", v, self.y)
+
+@public
+def set_list():
+    self.x = [1, 2, 3, 4]
+    """
+    c = get_contract_with_gas_estimation(code)
+
+    c.foo()
+    log = get_last_log(t, c)
+    assert log["arg2"] == [0, 0, 0, 0]
+    assert log["arg3"] == b"test"
+    assert log["arg4"] == [7, 8, 9]
+    assert log["arg5"] == [1024, 2048]
+    c.set_list()
+    c.foo()
+    log = get_last_log(t, c)
+    assert log["arg2"] == [1, 2, 3, 4]
+    assert log["arg3"] == b"test"
+    assert log["arg4"] == [7, 8, 9]
+    assert log["arg5"] == [1024, 2048]