From 57a56bdfac34bc65614143aed5c13bbd265e7326 Mon Sep 17 00:00:00 2001 From: outsider-analytics <105951311+outsider-analytics@users.noreply.github.com> Date: Thu, 20 Apr 2023 14:00:01 -0600 Subject: [PATCH 1/4] Create PARSE_ABI_FUNCTIONS This function takes a dune_name and abi string and returns an array of each function in the abi. I use it within a decode contracts dbt model which creates a the information needed to create decoded views of contracts for dune compatibility. Differences are: 1. Adds the dune name 2. Maps the ABI types to BQ types 3. If there is no name for the inputs or outputs, the name is "input_number" or "output_number" --- sql/PARSE_ABI_FUNCTIONS | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 sql/PARSE_ABI_FUNCTIONS diff --git a/sql/PARSE_ABI_FUNCTIONS b/sql/PARSE_ABI_FUNCTIONS new file mode 100644 index 0000000..7277d40 --- /dev/null +++ b/sql/PARSE_ABI_FUNCTIONS @@ -0,0 +1,106 @@ +CREATE OR REPLACE FUNCTION `blocktrekker.udfs.PARSE_ABI_FUNCTIONS`(abi STRING, dune_name STRING) RETURNS ARRAY> LANGUAGE js +OPTIONS (library=["gs://blockchain-etl-bigquery/ethers.js"]) AS R""" +abi = JSON.parse(abi); + res = []; + const typeMap = { + "uint32[]": "INT64", + "uint16[]": "INT64", + "uint8[]": "INT64", + "uint64[]": "INT64", + "uint128[]": "INT64", + "uint256[]": "BIGNUMERIC", + "bool[]": "BOOL", + "address[]": "STRING", + "string[]": "STRING", + "bytes[]": "BYTES", + "bytes4": "BYTES", + "bytes32": "BYTES", + "uint32": "INT64", + "uint16": "INT64", + "uint8": "INT64", + "uint64": "INT64", + "unit80": "INT64", + "uint112": "INT64", + "uint128": "INT64", + "uint168": "BIGNUMERIC", + "uint256": "BIGNUMERIC", + "BIGNUMERIC": "BIGNUMERIC", + "bool": "BOOL", + "address": "STRING", + "STRING": "STRING", + "string": "STRING", + "bytes": "BYTES" + }; + + + const nameMap = { + "from": "from_address", + "to": "to_address", + "limit": "_limit", + "all": "_all" + }; + + abi.forEach(function(x){ + tuple = []; + tuple['constant'] = x.constant; + tuple['payable'] = x.payable; + if (x.type != 'function') { + return; + } + + argtypes = []; + argpairs = []; + let count = 1; + x.inputs.forEach(function(y){ + pair = {}; + argtypes.push(y.type); + if (y.name in nameMap) { + pair.name = nameMap[y.name]; + } else if (y.name == "") { + pair.name = `input_${count}`; + } else { + pair.name = y.name ? y.name : `input_${count}`; + } + if (y.type in typeMap) { + pair.type = typeMap[y.type]; + } else { + if (y.type.slice(0, 4).toLowerCase() === "uint") { + pair.type = "BIGNUMERIC"; + } else { + pair.type = "STRING"; + } + } + argpairs.push(JSON.stringify(pair)); + count = count + 1; + }); + + outpairs = []; + if (x.outputs) { + let count = 1; + x.outputs.forEach(function(y){ + pair = {}; + if (y.name in nameMap) { + pair.name = nameMap[y.name]; + } else if (y.name == "") { + pair.name = `output_${count}` + } else { + pair.name = y.name ? y.name : `output_${count}`; + } + if (y.type in typeMap) { + pair.type = typeMap[y.type]; + } else if (y.type.slice(0, 4).toLowerCase() === "uint") { + pair.type = "BIGNUMERIC"; + } else { + pair.type = "STRING"; + } + outpairs.push(JSON.stringify(pair)); + }); + } + tuple['inputs'] = argpairs.join(","); + tuple['outputs'] = outpairs.join(","); + tuple['hash_id'] = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(x.name + '(' + argtypes.join(',') + ')')).substr(0,10); + tuple['name'] = dune_name + "_call_" + x.name; + res.push(tuple); + }); + return res; +"""; From 366f77c17d1e367263108b075389307fef011e1a Mon Sep 17 00:00:00 2001 From: outsider-analytics <105951311+outsider-analytics@users.noreply.github.com> Date: Thu, 20 Apr 2023 14:02:31 -0600 Subject: [PATCH 2/4] Rename PARSE_ABI_FUNCTIONS to PARSE_ABI_FUNCTIONS_UDF.sql --- sql/{PARSE_ABI_FUNCTIONS => PARSE_ABI_FUNCTIONS_UDF.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sql/{PARSE_ABI_FUNCTIONS => PARSE_ABI_FUNCTIONS_UDF.sql} (100%) diff --git a/sql/PARSE_ABI_FUNCTIONS b/sql/PARSE_ABI_FUNCTIONS_UDF.sql similarity index 100% rename from sql/PARSE_ABI_FUNCTIONS rename to sql/PARSE_ABI_FUNCTIONS_UDF.sql From 3b9a262d93422ac8bea96e34895afb71caddac97 Mon Sep 17 00:00:00 2001 From: outsider-analytics <105951311+outsider-analytics@users.noreply.github.com> Date: Thu, 20 Apr 2023 14:04:31 -0600 Subject: [PATCH 3/4] Create PARSE_ABI_EVENTS_UDF.sql This function takes a dune_name and abi string and returns an array of each event in the abi. I use it within a decode contracts dbt model which creates a the information needed to create decoded views of contracts for dune compatibility. Differences are: 1. Adds the dune name 2. Maps the ABI types to BQ types 3. If there is no name for the inputs, the name is "input_number" --- sql/PARSE_ABI_EVENTS_UDF.sql | 81 ++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 sql/PARSE_ABI_EVENTS_UDF.sql diff --git a/sql/PARSE_ABI_EVENTS_UDF.sql b/sql/PARSE_ABI_EVENTS_UDF.sql new file mode 100644 index 0000000..1870fbd --- /dev/null +++ b/sql/PARSE_ABI_EVENTS_UDF.sql @@ -0,0 +1,81 @@ +CREATE OR REPLACE FUNCTION `blocktrekker.udfs.PARSE_ABI_EVENTS`(abi STRING, dune_name STRING) RETURNS ARRAY> LANGUAGE js +OPTIONS (library=["gs://blockchain-etl-bigquery/ethers.js"]) AS R""" +abi = JSON.parse(abi); + res = []; + const typeMap = { + "uint32[]": "INT64", + "uint16[]": "INT64", + "uint8[]": "INT64", + "uint64[]": "INT64", + "uint128[]": "INT64", + "uint256[]": "BIGNUMERIC", + "bool[]": "BOOL", + "address[]": "STRING", + "string[]": "STRING", + "bytes[]": "BYTES", + "bytes4": "BYTES", + "bytes32": "BYTES", + "uint32": "INT64", + "uint16": "INT64", + "uint8": "INT64", + "uint64": "INT64", + "unit80": "INT64", + "uint112": "INT64", + "uint128": "INT64", + "uint168": "BIGNUMERIC", + "uint256": "BIGNUMERIC", + "BIGNUMERIC": "BIGNUMERIC", + "bool": "BOOL", + "address": "STRING", + "STRING": "STRING", + "string": "STRING", + "bytes": "BYTES" + }; + + const nameMap = { + "from": "from_address", + "to": "to_address", + "limit": "_limit", + "all": "_all" + }; + + abi.forEach(function(x){ + tuple = []; + tuple['name'] = dune_name + "_evt_" + x.name; + tuple['anonymous'] = x.anonymous; + + if (x.type != 'event') { + return; + } + + argtypes = []; + argpairs = []; + let count = 1; + x.inputs.forEach(function(y){ + pair = {}; + argtypes.push(y.type); + if (y.name in nameMap) { + pair.name = nameMap[y.name]; + } else if (y.name == "") { + pair.name = `input_${count}`; + } else { + pair.name = y.name ? y.name : `input_${count}`; + } + if (y.type in typeMap) { + pair.type = typeMap[y.type]; + } else { + if (y.type.slice(0, 4).toLowerCase() === "uint") { + pair.type = "BIGNUMERIC"; + } else { + pair.type = "STRING"; + } + } + argpairs.push(JSON.stringify(pair)); + count = count + 1; + }); + tuple['inputs'] = argpairs.join(","); + tuple['hash_id'] = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(tuple['name'] + '(' + argtypes.join(',') + ')')); + res.push(tuple); + }); + return res; +"""; From 1b3c0143dd9fc83413fd59dd4fe6719afa8e3eb1 Mon Sep 17 00:00:00 2001 From: outsider-analytics <105951311+outsider-analytics@users.noreply.github.com> Date: Thu, 11 May 2023 12:30:48 -0600 Subject: [PATCH 4/4] Fix event hash name input --- sql/PARSE_ABI_EVENTS_UDF.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/PARSE_ABI_EVENTS_UDF.sql b/sql/PARSE_ABI_EVENTS_UDF.sql index 1870fbd..ce5ff4b 100644 --- a/sql/PARSE_ABI_EVENTS_UDF.sql +++ b/sql/PARSE_ABI_EVENTS_UDF.sql @@ -42,6 +42,7 @@ abi = JSON.parse(abi); abi.forEach(function(x){ tuple = []; tuple['name'] = dune_name + "_evt_" + x.name; + tuple['orig_name'] = x.name; tuple['anonymous'] = x.anonymous; if (x.type != 'event') { @@ -74,7 +75,7 @@ abi = JSON.parse(abi); count = count + 1; }); tuple['inputs'] = argpairs.join(","); - tuple['hash_id'] = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(tuple['name'] + '(' + argtypes.join(',') + ')')); + tuple['hash_id'] = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(tuple['orig_name'] + '(' + argtypes.join(',') + ')')); res.push(tuple); }); return res;