diff --git a/.dockerignore b/.dockerignore index 8d9d590..e2e9861 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,6 +6,7 @@ npm-debug.log /pages /docs /.vscode +/preloaders # OS Files diff --git a/.vscode/settings.json b/.vscode/settings.json index 13aa77d..aa49e41 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,8 @@ { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": { - "source.fixAll": true, - "source.organizeImports": true + "source.fixAll": "explicit", + "source.organizeImports": "explicit" }, "[terraform]": { "editor.defaultFormatter": "hashicorp.terraform" diff --git a/assets/applications-1.json b/assets/applications-1.json new file mode 100644 index 0000000..b9d17e7 --- /dev/null +++ b/assets/applications-1.json @@ -0,0 +1 @@ +{"height":18958008,"hash":"0xd723ba51f35ad8e2e868972669b14f179d7ff451f08687d64d86972f7340f6a7","top":[],"addresses":{"0x7122cd1221c20892234186facfe8615e6743ab02":["0x0974cc873df893b302f6be7ecf4f9d4b1a15c366"]}} \ No newline at end of file diff --git a/assets/applications-11155111.json b/assets/applications-11155111.json new file mode 100644 index 0000000..7a2a12b --- /dev/null +++ b/assets/applications-11155111.json @@ -0,0 +1 @@ +{"height":5031033,"hash":"0x60b57b9528dec06fcf6b7038bf7c24e61588234a421be558bcb1c5c7df66a04c","top":[],"addresses":{"0x7122cd1221c20892234186facfe8615e6743ab02":["0x70ac08179605af2d9e75782b8decdd3c22aa4d0c","0x71ab24ee3ddb97dc01a161edf64c8d51102b0cd3","0x3694c82fde031b8462e90e8bfee0377de2b01ecc","0x3adf2a4ba065d4e128e1bdbfd91f489b07877d89","0x7bd3565af78d8457c81ff8b4870a174fa3783eb0","0xb35a7a3d7c1854e0a1196e3c706c4bb5c2b1657c","0xfa18d9738d906813fe81aade5bb2739207162305","0x184436275e033a8d1939849eadb5e744ade197c7","0x331094ebf8ba94790b1f0194291bef4e297b3466","0xc3af72d3c8e8aaa62535c6d5edfe4bd1d594a811","0xf9f432badf8514944b7a22c6ebd9775ef1d10494","0x3f19c60b0d818e0b843c10349111f64e5d5b18d5","0x74608454c71165ac0f2e503d47a00092ff0a48a3","0x87e73a1517e8144e37764192c544896eb4da7a57","0xeca14c01b1eb97dbd5c2e654f0e0f9693f580d42","0x9c3276bbeeee65e07c20acb262e4673dd4bcbce3","0x6fbe45a6b9e32092e2d813133c3904dfc4a92cc4","0x7c92af1ad96a80325d35ad4e505918f1923cf438","0x184b770bb161dcaade158a7347cc084a0c044801","0x674cb14ea404348b3b87c6e629f862014a72b6e9","0x53efed7c0a9e28c93201c71f0fa061ac9384407c","0x9f12d4365806fc000d6555acb85c5371b464e506","0xf4cb3c246203025dcfaf221deb1fff2bb90f2b20","0xb663c083fe2259f0514cf53a832308c1ae31986a","0x59b7b9328f06d5bc2843e8f3aec4a8d5e5419518","0x113dff0952688de6d9a09b2cdd58a5ba3d10d395","0xe5bdcc71da2c1df734cc560d33a8b4036372c27a","0x4ca2f6935200b9a782a78f408f640f17b29809d8","0xbef6f348e398b0fc81a20ecae268cee79ebf0053","0xa0f84501eaf5c85efdba0e44da026239023e531d","0xc6f515b07a8996aa78358ebe48a280cf4eabbed4","0x76e42e81f892ffac5ae7017b4764c8bba26536e1","0x7d7bc8237574fc74f7c6e0577db5526465e428bb","0xfb6de8730ff4a51332233d80c6abb38beed9e586","0x5b8647cd40278ff6504b8ad008f1fe468bcba2ae","0x9a6a74d1ca0b5219b0bea2988d4a9f309ce64a80","0x4906e2fc1383d5e70defb62e74ae7274b535fc65","0x5818d91e461d3120d525464f677ee17455e5c83c","0xd5e518c5bcd79d91fae9911ad8e2e31d95491975","0x7329704939e953242f1dbef51d790b773063601b","0x85280983a0e41533dfa4f8a7853645e8f08ff92a","0xcfcbedb8ceeb3ab21818039d274b59c774549597","0x40ecf0b914d1292bbf9390b80d701cee7c083ff7","0x9fbcd682e86f87420cf9b5f3c8c815f262b54ff4","0xf50ed28a165d368bdfb4a3e31c383917b0eadfea","0x0be010fa7e70d74fa8b6729fe1ae268787298f54","0x393d5ed52d3516ecd9442ca08f735143dee7e1ca","0xde1476c525805af79023fb55eb30ef0e01def6dc","0xe2510c5470fedd84e6fbff29c647b0c883dbc496","0x028367fe226cd9e5699f4288d512fe3a4a4a0012","0x56708c2cd2ee23ac169541013adea91e25e4eab9","0xa7a1fc4971435358718d5e48ddc38482410234b6","0x5874625fa6228bb63d2efdb2657b7f103380e403","0xe495186fde78d3c725079da3859b42da74ebf623","0xa8b4c251feff511d74c314adc893169880919227","0x26bf772117844cbaa3bcef7d038308f59adc92f4","0x87db74dc8f36d1cada0ee7cde5beb898dc41dd12","0x87e5bf4abe7f656a99cd9534badee10605212d24","0xc78fa4e18eb3c79805da64c387192ff0b1089372","0x5e6ed55cc045c5f1d95532edca7dafbcfe7435b4","0x634a235880bcc25844b43d0089b33eac063ada5a","0xc75e497eb836c010d7831696a83867384d46f49a","0x20840b831add95b40bb91b800292293fa8f58906","0xf8d68cfca57096ef665f3f5cb8d311b1fe9b3779","0xba06a9685aac487b9d820fa16ace3682d9999e64","0xc2a25a24304ac9b296657fbdc8dc79f2069e9de2","0xa433773b65ad045d666e9e9b31a32c747bf36bb2","0xa1c977656f68e1ee2733ff43b83529af2a5ae7c9","0xc0bf2492b753c10eb3c7f584f8f5c667e1e5a3f5","0x3950109fa34151b033bbc2990cb5f432f8cabf76","0x0c0a1646e383c3a6adb09299759904c52372a69b","0x1faea00e6859dfbbc80ea6fc768f1ea340d181bc","0x04c9faf00aff05a85f9cf5ee5ede7f6b627a0bad","0xc65d551a0b6c9ba65f07b4a93c930910af2e9b17","0xbe5a16b5e24d144920075a099880c48b89aa9213","0xd85323aeab29047cb22e9cb31967a25c5a26af8c","0x9e147b5a6d118c7ac17d20112c2c81116418d6e8","0xb896cbfac31d07cd6073b28973dfcde66e3d90b3","0x6a77f3cfc57dffb8dc82be3249aa5a6b979626ac","0xac795db158acb80fb14cebfa1167539f93ff0f05","0x211ebaf8002ca3fb894343bab4fdb5ae8fc03ad5","0x82a31cfdf65a9b793416f02735b96cdc8a4a88dd","0x9b81da10ed590aad669122fa8481f4659ae6cfe5","0x19927cb5ce72677297314c70f38416466cf6c781","0x75d26ee81e6642c4ed5df10b03c6649fd3f7629b","0xb76fddfdfa6a0f00213e5ea8491e103729eb1add","0xfdc52064fc8077bc48ad307bae41aebb050a58f2","0xdb84080e7d2b4654a7e384de851a6cf7281643de","0xd3fb544b9ce4725733e1b1e4114ce228b624099f","0x2dc155cb107b0ebe621b927f9e7acc4819e79a0d","0xf9f5c74acf5a20f1e91aeb057fd7f13db305a0e6","0x9b37f2d97fc0e46145a0d011898de40fe1704c3b","0x0e16cc94ed11d3aff0115ce07a1ac3f57e08b67b","0x2871b7d22693ca922eb497b945d84fdd9c786aae","0xd08e09d072ee853b8a6f331d94e0c15ee2d3d8ed","0xfd142fb8e80a95f25ff5e75960781649bb858e39","0xe70f662d7d47acb03c7d733e23af1534eed82733","0xc3f1384c2250075004a731140e481bf1a5b8e0d8","0x51ca152db75570bd7df12f99891129468802c0be","0xbc5ef46af65b986e83609d0ab2c386b4e8f110c0","0x131bfd89a99361fe2bd7078d7e83303f59a818f0","0xa946c3fbd53adc29e56ba32b1a289c135434bff4","0x6e1986c7d405884e5d13031d33ed981b160bac50","0xdd1f9b83507327f29c2c1bb42011fad5fb482dc6"]}} \ No newline at end of file diff --git a/codegen-squid-schema.yaml b/codegen-squid-schema.yaml deleted file mode 100644 index 6eb6e05..0000000 --- a/codegen-squid-schema.yaml +++ /dev/null @@ -1,6 +0,0 @@ -generates: - ./graphql/squid/schema.graphql: - schema: - - http://localhost:4350/graphql - plugins: - - schema-ast diff --git a/codegen.yml b/codegen.yml deleted file mode 100644 index e95320a..0000000 --- a/codegen.yml +++ /dev/null @@ -1,19 +0,0 @@ -generates: - ./ui/generated/graphql/squid/index.ts: - schema: - - ./graphql/squid/schema.graphql - documents: - - ./graphql/squid/rollups.graphql - plugins: - - typescript - - typescript-operations - - typescript-urql - ./ui/generated/graphql/rollups/1.0/index.ts: - schema: - - ./graphql/rollups/1.0/schema.graphql - documents: - - ./graphql/rollups/1.0/queries.graphql - plugins: - - typescript - - typescript-operations - - typescript-urql diff --git a/commands.json b/commands.json index 1391c3a..24c5090 100644 --- a/commands.json +++ b/commands.json @@ -117,6 +117,10 @@ "open": { "description": "Open a local browser window", "cmd": ["npx", "--yes", "opener"] + }, + "preload:apps": { + "description": "Preload the address of applications created by CartesiDAppFactory", + "cmd": ["npx", "--yes", "ts-node", "./preloaders/applicationLoader"] } } } diff --git a/package-lock.json b/package-lock.json index 4b10a1b..d8fec77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@cartesi/rollups": "1.0.0", "@subsquid/archive-registry": "^3.2.0", "@subsquid/evm-processor": "^1.4.0", + "@subsquid/file-store": "^2.0.0", "@subsquid/graphql-server": "^4.2.0", "@subsquid/logger": "^1.3.0", "@subsquid/typeorm-migration": "^1.2.0", @@ -2512,6 +2513,24 @@ "node": ">=16" } }, + "node_modules/@subsquid/file-store": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@subsquid/file-store/-/file-store-2.0.0.tgz", + "integrity": "sha512-LlBz8BVF0LxjOs320VygUwRKY7lUdv3UV2e6nhbKurCUyBHvTSJMmVT07DHkAB/RNTqGA5NvSSZnYTEM22y0qA==", + "dependencies": { + "@subsquid/logger": "~1.3.1", + "@subsquid/util-internal": "^2.5.0", + "upath": "^2.0.1" + }, + "peerDependencies": { + "@subsquid/util-internal-processor-tools": "^2.0.0" + } + }, + "node_modules/@subsquid/file-store/node_modules/@subsquid/util-internal": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-2.5.2.tgz", + "integrity": "sha512-N7lfZdWEkM35jG5wdGYx25TJKGGLMOx9VInSeRhW9T/3BEmHAuSWI2mIIYnZ8w5L041V8HGo61ijWF6qsXvZjg==" + }, "node_modules/@subsquid/graphiql-console": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@subsquid/graphiql-console/-/graphiql-console-0.3.0.tgz", @@ -2624,12 +2643,12 @@ } }, "node_modules/@subsquid/logger": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@subsquid/logger/-/logger-1.3.0.tgz", - "integrity": "sha512-1aCaFzGXrzW4bioiv6hPX+vsKO2EsiqFKcWedRW9G+0nnelAfhw5lGsyCvMzfkuk2xUCRA6vWwOReVK3BPW0Zg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@subsquid/logger/-/logger-1.3.2.tgz", + "integrity": "sha512-5YPdeRu0WD9iBak+r3S8blBeg2uyliPUlmCjj/ZE6dpXmXsSN9T+ZYrNeDr9eAbRV7OXVI49lSfCuAGenFcYjQ==", "dependencies": { - "@subsquid/util-internal-hex": "^1.2.0", - "@subsquid/util-internal-json": "^1.2.0", + "@subsquid/util-internal-hex": "^1.2.2", + "@subsquid/util-internal-json": "^1.2.2", "supports-color": "^8.1.1" } }, @@ -2838,9 +2857,9 @@ "integrity": "sha512-/Eeu9d3gW50nFSp49PDYrioIJdczR+wPlXBCe8EIMTUFEySIhhxFPB7y4JqL2pnUrK2UqAgyKoTx0diqP6fdxA==" }, "node_modules/@subsquid/util-internal-hex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal-hex/-/util-internal-hex-1.2.0.tgz", - "integrity": "sha512-5Xf1Zp52gQlkNTeWFLGaf5ChXNCrOcz/nMYtylYASnfjEWXX8XutdDrUpz/YVQH88MP8CiQ/xkKprXAE9csqfg==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal-hex/-/util-internal-hex-1.2.2.tgz", + "integrity": "sha512-E43HVqf23jP5hvtWF9GsiN8luANjnJ1daR2SVTwaIUAYU/uNjv1Bi6tHz2uexlflBhyxAgBDmHgunXZ45wQTIw==" }, "node_modules/@subsquid/util-internal-http-server": { "version": "1.2.0", @@ -2851,11 +2870,11 @@ } }, "node_modules/@subsquid/util-internal-json": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal-json/-/util-internal-json-1.2.0.tgz", - "integrity": "sha512-r9DlJWs6GFF4uS486QTeBmmrMAFqUdSgOmx/HfxkOfEO/iSVaoh2n8FKKw0uQyCVmnsDvoz8v6COOovL4blZWA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal-json/-/util-internal-json-1.2.2.tgz", + "integrity": "sha512-+axQnlkIzscdy0T/vR1Dez/2NVCryWgB3OocMGJcx2GjhHVkmuJSLFqOdk9o90OocfQFC57NTZx22oa2yd+4Yw==", "dependencies": { - "@subsquid/util-internal-hex": "^1.2.0" + "@subsquid/util-internal-hex": "^1.2.2" } }, "node_modules/@subsquid/util-internal-processor-tools": { @@ -12800,9 +12819,9 @@ "dev": true }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -13544,6 +13563,15 @@ "node": ">=8" } }, + "node_modules/upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, "node_modules/url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", @@ -17579,6 +17607,23 @@ } } }, + "@subsquid/file-store": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@subsquid/file-store/-/file-store-2.0.0.tgz", + "integrity": "sha512-LlBz8BVF0LxjOs320VygUwRKY7lUdv3UV2e6nhbKurCUyBHvTSJMmVT07DHkAB/RNTqGA5NvSSZnYTEM22y0qA==", + "requires": { + "@subsquid/logger": "~1.3.1", + "@subsquid/util-internal": "^2.5.0", + "upath": "^2.0.1" + }, + "dependencies": { + "@subsquid/util-internal": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal/-/util-internal-2.5.2.tgz", + "integrity": "sha512-N7lfZdWEkM35jG5wdGYx25TJKGGLMOx9VInSeRhW9T/3BEmHAuSWI2mIIYnZ8w5L041V8HGo61ijWF6qsXvZjg==" + } + } + }, "@subsquid/graphiql-console": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@subsquid/graphiql-console/-/graphiql-console-0.3.0.tgz", @@ -17660,12 +17705,12 @@ } }, "@subsquid/logger": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@subsquid/logger/-/logger-1.3.0.tgz", - "integrity": "sha512-1aCaFzGXrzW4bioiv6hPX+vsKO2EsiqFKcWedRW9G+0nnelAfhw5lGsyCvMzfkuk2xUCRA6vWwOReVK3BPW0Zg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@subsquid/logger/-/logger-1.3.2.tgz", + "integrity": "sha512-5YPdeRu0WD9iBak+r3S8blBeg2uyliPUlmCjj/ZE6dpXmXsSN9T+ZYrNeDr9eAbRV7OXVI49lSfCuAGenFcYjQ==", "requires": { - "@subsquid/util-internal-hex": "^1.2.0", - "@subsquid/util-internal-json": "^1.2.0", + "@subsquid/util-internal-hex": "^1.2.2", + "@subsquid/util-internal-json": "^1.2.2", "supports-color": "^8.1.1" } }, @@ -17838,9 +17883,9 @@ "integrity": "sha512-/Eeu9d3gW50nFSp49PDYrioIJdczR+wPlXBCe8EIMTUFEySIhhxFPB7y4JqL2pnUrK2UqAgyKoTx0diqP6fdxA==" }, "@subsquid/util-internal-hex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal-hex/-/util-internal-hex-1.2.0.tgz", - "integrity": "sha512-5Xf1Zp52gQlkNTeWFLGaf5ChXNCrOcz/nMYtylYASnfjEWXX8XutdDrUpz/YVQH88MP8CiQ/xkKprXAE9csqfg==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal-hex/-/util-internal-hex-1.2.2.tgz", + "integrity": "sha512-E43HVqf23jP5hvtWF9GsiN8luANjnJ1daR2SVTwaIUAYU/uNjv1Bi6tHz2uexlflBhyxAgBDmHgunXZ45wQTIw==" }, "@subsquid/util-internal-http-server": { "version": "1.2.0", @@ -17851,11 +17896,11 @@ } }, "@subsquid/util-internal-json": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@subsquid/util-internal-json/-/util-internal-json-1.2.0.tgz", - "integrity": "sha512-r9DlJWs6GFF4uS486QTeBmmrMAFqUdSgOmx/HfxkOfEO/iSVaoh2n8FKKw0uQyCVmnsDvoz8v6COOovL4blZWA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@subsquid/util-internal-json/-/util-internal-json-1.2.2.tgz", + "integrity": "sha512-+axQnlkIzscdy0T/vR1Dez/2NVCryWgB3OocMGJcx2GjhHVkmuJSLFqOdk9o90OocfQFC57NTZx22oa2yd+4Yw==", "requires": { - "@subsquid/util-internal-hex": "^1.2.0" + "@subsquid/util-internal-hex": "^1.2.2" } }, "@subsquid/util-internal-processor-tools": { @@ -25507,9 +25552,9 @@ "dev": true }, "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", @@ -25987,6 +26032,11 @@ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, + "upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==" + }, "url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", diff --git a/package.json b/package.json index 911ef00..8857287 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,9 @@ "sqd:deploy": "sqd deploy -o cartesi -m", "sqd:deploy:mainnet": "run-s \"sqd:deploy -- {@} .\" -- \"squid-mainnet.yaml\"", "sqd:deploy:sepolia": "run-s \"sqd:deploy -- {@} .\" -- \"squid-sepolia.yaml\"", + "preload:apps": "run-p preload:apps:mainnet preload:apps:sepolia", + "preload:apps:mainnet": "CHAIN_ID=1 sqd preload:apps", + "preload:apps:sepolia": "CHAIN_ID=11155111 sqd preload:apps", "tsc": "tsc", "process:prod": "node deploy/run --npmScriptName=sqd:process:prod", "serve:prod": "node deploy/run --npmScriptName=sqd:graphql:prod" @@ -31,6 +34,7 @@ "@cartesi/rollups": "1.0.0", "@subsquid/archive-registry": "^3.2.0", "@subsquid/evm-processor": "^1.4.0", + "@subsquid/file-store": "^2.0.0", "@subsquid/graphql-server": "^4.2.0", "@subsquid/logger": "^1.3.0", "@subsquid/typeorm-migration": "^1.2.0", diff --git a/preloaders/ApplicationLoader.ts b/preloaders/ApplicationLoader.ts new file mode 100644 index 0000000..69b7cb2 --- /dev/null +++ b/preloaders/ApplicationLoader.ts @@ -0,0 +1,110 @@ +import { EvmBatchProcessor } from '@subsquid/evm-processor'; +import { Database, LocalDest } from '@subsquid/file-store'; +import { createLogger } from '@subsquid/logger'; +import { + events as CartesiDAppFactory, + events, +} from '../src/abi/CartesiDAppFactory'; +import { CartesiDAppFactoryAddress, getConfig } from '../src/config'; + +type Metadata = { + height: number; + hash: string; + addresses: Record; +}; + +const logger = createLogger('sqd:preloader:application'); + +if (!process.env.CHAIN_ID) { + logger.error( + 'Looks like the CHAIN_ID environment is not set. The supported chain ids are [1, 11155111]', + ); + throw new Error('ChainId is required to preload the application addresses'); +} + +const chainId = parseInt(process.env.CHAIN_ID ?? 0); +const config = getConfig(chainId); + +logger.info(`Processing chain_id: ${chainId}`); + +const processor = new EvmBatchProcessor() + .setDataSource(config.dataSource) + .useArchiveOnly(true) + .setFinalityConfirmation(config.finalityConfirmation ?? 10) + .setFields({ + log: { + topics: true, + }, + }) + .setBlockRange({ + from: config.from, + }) + .addLog({ + address: [CartesiDAppFactoryAddress], + topic0: [CartesiDAppFactory.ApplicationCreated.topic], + }); + +const appFilename = `applications-${chainId}.json` as const; + +let applications: string[] = []; +let isInit = false; + +const database = new Database({ + tables: {}, + dest: new LocalDest('./assets'), + chunkSizeMb: Infinity, + hooks: { + async onStateRead(dest) { + if (await dest.exists(appFilename)) { + let { height, hash, addresses }: Metadata = await dest + .readFile(appFilename) + .then(JSON.parse); + + if (!isInit) { + applications = addresses[CartesiDAppFactoryAddress]; + isInit = true; + } + + return { height, hash }; + } else { + return undefined; + } + }, + async onStateUpdate(dest, info) { + let metadata: Metadata = { + ...info, + addresses: { + [CartesiDAppFactoryAddress]: applications, + }, + }; + + await dest.writeFile(appFilename, JSON.stringify(metadata)); + }, + }, +}); + +processor.run(database, async (ctx) => { + for (const block of ctx.blocks) { + for (const log of block.logs) { + if ( + log.address === CartesiDAppFactoryAddress && + log.topics[0] === events.ApplicationCreated.topic + ) { + const { application } = events.ApplicationCreated.decode(log); + const id = application.toLowerCase(); + + applications.push(id); + ctx.log.info(`${id} (Application) preloaded`); + } + } + } + + ctx.store.setForceFlush(true); + + if (ctx.isHead) { + ctx.log.info( + `Block header for chain-id ${chainId} reached. Finishing preloader`, + ); + process.exit(); + } +}); diff --git a/preloaders/README.md b/preloaders/README.md new file mode 100644 index 0000000..6cf41f9 --- /dev/null +++ b/preloaders/README.md @@ -0,0 +1,7 @@ +# Preloaders + +It holds processor scripts to preload information about data of interest to speed up the indexing process (usually when dealing with factory contracts). + +### Periodicity + +The rule of thumb is to keep an eye on the preload height and the current head block; when it becomes large enough to be unacceptable, it is time to run and upgrade the content. diff --git a/src/handlers/EventHandler.ts b/src/handlers/EventHandler.ts index 0622ea6..ac39c24 100644 --- a/src/handlers/EventHandler.ts +++ b/src/handlers/EventHandler.ts @@ -4,7 +4,9 @@ import { Application, ApplicationFactory, Erc20Deposit, + Erc721Deposit, Input, + NFT, Token, } from '../model'; import ApplicationCreated from './ApplicationCreated'; @@ -18,6 +20,8 @@ export default class EventHandler { private readonly inputs: Map; private readonly applications: Map; private readonly factories: Map; + private readonly nfts: Map; + private readonly erc721Deposits: Map; private readonly applicationCreated: Handler; private readonly inputAdded: Handler; private readonly ownershipTransferred: Handler; @@ -28,6 +32,8 @@ export default class EventHandler { this.inputs = new Map(); this.applications = new Map(); this.factories = new Map(); + this.nfts = new Map(); + this.erc721Deposits = new Map(); this.applicationCreated = new ApplicationCreated( this.factories, this.applications, @@ -38,6 +44,8 @@ export default class EventHandler { this.deposits, this.applications, this.inputs, + this.nfts, + this.erc721Deposits, ); this.ownershipTransferred = new OwnershipTransferred(this.applications); @@ -57,6 +65,8 @@ export default class EventHandler { factories: this.factories, deposits: this.deposits, inputs: this.inputs, + nfts: this.nfts, + erc721Deposits: this.erc721Deposits, }; } } diff --git a/src/handlers/InputAdded.ts b/src/handlers/InputAdded.ts index 2ce5ec2..1dc314e 100644 --- a/src/handlers/InputAdded.ts +++ b/src/handlers/InputAdded.ts @@ -1,6 +1,6 @@ import { BlockData, DataHandlerContext, Log } from '@subsquid/evm-processor'; import { Store } from '@subsquid/typeorm-store'; -import { dataSlice, getNumber, getUint } from 'ethers'; +import { dataSlice, getUint } from 'ethers'; import { Contract as ERC20 } from '../abi/ERC20'; import { Contract as ERC721 } from '../abi/ERC721'; import { events } from '../abi/InputBox'; @@ -27,10 +27,12 @@ const logErrorAndReturnNull = export default class InputAdded implements Handler { constructor( - private tokenStorage: Map, - private depositStorage: Map, + private tokenStorage: Map, + private depositStorage: Map, private applicationStorage: Map, private inputStorage: Map, + private nftStorage: Map, + private erc721DepositStorage: Map, ) {} private async prepareErc20Deposit( @@ -43,7 +45,7 @@ export default class InputAdded implements Handler { ) { if (input.msgSender !== ERC20PortalAddress) return undefined; - const success = getNumber(dataSlice(input.payload, 0, 1)) == 1; // 1 byte for boolean (not used?) + // first byte is a boolean and it is not used here at the moment const tokenAddress = dataSlice(input.payload, 1, 21).toLowerCase(); // 20 bytes for address const from = dataSlice(input.payload, 21, 41).toLowerCase(); // 20 bytes for address const amount = getUint(dataSlice(input.payload, 41, 73)); // 32 bytes for uint256 @@ -85,7 +87,7 @@ export default class InputAdded implements Handler { const from = dataSlice(input.payload, 20, 40).toLowerCase(); // 20 bytes for address const tokenIndex = getUint(dataSlice(input.payload, 40, 72)); // 32 bytes for uint256 - let nft = this.tokenStorage.get(tokenAddress) as NFT; + let nft = this.nftStorage.get(tokenAddress); if (!nft) { const contract = new ERC721(ctx, block.header, tokenAddress); const name = await contract @@ -95,7 +97,7 @@ export default class InputAdded implements Handler { .symbol() .catch(logErrorAndReturnNull(ctx)); nft = new NFT({ id: tokenAddress, name, symbol }); - this.tokenStorage.set(tokenAddress, nft); + this.nftStorage.set(tokenAddress, nft); ctx.log.info(`${tokenAddress} (NFT) stored`); } @@ -106,7 +108,7 @@ export default class InputAdded implements Handler { tokenIndex, }); - this.depositStorage.set(opts.inputId, deposit); + this.erc721DepositStorage.set(opts.inputId, deposit); ctx.log.info(`${opts.inputId} (Erc721Deposit) stored`); return deposit; diff --git a/src/main.ts b/src/main.ts index 9848a93..a7f05a7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -27,15 +27,24 @@ processor.run(new TypeormDatabase({ supportHotBlocks: true }), async (ctx) => { } } - const { tokens, applications, factories, deposits, inputs } = - eventHandler.getValues(); + const { + tokens, + applications, + factories, + deposits, + inputs, + nfts, + erc721Deposits, + } = eventHandler.getValues(); const total = tokens.size + applications.size + factories.size + deposits.size + - inputs.size; + inputs.size + + nfts.size + + erc721Deposits.size; if (total > 0) { const summary = Object.entries({ @@ -44,6 +53,8 @@ processor.run(new TypeormDatabase({ supportHotBlocks: true }), async (ctx) => { factories: factories.size, deposits: deposits.size, inputs: inputs.size, + nfts: nfts.size, + erc721Deposits: erc721Deposits.size, }) .map(([entity, count]) => `${entity}: ${count}`) .join(', '); @@ -51,8 +62,10 @@ processor.run(new TypeormDatabase({ supportHotBlocks: true }), async (ctx) => { } await ctx.store.upsert([...tokens.values()]); + await ctx.store.upsert([...nfts.values()]); await ctx.store.upsert([...factories.values()]); await ctx.store.upsert([...applications.values()]); await ctx.store.upsert([...deposits.values()]); + await ctx.store.upsert([...erc721Deposits.values()]); await ctx.store.upsert([...inputs.values()]); }); diff --git a/src/processor.ts b/src/processor.ts index 18108d3..d404185 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -14,6 +14,7 @@ import { InputBoxAddress, getConfig, } from './config'; +import { loadApplications } from './utils'; export type NetworkConfig = { archive: string; @@ -21,8 +22,9 @@ export type NetworkConfig = { }; export const createProcessor = (chainId: number): EvmBatchProcessor => { + const applicationMetadata = loadApplications(chainId); const config = getConfig(chainId); - const processor = new EvmBatchProcessor() + let processor = new EvmBatchProcessor() .setDataSource(config.dataSource) .setFinalityConfirmation(config.finalityConfirmation ?? 10) .setFields({ @@ -44,11 +46,29 @@ export const createProcessor = (chainId: number): EvmBatchProcessor => { address: [InputBoxAddress], topic0: [InputBox.InputAdded.topic], transaction: true, - }) - .addLog({ + }); + + if (applicationMetadata !== null) { + processor = processor + .addLog({ + address: + applicationMetadata.addresses[CartesiDAppFactoryAddress], + topic0: [CartesiDApp.OwnershipTransferred.topic], + range: { from: config.from, to: applicationMetadata.height }, + transaction: true, + }) + .addLog({ + topic0: [CartesiDApp.OwnershipTransferred.topic], + range: { from: applicationMetadata.height + 1 }, + transaction: true, + }); + } else { + processor = processor.addLog({ topic0: [CartesiDApp.OwnershipTransferred.topic], transaction: true, }); + } + return processor; }; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..db133ad --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,21 @@ +import { existsSync, readFileSync } from 'node:fs'; + +type ApplicationMetadata = { + height: number; + addresses: Record; +}; + +/** + * Load application metadata structure containing block height and the list of + * application addresses from specified CartesiDAppFactory address. + * @param chainId + * @returns {ApplicationMetadata | null} + */ +export function loadApplications(chainId: number): ApplicationMetadata | null { + const filePath = `./assets/applications-${chainId}.json`; + + if (!existsSync(filePath)) return null; + + const file = readFileSync(filePath, 'utf-8'); + return JSON.parse(file) as ApplicationMetadata; +} diff --git a/tests/handlers/InputAdded.test.ts b/tests/handlers/InputAdded.test.ts index df85823..9df1f43 100644 --- a/tests/handlers/InputAdded.test.ts +++ b/tests/handlers/InputAdded.test.ts @@ -55,6 +55,8 @@ describe('InputAdded', () => { const mockDepositStorage = new Map(); const mockInputStorage = new Map(); const mockApplicationStorage = new Map(); + const mockNftStorage = new Map(); + const mockErc721DepositStorage = new Map(); beforeEach(() => { inputAdded = new InputAdded( @@ -62,11 +64,16 @@ describe('InputAdded', () => { mockDepositStorage, mockApplicationStorage, mockInputStorage, + mockNftStorage, + mockErc721DepositStorage, ); + mockTokenStorage.clear(); mockDepositStorage.clear(); mockApplicationStorage.clear(); mockInputStorage.clear(); + mockNftStorage.clear(); + mockErc721DepositStorage.clear(); }); afterEach(() => { @@ -209,8 +216,8 @@ describe('InputAdded', () => { test('should store the token information', async () => { await inputAdded.handle(logErc721Transfer, block, ctx); - expect(mockTokenStorage.size).toBe(1); - const token = mockTokenStorage.values().next().value; + expect(mockNftStorage.size).toBe(1); + const token = mockNftStorage.values().next().value; expect(token.name).toEqual(name); expect(token.symbol).toEqual(symbol); }); @@ -218,8 +225,8 @@ describe('InputAdded', () => { test('should store the deposit information', async () => { await inputAdded.handle(logErc721Transfer, block, ctx); - expect(mockDepositStorage.size).toBe(1); - const deposit = mockDepositStorage.values().next().value; + expect(mockErc721DepositStorage.size).toBe(1); + const deposit = mockErc721DepositStorage.values().next().value; expect(deposit.id).toEqual( '0x0be010fa7e70d74fa8b6729fe1ae268787298f54-1', ); diff --git a/tests/processor.test.ts b/tests/processor.test.ts index bea62af..a3c0fa9 100644 --- a/tests/processor.test.ts +++ b/tests/processor.test.ts @@ -1,6 +1,8 @@ import { afterEach } from 'node:test'; import { MockInstance, beforeEach, describe, expect, test, vi } from 'vitest'; +import { CartesiDAppFactoryAddress } from '../src/config'; import { createProcessor } from '../src/processor'; +import { loadApplications } from '../src/utils'; vi.mock('@subsquid/evm-processor', async () => { const actualMods = await vi.importActual('@subsquid/evm-processor'); @@ -45,6 +47,7 @@ describe('Processor creation', () => { test('Required configs for sepolia', () => { const processor = createProcessor(sepolia); + const applicationMetadata = loadApplications(sepolia); expect(processor.setDataSource).toHaveBeenCalledWith({ archive: 'https://v2.archive.subsquid.io/network/ethereum-sepolia', @@ -66,7 +69,7 @@ describe('Processor creation', () => { const addLog = processor.addLog as unknown as MockInstance; - expect(addLog).toHaveBeenCalledTimes(3); + expect(addLog).toHaveBeenCalledTimes(4); expect(addLog.mock.calls[0][0]).toEqual({ address: ['0x7122cd1221c20892234186facfe8615e6743ab02'], topic0: [ @@ -82,9 +85,22 @@ describe('Processor creation', () => { }); expect(addLog.mock.calls[2][0]).toEqual({ + address: applicationMetadata?.addresses[CartesiDAppFactoryAddress], topic0: [ '0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0', ], + range: { + from: expect.any(Number), + to: applicationMetadata?.height, + }, + transaction: true, + }); + + expect(addLog.mock.calls[3][0]).toEqual({ + topic0: [ + '0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0', + ], + range: { from: applicationMetadata?.height! + 1 }, transaction: true, }); }); @@ -136,6 +152,7 @@ describe('Processor creation', () => { test('Required configs for mainnet', () => { const processor = createProcessor(mainnet); + const applicationMetadata = loadApplications(mainnet); expect(processor.setDataSource).toHaveBeenCalledWith({ archive: 'https://v2.archive.subsquid.io/network/ethereum-mainnet', @@ -157,7 +174,7 @@ describe('Processor creation', () => { const addLog = processor.addLog as unknown as MockInstance; - expect(addLog).toHaveBeenCalledTimes(3); + expect(addLog).toHaveBeenCalledTimes(4); expect(addLog.mock.calls[0][0]).toEqual({ address: ['0x7122cd1221c20892234186facfe8615e6743ab02'], topic0: [ @@ -173,9 +190,24 @@ describe('Processor creation', () => { }); expect(addLog.mock.calls[2][0]).toEqual({ + address: applicationMetadata?.addresses[CartesiDAppFactoryAddress], topic0: [ '0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0', ], + range: { + from: expect.any(Number), + to: applicationMetadata?.height, + }, + transaction: true, + }); + + expect(addLog.mock.calls[3][0]).toEqual({ + topic0: [ + '0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0', + ], + range: { + from: applicationMetadata?.height! + 1, + }, transaction: true, }); }); diff --git a/tsconfig.json b/tsconfig.json index 5717545..e3ad7ba 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,5 +14,5 @@ "resolveJsonModule": true }, "include": ["src"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "preloaders"] }