From b0e51e2b4d69f215a50b5fdfdf92c41040424b5f Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 9 Dec 2025 13:24:24 -0500 Subject: [PATCH 1/6] updates --- macros/streamline/configs.yaml.sql | 14 ++ macros/streamline/functions.py.sql | 349 +++++++++++++++++++++++++++++ 2 files changed, 363 insertions(+) diff --git a/macros/streamline/configs.yaml.sql b/macros/streamline/configs.yaml.sql index 97c8340..47ef2b7 100644 --- a/macros/streamline/configs.yaml.sql +++ b/macros/streamline/configs.yaml.sql @@ -293,5 +293,19 @@ sql: | {{ fsc_utils.create_udf_stablecoin_data_parse() | indent(4) }} +- name: {{ schema }}.udf_encode_contract_call + signature: + - [function_abi, VARIANT] + - [input_values, ARRAY] + return_type: STRING + options: | + LANGUAGE PYTHON + RUNTIME_VERSION = '3.10' + PACKAGES = ('eth-abi') + HANDLER = 'encode_call' + COMMENT = '{{ fsc_utils.udf_encode_contract_call_comment() }}' + sql: | + {{ fsc_utils.create_udf_encode_contract_call() | indent(4) }} + {% endmacro %} diff --git a/macros/streamline/functions.py.sql b/macros/streamline/functions.py.sql index ebd6c2f..8b92064 100644 --- a/macros/streamline/functions.py.sql +++ b/macros/streamline/functions.py.sql @@ -784,4 +784,353 @@ class udf_stablecoin_data_parse: except Exception as error: raise Exception(f'Error parsing peggedData content: {str(error)}') +{% endmacro %} + +{% macro create_udf_encode_contract_call() %} + +def encode_call(function_abi, input_values): + """ + Encodes EVM contract function calls into ABI-encoded calldata. + + This function generates complete calldata (selector + encoded params) that can be + used directly in eth_call JSON-RPC requests to query contract state. + """ + import eth_abi + from eth_hash.auto import keccak + import json + + def get_function_signature(abi): + """ + Generate function signature using the same logic as utils.udf_evm_text_signature. + + Examples: + balanceOf(address) + transfer(address,uint256) + swap((address,address,uint256)) + """ + def generate_signature(inputs): + signature_parts = [] + for input_data in inputs: + if 'components' in input_data: + # Handle nested tuples + component_signature_parts = [] + components = input_data['components'] + component_signature_parts.extend(generate_signature(components)) + component_signature_parts[-1] = component_signature_parts[-1].rstrip(",") + if input_data['type'].endswith('[]'): + signature_parts.append("(" + "".join(component_signature_parts) + ")[],") + else: + signature_parts.append("(" + "".join(component_signature_parts) + "),") + else: + # Clean up Solidity-specific modifiers + signature_parts.append(input_data['type'].replace('enum ', '').replace(' payable', '') + ",") + return signature_parts + + signature_parts = [abi['name'] + "("] + signature_parts.extend(generate_signature(abi.get('inputs', []))) + if len(signature_parts) > 1: + signature_parts[-1] = signature_parts[-1].rstrip(",") + ")" + else: + signature_parts.append(")") + return "".join(signature_parts) + + def function_selector(abi): + """Calculate 4-byte function selector using Keccak256 hash.""" + signature = get_function_signature(abi) + hash_bytes = keccak(signature.encode('utf-8')) + return hash_bytes[:4].hex(), signature + + def get_canonical_type(input_spec): + """ + Convert ABI input spec to canonical type string for eth_abi encoding. + + Handles tuple expansion: tuple -> (address,uint256,bytes) + """ + param_type = input_spec['type'] + + if param_type.startswith('tuple'): + components = input_spec.get('components', []) + component_types = ','.join([get_canonical_type(comp) for comp in components]) + canonical = f"({component_types})" + + # Preserve array suffixes: tuple[] -> (address,uint256)[] + if param_type.endswith('[]'): + array_suffix = param_type[5:] # Everything after 'tuple' + canonical += array_suffix + + return canonical + + return param_type + + def prepare_value(value, param_type, components=None): + """ + Convert Snowflake values to Python types suitable for eth_abi encoding. + + Handles type coercion and format normalization for all Solidity types. + """ + # Handle null/None values with sensible defaults + if value is None: + if param_type.startswith('uint') or param_type.startswith('int'): + return 0 + elif param_type == 'address': + return '0x' + '0' * 40 + elif param_type == 'bool': + return False + elif param_type.startswith('bytes'): + return b'' + else: + return value + + # CRITICAL: Check arrays FIRST (before base types) + # This prevents bytes[] from matching the bytes check + if param_type.endswith('[]'): + base_type = param_type[:-2] + if not isinstance(value, list): + return [] + + # Special handling for tuple arrays + if base_type == 'tuple' and components: + return [prepare_tuple(v, components) for v in value] + else: + return [prepare_value(v, base_type) for v in value] + + # Base type conversions + if param_type == 'address': + addr = str(value).lower() + if not addr.startswith('0x'): + addr = '0x' + addr + return addr + + if param_type.startswith('uint') or param_type.startswith('int'): + return int(value) + + if param_type == 'bool': + if isinstance(value, str): + return value.lower() in ('true', '1', 'yes') + return bool(value) + + if param_type.startswith('bytes'): + if isinstance(value, str): + if value.startswith('0x'): + value = value[2:] + return bytes.fromhex(value) + return value + + if param_type == 'string': + return str(value) + + return value + + def prepare_tuple(value, components): + """ + Recursively prepare tuple values, handling nested structures. + + Tuples can contain other tuples, arrays, or tuple arrays. + """ + if not isinstance(value, (list, tuple)): + # Support dict-style input (by component name) + if isinstance(value, dict): + value = [value.get(comp.get('name', f'field_{i}')) + for i, comp in enumerate(components)] + else: + return value + + result = [] + for i, comp in enumerate(components): + if i >= len(value): + result.append(None) + continue + + comp_type = comp['type'] + val = value[i] + + # Handle tuple arrays within tuples + if comp_type.endswith('[]') and comp_type.startswith('tuple'): + sub_components = comp.get('components', []) + result.append(prepare_value(val, comp_type, sub_components)) + elif comp_type.startswith('tuple'): + # Single tuple (not array) + sub_components = comp.get('components', []) + result.append(prepare_tuple(val, sub_components)) + else: + result.append(prepare_value(val, comp_type)) + + return tuple(result) + + try: + inputs = function_abi.get('inputs', []) + + # Calculate selector using battle-tested signature generation + selector_hex, signature = function_selector(function_abi) + + # Functions with no inputs only need the selector + if not inputs: + return '0x' + selector_hex + + # Prepare values for encoding + prepared_values = [] + for i, inp in enumerate(inputs): + if i >= len(input_values): + prepared_values.append(None) + continue + + value = input_values[i] + param_type = inp['type'] + + # Handle tuple arrays at top level + if param_type.endswith('[]') and param_type.startswith('tuple'): + components = inp.get('components', []) + prepared_values.append(prepare_value(value, param_type, components)) + elif param_type.startswith('tuple'): + # Single tuple (not array) + components = inp.get('components', []) + prepared_values.append(prepare_tuple(value, components)) + else: + prepared_values.append(prepare_value(value, param_type)) + + # Get canonical type strings for eth_abi (expands tuples) + types = [get_canonical_type(inp) for inp in inputs] + + # Encode parameters using eth_abi + encoded_params = eth_abi.encode(types, prepared_values).hex() + + # Return complete calldata: selector + encoded params + return '0x' + selector_hex + encoded_params + + except Exception as e: + # Return structured error for debugging + import traceback + return json.dumps({ + 'error': str(e), + 'traceback': traceback.format_exc(), + 'function': function_abi.get('name', 'unknown'), + 'signature': signature if 'signature' in locals() else 'not computed', + 'selector': '0x' + selector_hex if 'selector_hex' in locals() else 'not computed', + 'types': types if 'types' in locals() else 'not computed' + }) + +{% endmacro %} + +{% macro udf_encode_contract_call_comment() %} +Encodes EVM contract function calls into hex calldata format for eth_call RPC requests. + +PURPOSE: + Converts human-readable function parameters into ABI-encoded calldata that can be sent + to Ethereum nodes via JSON-RPC. Handles all Solidity types including complex nested + structures like tuples and arrays. + +PARAMETERS: + function_abi (VARIANT): + - JSON object containing the function ABI definition + - Must include: "name" (string) and "inputs" (array of input definitions) + - Each input needs: "name", "type", and optionally "components" for tuples + + input_values (ARRAY): + - Array of values matching the function inputs in order + - Values should be provided as native Snowflake types: + * addresses: strings (with or without 0x prefix) + * uint/int: numbers + * bool: booleans + * bytes/bytes32: hex strings (with or without 0x prefix) + * arrays: Snowflake arrays + * tuples: Snowflake arrays in component order + +RETURNS: + STRING: Complete calldata as hex string with 0x prefix + - Format: 0x{4-byte selector}{encoded parameters} + - Can be used directly in eth_call RPC requests + - Returns JSON error object if encoding fails + +EXAMPLES: + + -- Simple function with no inputs + SELECT crosschain_dev.utils.udf_encode_contract_call( + PARSE_JSON(''{"name": "totalSupply", "inputs": []}''), + ARRAY_CONSTRUCT() + ); + -- Returns: 0x18160ddd + + -- Function with single address parameter + SELECT crosschain_dev.utils.udf_encode_contract_call( + PARSE_JSON(''{ + "name": "balanceOf", + "inputs": [{"name": "account", "type": "address"}] + }''), + ARRAY_CONSTRUCT(''0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'') + ); + -- Returns: 0x70a08231000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 + + -- Function with multiple parameters + SELECT crosschain_dev.utils.udf_encode_contract_call( + PARSE_JSON(''{ + "name": "transfer", + "inputs": [ + {"name": "to", "type": "address"}, + {"name": "amount", "type": "uint256"} + ] + }''), + ARRAY_CONSTRUCT(''0x1234567890123456789012345678901234567890'', 1000000) + ); + + -- Complex function with nested tuples + SELECT crosschain_dev.utils.udf_encode_contract_call( + PARSE_JSON(''{ + "name": "swap", + "inputs": [{ + "name": "params", + "type": "tuple", + "components": [ + {"name": "tokenIn", "type": "address"}, + {"name": "tokenOut", "type": "address"}, + {"name": "amountIn", "type": "uint256"} + ] + }] + }''), + ARRAY_CONSTRUCT( + ARRAY_CONSTRUCT( + ''0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'', + ''0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'', + 1000000 + ) + ) + ); + +TYPICAL WORKFLOW: + 1. Get function ABI from crosschain.evm.dim_contract_abis + 2. Prepare input values as Snowflake arrays + 3. Encode using this function + 4. Execute via eth_call RPC (ai.live.udf_api) + 5. Decode response using streamline_dev.utils.udf_evm_decode_trace + +SUPPORTED TYPES: + - address: Ethereum addresses + - uint8, uint16, ..., uint256: Unsigned integers + - int8, int16, ..., int256: Signed integers + - bool: Boolean values + - bytes, bytes1, ..., bytes32: Fixed and dynamic byte arrays + - string: Dynamic strings + - Arrays: Any type followed by [] + - Tuples: Nested structures with components + - Nested combinations: tuple[], tuple[][], etc. + +NOTES: + - Function selector is automatically calculated using Keccak256 + - Compatible with existing utils.udf_evm_text_signature and utils.udf_keccak256 + - Handles gas-optimized function names (e.g., selector 0x00000000) + - Tuples must be provided as arrays in component order + - Empty arrays are valid for array-type parameters + +ERROR HANDLING: + - Returns JSON error object on failure + - Check if result starts with "{" to detect errors + - Error object includes: error message, traceback, function name, types + +RELATED FUNCTIONS: + - utils.udf_evm_text_signature: Generate function signature + - utils.udf_keccak256: Calculate function selector + - streamline_dev.utils.udf_evm_decode_trace: Decode call results + +VERSION: 1.0 +AUTHOR: Flipside Crypto Data Engineering +LAST UPDATED: 2024-12-09 {% endmacro %} \ No newline at end of file From 1fd04663114b03f566b9f339357a5dd6752bf35f Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 9 Dec 2025 13:28:53 -0500 Subject: [PATCH 2/6] comment --- macros/streamline/functions.py.sql | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/macros/streamline/functions.py.sql b/macros/streamline/functions.py.sql index 8b92064..03a9e89 100644 --- a/macros/streamline/functions.py.sql +++ b/macros/streamline/functions.py.sql @@ -1128,9 +1128,6 @@ ERROR HANDLING: RELATED FUNCTIONS: - utils.udf_evm_text_signature: Generate function signature - utils.udf_keccak256: Calculate function selector - - streamline_dev.utils.udf_evm_decode_trace: Decode call results + - utils.udf_evm_decode_trace: Decode call results -VERSION: 1.0 -AUTHOR: Flipside Crypto Data Engineering -LAST UPDATED: 2024-12-09 {% endmacro %} \ No newline at end of file From 6981b8b42dabb83ecdd9192669ab7d75f545b5ff Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 9 Dec 2025 13:31:48 -0500 Subject: [PATCH 3/6] db --- macros/streamline/functions.py.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/macros/streamline/functions.py.sql b/macros/streamline/functions.py.sql index 03a9e89..3c148c6 100644 --- a/macros/streamline/functions.py.sql +++ b/macros/streamline/functions.py.sql @@ -1044,14 +1044,14 @@ RETURNS: EXAMPLES: -- Simple function with no inputs - SELECT crosschain_dev.utils.udf_encode_contract_call( + SELECT utils.udf_encode_contract_call( PARSE_JSON(''{"name": "totalSupply", "inputs": []}''), ARRAY_CONSTRUCT() ); -- Returns: 0x18160ddd -- Function with single address parameter - SELECT crosschain_dev.utils.udf_encode_contract_call( + SELECT utils.udf_encode_contract_call( PARSE_JSON(''{ "name": "balanceOf", "inputs": [{"name": "account", "type": "address"}] @@ -1061,7 +1061,7 @@ EXAMPLES: -- Returns: 0x70a08231000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 -- Function with multiple parameters - SELECT crosschain_dev.utils.udf_encode_contract_call( + SELECT utils.udf_encode_contract_call( PARSE_JSON(''{ "name": "transfer", "inputs": [ @@ -1073,7 +1073,7 @@ EXAMPLES: ); -- Complex function with nested tuples - SELECT crosschain_dev.utils.udf_encode_contract_call( + SELECT utils.udf_encode_contract_call( PARSE_JSON(''{ "name": "swap", "inputs": [{ From 027f73276cc68781e0fd209b5e6e9c7a6d8217d9 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 9 Dec 2025 13:36:48 -0500 Subject: [PATCH 4/6] dbs --- macros/streamline/functions.py.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/streamline/functions.py.sql b/macros/streamline/functions.py.sql index 3c148c6..769e9d2 100644 --- a/macros/streamline/functions.py.sql +++ b/macros/streamline/functions.py.sql @@ -1100,7 +1100,7 @@ TYPICAL WORKFLOW: 2. Prepare input values as Snowflake arrays 3. Encode using this function 4. Execute via eth_call RPC (ai.live.udf_api) - 5. Decode response using streamline_dev.utils.udf_evm_decode_trace + 5. Decode response using utils.udf_evm_decode_trace SUPPORTED TYPES: - address: Ethereum addresses From 70d5fc1c3ef2afc649589720e98b0b6414ca108b Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 10 Dec 2025 12:58:54 -0500 Subject: [PATCH 5/6] updates --- macros/streamline/configs.yaml.sql | 86 ++++++++++++++++++++++ macros/streamline/functions.py.sql | 111 ++++++++++++++++++++++++++++- packages.yml | 4 -- 3 files changed, 196 insertions(+), 5 deletions(-) diff --git a/macros/streamline/configs.yaml.sql b/macros/streamline/configs.yaml.sql index 47ef2b7..d46b6b4 100644 --- a/macros/streamline/configs.yaml.sql +++ b/macros/streamline/configs.yaml.sql @@ -307,5 +307,91 @@ sql: | {{ fsc_utils.create_udf_encode_contract_call() | indent(4) }} +- name: {{ schema }}.udf_create_eth_call + signature: + - [contract_address, STRING] + - [encoded_calldata, STRING] + return_type: OBJECT + options: | + NULL + LANGUAGE SQL + STRICT IMMUTABLE + COMMENT = 'Creates an eth_call JSON-RPC request object with default block parameter "latest".' + sql: | + {{ schema }}.udf_json_rpc_call( + 'eth_call', + ARRAY_CONSTRUCT( + OBJECT_CONSTRUCT( + 'to', contract_address, + 'data', encoded_calldata + ), + 'latest' + ) + ) + +- name: {{ schema }}.udf_create_eth_call + signature: + - [contract_address, STRING] + - [encoded_calldata, STRING] + - [block_parameter, VARIANT] + return_type: OBJECT + options: | + NULL + LANGUAGE SQL + STRICT IMMUTABLE + COMMENT = 'Creates an eth_call JSON-RPC request object. Accepts contract address, encoded calldata, and optional block parameter (string or number). If block_parameter is a number, it will be converted to hex format using ai.utils.udf_int_to_hex.' + sql: | + {{ schema }}.udf_json_rpc_call( + 'eth_call', + ARRAY_CONSTRUCT( + OBJECT_CONSTRUCT( + 'to', contract_address, + 'data', encoded_calldata + ), + CASE + WHEN block_parameter IS NULL THEN 'latest' + WHEN TYPEOF(block_parameter) IN ('INTEGER', 'NUMBER', 'FIXED', 'FLOAT') THEN + {{ schema }}.udf_int_to_hex(block_parameter::NUMBER) + ELSE block_parameter::STRING + END + ) + ) + +- name: {{ schema }}.udf_create_eth_call_from_abi + signature: + - [contract_address, STRING] + - [function_abi, VARIANT] + - [input_values, ARRAY] + return_type: OBJECT + options: | + NULL + LANGUAGE SQL + STRICT IMMUTABLE + COMMENT = '{{ fsc_utils.udf_create_eth_call_from_abi_comment() }}' + sql: | + {{ schema }}.udf_create_eth_call( + contract_address, + {{ schema }}.udf_encode_contract_call(function_abi, input_values) + ) + +- name: {{ schema }}.udf_create_eth_call_from_abi + signature: + - [contract_address, STRING] + - [function_abi, VARIANT] + - [input_values, ARRAY] + - [block_parameter, VARIANT] + return_type: OBJECT + options: | + NULL + LANGUAGE SQL + STRICT IMMUTABLE + COMMENT = '{{ fsc_utils.udf_create_eth_call_from_abi_comment() }}' + sql: | + {{ schema }}.udf_create_eth_call( + contract_address, + {{ schema }}.udf_encode_contract_call(function_abi, input_values), + block_parameter + ) + {% endmacro %} diff --git a/macros/streamline/functions.py.sql b/macros/streamline/functions.py.sql index 769e9d2..3c7378e 100644 --- a/macros/streamline/functions.py.sql +++ b/macros/streamline/functions.py.sql @@ -1099,7 +1099,7 @@ TYPICAL WORKFLOW: 1. Get function ABI from crosschain.evm.dim_contract_abis 2. Prepare input values as Snowflake arrays 3. Encode using this function - 4. Execute via eth_call RPC (ai.live.udf_api) + 4. Execute via eth_call RPC (live.udf_api) 5. Decode response using utils.udf_evm_decode_trace SUPPORTED TYPES: @@ -1130,4 +1130,113 @@ RELATED FUNCTIONS: - utils.udf_keccak256: Calculate function selector - utils.udf_evm_decode_trace: Decode call results +{% endmacro %} + +{% macro udf_create_eth_call_from_abi_comment() %} +Convenience function that combines contract call encoding and JSON-RPC request creation for eth_call. + +PURPOSE: + Simplifies the workflow of creating eth_call JSON-RPC requests by combining ABI encoding + and RPC call construction into a single function call. This is the recommended approach for + most use cases where you want to query contract state via eth_call. + +PARAMETERS: + contract_address (STRING): + - Ethereum contract address (with or without 0x prefix) + - Example: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' + + function_abi (VARIANT): + - JSON object containing the function ABI definition + - Must include: "name" (string) and "inputs" (array of input definitions) + - Each input needs: "name", "type", and optionally "components" for tuples + - Can be retrieved from tables like crosschain.evm.dim_contract_abis or ethereum.silver.flat_function_abis + + input_values (ARRAY): + - Array of values matching the function inputs in order + - Values should be provided as native Snowflake types: + * addresses: strings (with or without 0x prefix) + * uint/int: numbers + * bool: booleans + * bytes/bytes32: hex strings (with or without 0x prefix) + * arrays: Snowflake arrays + * tuples: Snowflake arrays in component order + + block_parameter (VARIANT, optional): + - Block identifier for the eth_call request + - If NULL or omitted: defaults to 'latest' + - If NUMBER: automatically converted to hex format (e.g., 18500000 -> '0x11a7f80') + - If STRING: used directly (e.g., 'latest', '0x11a7f80', 'pending') + +RETURNS: + OBJECT: Complete JSON-RPC request object ready for eth_call + - Format: {"jsonrpc": "2.0", "method": "eth_call", "params": [...], "id": "..."} + - Can be used directly with RPC execution functions like live.udf_api + +EXAMPLES: + + -- Simple balanceOf call with default 'latest' block + SELECT utils.udf_create_eth_call_from_abi( + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + PARSE_JSON('{ + "name": "balanceOf", + "inputs": [{"name": "account", "type": "address"}] + }'), + ARRAY_CONSTRUCT('0xBcca60bB61934080951369a648Fb03DF4F96263C') + ); + + -- Same call but at a specific block number + SELECT utils.udf_create_eth_call_from_abi( + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + PARSE_JSON('{ + "name": "balanceOf", + "inputs": [{"name": "account", "type": "address"}] + }'), + ARRAY_CONSTRUCT('0xBcca60bB61934080951369a648Fb03DF4F96263C'), + 18500000 + ); + + -- Using ABI from a table + WITH abi_data AS ( + SELECT + abi, + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as contract_address, + '0xBcca60bB61934080951369a648Fb03DF4F96263C' as user_address + FROM ethereum.silver.flat_function_abis + WHERE contract_address = LOWER('0x43506849D7C04F9138D1A2050bbF3A0c054402dd') + AND function_name = 'balanceOf' + ) + SELECT + utils.udf_create_eth_call_from_abi( + contract_address, + abi, + ARRAY_CONSTRUCT(user_address) + ) as rpc_call + FROM abi_data; + +TYPICAL WORKFLOW: + 1. Get function ABI from contract ABI tables (crosschain.evm.dim_contract_abis, etc.) + 2. Prepare input values as Snowflake arrays matching the function signature + 3. Call this function with contract address, ABI, and inputs + 4. Execute the returned RPC call object via live.udf_api or similar + 5. Decode the response using utils.udf_evm_decode_trace or similar decoder + +ADVANTAGES OVER MODULAR APPROACH: + - Single function call instead of two (encode + create) + - Cleaner, more readable SQL + - Better for AI systems (fewer steps to explain) + - Less error-prone (no intermediate variables) + - More intuitive function name + +WHEN TO USE MODULAR FUNCTIONS INSTEAD: + - When you need to reuse encoded calldata for multiple RPC calls + - When you need encoded calldata for transaction construction + - When building complex workflows with intermediate steps + +RELATED FUNCTIONS: + - utils.udf_encode_contract_call: Encode function calls to calldata (used internally) + - utils.udf_create_eth_call: Create RPC call from encoded calldata (used internally) + - utils.udf_evm_text_signature: Generate function signature from ABI + - utils.udf_keccak256: Calculate function selector hash + - utils.udf_evm_decode_trace: Decode eth_call response results + {% endmacro %} \ No newline at end of file diff --git a/packages.yml b/packages.yml index 3ddb98c..1ba733a 100644 --- a/packages.yml +++ b/packages.yml @@ -1,7 +1,3 @@ packages: - - package: calogica/dbt_expectations - version: [">=0.8.0", "<0.9.0"] - - package: dbt-labs/dbt_utils - version: [">=1.0.0", "<1.1.0"] - git: https://github.com/FlipsideCrypto/livequery-models.git revision: "v1.10.2" From d0e3f57772014398da2285a75362daf6823afdb5 Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 10 Dec 2025 13:15:16 -0500 Subject: [PATCH 6/6] format --- macros/streamline/functions.py.sql | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/macros/streamline/functions.py.sql b/macros/streamline/functions.py.sql index 3c7378e..9843275 100644 --- a/macros/streamline/functions.py.sql +++ b/macros/streamline/functions.py.sql @@ -1056,7 +1056,7 @@ EXAMPLES: "name": "balanceOf", "inputs": [{"name": "account", "type": "address"}] }''), - ARRAY_CONSTRUCT(''0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'') + ARRAY_CONSTRUCT(''0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'') ); -- Returns: 0x70a08231000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 @@ -1088,8 +1088,8 @@ EXAMPLES: }''), ARRAY_CONSTRUCT( ARRAY_CONSTRUCT( - ''0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'', - ''0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'', + ''0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'', + ''0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'', 1000000 ) ) @@ -1143,7 +1143,7 @@ PURPOSE: PARAMETERS: contract_address (STRING): - Ethereum contract address (with or without 0x prefix) - - Example: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' + - Example: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' function_abi (VARIANT): - JSON object containing the function ABI definition @@ -1176,22 +1176,22 @@ EXAMPLES: -- Simple balanceOf call with default 'latest' block SELECT utils.udf_create_eth_call_from_abi( - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', PARSE_JSON('{ "name": "balanceOf", "inputs": [{"name": "account", "type": "address"}] }'), - ARRAY_CONSTRUCT('0xBcca60bB61934080951369a648Fb03DF4F96263C') + ARRAY_CONSTRUCT('0xbcca60bb61934080951369a648fb03df4f96263c') ); -- Same call but at a specific block number SELECT utils.udf_create_eth_call_from_abi( - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', PARSE_JSON('{ "name": "balanceOf", "inputs": [{"name": "account", "type": "address"}] }'), - ARRAY_CONSTRUCT('0xBcca60bB61934080951369a648Fb03DF4F96263C'), + ARRAY_CONSTRUCT('0xbcca60bb61934080951369a648fb03df4f96263c'), 18500000 ); @@ -1199,10 +1199,10 @@ EXAMPLES: WITH abi_data AS ( SELECT abi, - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as contract_address, - '0xBcca60bB61934080951369a648Fb03DF4F96263C' as user_address + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' as contract_address, + '0xbcca60bb61934080951369a648fb03df4f96263c' as user_address FROM ethereum.silver.flat_function_abis - WHERE contract_address = LOWER('0x43506849D7C04F9138D1A2050bbF3A0c054402dd') + WHERE contract_address = LOWER('0x43506849d7c04f9138d1a2050bbf3a0c054402dd') AND function_name = 'balanceOf' ) SELECT