diff --git a/.cspell.json b/.cspell.json index e6c6f1fa0a..9bcb16b2b7 100644 --- a/.cspell.json +++ b/.cspell.json @@ -104,6 +104,7 @@ "reactjs", "recid", "rlnrelay", + "rlnv", "roadmap", "sandboxed", "scanf", @@ -132,7 +133,9 @@ "upgrader", "vacp", "varint", + "viem", "vkey", + "wagmi", "waku", "wakuconnect", "wakunode", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07654a4a3b..ec99cc2f40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,6 +116,28 @@ jobs: - run: npm install if: ${{ steps.release.outputs.releases_created }} + - name: Setup Foundry + if: ${{ steps.release.outputs.releases_created }} + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Generate RLN contract ABIs and verify build + if: ${{ steps.release.outputs.releases_created }} + run: | + cd packages/rln + npm run setup:contract-abi || { + echo "Failed to generate contract ABIs, marking @waku/rln as private to skip publishing" + node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); pkg.private = true; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));" + exit 0 + } + npm run build || { + echo "Failed to build @waku/rln, marking as private to skip publishing" + node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); pkg.private = true; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));" + exit 0 + } + cd ../.. + - run: npm run build if: ${{ steps.release.outputs.releases_created }} diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 659676f47e..5ccf41befe 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -25,6 +25,26 @@ jobs: - run: npm install + - name: Setup Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Generate RLN contract ABIs and verify build + run: | + cd packages/rln + npm run setup:contract-abi || { + echo "Failed to generate contract ABIs, marking @waku/rln as private to skip publishing" + node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); pkg.private = true; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));" + exit 0 + } + npm run build || { + echo "Failed to build @waku/rln, marking as private to skip publishing" + node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); pkg.private = true; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));" + exit 0 + } + cd ../.. + - run: npm run build - run: npm run publish -- --tag next diff --git a/.gitignore b/.gitignore index 1e6952cf2f..188172b1d3 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ packages/discovery/mock_local_storage CLAUDE.md .env postgres-data/ +packages/rln/waku-rlnv2-contract/ diff --git a/package-lock.json b/package-lock.json index 9bc9d3b4ed..912a4f0258 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,6 +52,12 @@ "wscat": "^6.0.1" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -2654,164 +2660,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@ethersproject/abi": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz", - "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/hash": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/abstract-provider": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz", - "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/networks": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "@ethersproject/web": "^5.8.0" - } - }, - "node_modules/@ethersproject/abstract-signer": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz", - "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0" - } - }, - "node_modules/@ethersproject/address": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz", - "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/rlp": "^5.8.0" - } - }, - "node_modules/@ethersproject/base64": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz", - "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0" - } - }, - "node_modules/@ethersproject/basex": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.8.0.tgz", - "integrity": "sha512-PIgTszMlDRmNwW9nhS6iqtVfdTAKosA7llYXNmGPw4YAI1PUyMv28988wAb41/gHF/WqGdoLv0erHaRcHRKW2Q==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/properties": "^5.8.0" - } - }, - "node_modules/@ethersproject/bignumber": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz", - "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "bn.js": "^5.2.1" - } - }, "node_modules/@ethersproject/bytes": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", @@ -2831,161 +2679,6 @@ "@ethersproject/logger": "^5.8.0" } }, - "node_modules/@ethersproject/constants": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz", - "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0" - } - }, - "node_modules/@ethersproject/contracts": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.8.0.tgz", - "integrity": "sha512-0eFjGz9GtuAi6MZwhb4uvUM216F38xiuR0yYCjKJpNfSEy4HUM8hvqqBj9Jmm0IUz8l0xKEhWwLIhPgxNY0yvQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abi": "^5.8.0", - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/transactions": "^5.8.0" - } - }, - "node_modules/@ethersproject/hash": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz", - "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/base64": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/hdnode": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.8.0.tgz", - "integrity": "sha512-4bK1VF6E83/3/Im0ERnnUeWOY3P1BZml4ZD3wcH8Ys0/d1h1xaFt6Zc+Dh9zXf9TapGro0T4wvO71UTCp3/uoA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/basex": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/pbkdf2": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/sha2": "^5.8.0", - "@ethersproject/signing-key": "^5.8.0", - "@ethersproject/strings": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "@ethersproject/wordlists": "^5.8.0" - } - }, - "node_modules/@ethersproject/json-wallets": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.8.0.tgz", - "integrity": "sha512-HxblNck8FVUtNxS3VTEYJAcwiKYsBIF77W15HufqlBF9gGfhmYOJtYZp8fSDZtn9y5EaXTE87zDwzxRoTFk11w==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/hdnode": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/pbkdf2": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/random": "^5.8.0", - "@ethersproject/strings": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "aes-js": "3.0.0", - "scrypt-js": "3.0.1" - } - }, - "node_modules/@ethersproject/keccak256": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz", - "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "js-sha3": "0.8.0" - } - }, "node_modules/@ethersproject/logger": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", @@ -3002,143 +2695,6 @@ ], "license": "MIT" }, - "node_modules/@ethersproject/networks": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz", - "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/pbkdf2": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.8.0.tgz", - "integrity": "sha512-wuHiv97BrzCmfEaPbUFpMjlVg/IDkZThp9Ri88BpjRleg4iePJaj2SW8AIyE8cXn5V1tuAaMj6lzvsGJkGWskg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/sha2": "^5.8.0" - } - }, - "node_modules/@ethersproject/properties": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", - "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/providers": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.8.0.tgz", - "integrity": "sha512-3Il3oTzEx3o6kzcg9ZzbE+oCZYyY+3Zh83sKkn4s1DZfTUjIegHnN2Cm0kbn9YFy45FDVcuCLLONhU7ny0SsCw==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/base64": "^5.8.0", - "@ethersproject/basex": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/hash": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/networks": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/random": "^5.8.0", - "@ethersproject/rlp": "^5.8.0", - "@ethersproject/sha2": "^5.8.0", - "@ethersproject/strings": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "@ethersproject/web": "^5.8.0", - "bech32": "1.1.4", - "ws": "8.18.0" - } - }, - "node_modules/@ethersproject/providers/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@ethersproject/random": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.8.0.tgz", - "integrity": "sha512-E4I5TDl7SVqyg4/kkA/qTfuLWAQGXmSOgYyO01So8hLfwgKvYK5snIlzxJMk72IFdG/7oh8yuSqY2KX7MMwg+A==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0" - } - }, "node_modules/@ethersproject/rlp": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz", @@ -3159,223 +2715,6 @@ "@ethersproject/logger": "^5.8.0" } }, - "node_modules/@ethersproject/sha2": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.8.0.tgz", - "integrity": "sha512-dDOUrXr9wF/YFltgTBYS0tKslPEKr6AekjqDW2dbn1L1xmjGR+9GiKu4ajxovnrDbwxAKdHjW8jNcwfz8PAz4A==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "hash.js": "1.1.7" - } - }, - "node_modules/@ethersproject/signing-key": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", - "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "bn.js": "^5.2.1", - "elliptic": "6.6.1", - "hash.js": "1.1.7" - } - }, - "node_modules/@ethersproject/solidity": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.8.0.tgz", - "integrity": "sha512-4CxFeCgmIWamOHwYN9d+QWGxye9qQLilpgTU0XhYs1OahkclF+ewO+3V1U0mvpiuQxm5EHHmv8f7ClVII8EHsA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/sha2": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/strings": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz", - "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/transactions": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz", - "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/rlp": "^5.8.0", - "@ethersproject/signing-key": "^5.8.0" - } - }, - "node_modules/@ethersproject/units": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.8.0.tgz", - "integrity": "sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/wallet": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.8.0.tgz", - "integrity": "sha512-G+jnzmgg6UxurVKRKvw27h0kvG75YKXZKdlLYmAHeF32TGUzHkOFd7Zn6QHOTYRFWnfjtSSFjBowKo7vfrXzPA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/hash": "^5.8.0", - "@ethersproject/hdnode": "^5.8.0", - "@ethersproject/json-wallets": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/random": "^5.8.0", - "@ethersproject/signing-key": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "@ethersproject/wordlists": "^5.8.0" - } - }, - "node_modules/@ethersproject/web": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz", - "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/base64": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/wordlists": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.8.0.tgz", - "integrity": "sha512-2df9bbXicZws2Sb5S6ET493uJ0Z84Fjr3pC4tu/qlnZERibZCeUVuqdtt+7Tv9xxhUxHoIekIA7avrKUWHrezg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/hash": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, "node_modules/@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", @@ -6810,7 +6149,7 @@ "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/qs": { @@ -6831,7 +6170,7 @@ "version": "18.3.24", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -7532,6 +6871,151 @@ "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==", "license": "MIT" }, + "node_modules/@wagmi/cli": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@wagmi/cli/-/cli-2.7.0.tgz", + "integrity": "sha512-M0FDVK2/mQSOJne3nG7GiZrecw069GYFY6YGQZbG9IyxPgfOHRgVBvGkeXzGXmb3ezFlzn5jCCIQ2q/9lYh07g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abitype": "^1.0.4", + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "change-case": "^5.4.4", + "chokidar": "4.0.1", + "dedent": "^0.7.0", + "dotenv": "^16.3.1", + "dotenv-expand": "^10.0.0", + "esbuild": "~0.25.4", + "escalade": "3.2.0", + "fdir": "^6.1.1", + "nanospinner": "1.2.2", + "pathe": "^1.1.2", + "picocolors": "^1.0.0", + "picomatch": "^3.0.0", + "prettier": "^3.0.3", + "viem": "2.x", + "zod": "^4.1.11" + }, + "bin": { + "wagmi": "dist/esm/cli.js" + }, + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@wagmi/cli/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@wagmi/cli/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/@wagmi/cli/node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@wagmi/cli/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@wagmi/cli/node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@wagmi/core": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.22.1.tgz", + "integrity": "sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==", + "license": "MIT", + "dependencies": { + "eventemitter3": "5.0.1", + "mipd": "0.0.7", + "zustand": "5.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "@tanstack/query-core": ">=5.0.0", + "typescript": ">=5.0.4", + "viem": "2.x" + }, + "peerDependenciesMeta": { + "@tanstack/query-core": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@wagmi/core/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/@waku/browser-tests": { "resolved": "packages/browser-tests", "link": true @@ -7840,6 +7324,27 @@ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "license": "BSD-2-Clause" }, + "node_modules/abitype": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.1.1.tgz", + "integrity": "sha512-Loe5/6tAgsBukY95eGaPSDmQHIjRZYQq8PB1MpsNccDIK8WiV+Uw6WzaIXipvaxTEL2yEB0OpEaQv3gs8pkS9Q==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -9467,12 +8972,6 @@ "integrity": "sha512-Xz4Nm9c+LiBHhDR5bDLnNzmj6+5F+cyEAWPMkbs2awq/dYazR/efelZzUAjB/y3kNHL+uzkHvxVVpaOfGCPV7A==", "license": "Apache-2.0" }, - "node_modules/aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", - "license": "MIT" - }, "node_modules/agent-base": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", @@ -10443,12 +9942,6 @@ "tweetnacl": "^0.14.3" } }, - "node_modules/bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", - "license": "MIT" - }, "node_modules/before-after-hook": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", @@ -10486,12 +9979,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bn.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", - "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", - "license": "MIT" - }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -10566,12 +10053,6 @@ "node": ">=8" } }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "license": "MIT" - }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -10658,6 +10139,22 @@ "node": ">=10.0.0" } }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, "node_modules/byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", @@ -10720,6 +10217,16 @@ } } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -11066,6 +10573,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -12310,7 +11824,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/custom-event": { @@ -12514,6 +12028,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true, + "license": "MIT" + }, "node_modules/deep-eql": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", @@ -13151,6 +12672,29 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -13451,27 +12995,6 @@ "node": ">=0.10.0" } }, - "node_modules/elliptic": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", - "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", - "license": "MIT", - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", - "license": "MIT" - }, "node_modules/email-addresses": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", @@ -15002,54 +14525,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/ethers": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.8.0.tgz", - "integrity": "sha512-DUq+7fHrCg1aPDFCHx6UIPb3nmt2XMpM7Y/g2gLhsl3lIBqeAfOJIl1qEvRf2uq3BiKxmh6Fh5pfp2ieyek7Kg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abi": "5.8.0", - "@ethersproject/abstract-provider": "5.8.0", - "@ethersproject/abstract-signer": "5.8.0", - "@ethersproject/address": "5.8.0", - "@ethersproject/base64": "5.8.0", - "@ethersproject/basex": "5.8.0", - "@ethersproject/bignumber": "5.8.0", - "@ethersproject/bytes": "5.8.0", - "@ethersproject/constants": "5.8.0", - "@ethersproject/contracts": "5.8.0", - "@ethersproject/hash": "5.8.0", - "@ethersproject/hdnode": "5.8.0", - "@ethersproject/json-wallets": "5.8.0", - "@ethersproject/keccak256": "5.8.0", - "@ethersproject/logger": "5.8.0", - "@ethersproject/networks": "5.8.0", - "@ethersproject/pbkdf2": "5.8.0", - "@ethersproject/properties": "5.8.0", - "@ethersproject/providers": "5.8.0", - "@ethersproject/random": "5.8.0", - "@ethersproject/rlp": "5.8.0", - "@ethersproject/sha2": "5.8.0", - "@ethersproject/signing-key": "5.8.0", - "@ethersproject/solidity": "5.8.0", - "@ethersproject/strings": "5.8.0", - "@ethersproject/transactions": "5.8.0", - "@ethersproject/units": "5.8.0", - "@ethersproject/wallet": "5.8.0", - "@ethersproject/web": "5.8.0", - "@ethersproject/wordlists": "5.8.0" - } - }, "node_modules/event-iterator": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/event-iterator/-/event-iterator-2.0.0.tgz", @@ -16550,16 +16025,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -16639,17 +16104,6 @@ "node": "*" } }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "license": "MIT", - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -17891,6 +17345,21 @@ "whatwg-fetch": "^3.4.1" } }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -18383,9 +17852,9 @@ } }, "node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.9.3.tgz", + "integrity": "sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg==", "license": "MIT" }, "node_modules/js-tokens": { @@ -19603,6 +19072,16 @@ "node": ">=4" } }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -21156,18 +20635,6 @@ "node": ">=4" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "license": "MIT" - }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -21234,6 +20701,26 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mipd": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mipd/-/mipd-0.0.7.tgz", + "integrity": "sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "license": "MIT", + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -25572,6 +25059,57 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ox": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.9.6.tgz", + "integrity": "sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.0.9", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ox/node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/p-cancelable": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", @@ -26233,6 +25771,13 @@ "inherits": "2.0.3" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -26959,7 +26504,6 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -29102,12 +28646,6 @@ "dev": true, "license": "MIT" }, - "node_modules/scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", - "license": "MIT" - }, "node_modules/semantic-release": { "version": "24.2.5", "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.5.tgz", @@ -31876,6 +31414,26 @@ "devOptional": true, "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -32965,6 +32523,93 @@ "node": ">= 0.8" } }, + "node_modules/viem": { + "version": "2.38.4", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.38.4.tgz", + "integrity": "sha512-qnyPNg6Lz1EEC86si/1dq7GlOyZVFHSgAW+p8Q31R5idnAYCOdTM2q5KLE4/ykMeMXzY0bnp5MWTtR/wjCtWmQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.1.0", + "isows": "1.0.7", + "ox": "0.9.6", + "ws": "8.18.3" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/abitype": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.1.0.tgz", + "integrity": "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", @@ -33755,12 +33400,41 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zustand": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.0.tgz", + "integrity": "sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", @@ -34377,12 +34051,6 @@ "@esbuild/win32-x64": "0.21.5" } }, - "packages/browser-tests/node_modules/js-sha3": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.9.3.tgz", - "integrity": "sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg==", - "license": "MIT" - }, "packages/browser-tests/node_modules/undici-types": { "version": "6.19.8", "dev": true, @@ -34553,10 +34221,6 @@ } } }, - "packages/enr/node_modules/js-sha3": { - "version": "0.9.3", - "license": "MIT" - }, "packages/headless-tests": { "name": "@waku/headless-tests", "version": "0.1.0", @@ -34625,10 +34289,6 @@ "node": ">=22" } }, - "packages/message-encryption/node_modules/js-sha3": { - "version": "0.9.3", - "license": "MIT" - }, "packages/proto": { "name": "@waku/proto", "version": "0.0.15", @@ -35092,6 +34752,7 @@ "dependencies": { "@chainsafe/bls-keystore": "3.0.0", "@noble/hashes": "^1.2.0", + "@wagmi/core": "^2.22.1", "@waku/core": "^0.0.40", "@waku/utils": "^0.0.27", "@waku/zerokit-rln-wasm": "^0.2.1", @@ -35100,10 +34761,10 @@ "chai-spies": "^1.1.0", "chai-subset": "^1.6.0", "ethereum-cryptography": "^3.1.0", - "ethers": "^5.7.2", "lodash": "^4.17.21", "sinon": "^19.0.2", - "uuid": "^11.0.5" + "uuid": "^11.0.5", + "viem": "^2.38.4" }, "devDependencies": { "@rollup/plugin-commonjs": "^25.0.7", @@ -35114,12 +34775,14 @@ "@types/deep-equal-in-any-order": "^1.0.4", "@types/lodash": "^4.17.15", "@types/sinon": "^17.0.3", + "@wagmi/cli": "^2.7.0", "@waku/build-utils": "^1.0.0", "@waku/interfaces": "0.0.34", - "@waku/message-encryption": "^0.0.38", + "@waku/message-encryption": "^0.0.37", "deep-equal-in-any-order": "^2.0.6", "fast-check": "^3.23.2", - "rollup-plugin-copy": "^3.5.0" + "rollup-plugin-copy": "^3.5.0", + "tsx": "^4.19.2" }, "engines": { "node": ">=22" @@ -35140,6 +34803,89 @@ "@types/deep-eql": "*" } }, + "packages/rln/node_modules/@waku/message-encryption": { + "version": "0.0.37", + "resolved": "https://registry.npmjs.org/@waku/message-encryption/-/message-encryption-0.0.37.tgz", + "integrity": "sha512-8WmDxVnitqzxWrKRHdmHmOKk81LBh0OE9uLFzBNxirmH9G4uDx0va3+S4lazgCQ4kT0zKrsN8VldX0BfePTVDQ==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "@noble/secp256k1": "^1.7.1", + "@waku/core": "0.0.39", + "@waku/interfaces": "0.0.34", + "@waku/proto": "0.0.14", + "@waku/utils": "0.0.27", + "debug": "^4.3.4", + "js-sha3": "^0.9.2", + "uint8arrays": "^5.0.1" + }, + "engines": { + "node": ">=22" + } + }, + "packages/rln/node_modules/@waku/message-encryption/node_modules/@waku/core": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.39.tgz", + "integrity": "sha512-Vgb52md4GOzM5z9xfULzjN2tvVHKszFmj5zc2mVDoIgySH4cFBgDTHtVtGEwrFRFWadWYKBtpKBdmG3X+W7SNA==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "@libp2p/ping": "2.0.35", + "@noble/hashes": "^1.3.2", + "@waku/enr": "^0.0.33", + "@waku/interfaces": "0.0.34", + "@waku/proto": "0.0.14", + "@waku/utils": "0.0.27", + "debug": "^4.3.4", + "it-all": "^3.0.4", + "it-length-prefixed": "^9.0.4", + "it-pipe": "^3.0.1", + "uint8arraylist": "^2.4.3", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "@multiformats/multiaddr": "^12.0.0", + "libp2p": "2.8.11" + }, + "peerDependenciesMeta": { + "@multiformats/multiaddr": { + "optional": true + }, + "libp2p": { + "optional": true + } + } + }, + "packages/rln/node_modules/@waku/message-encryption/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "packages/rln/node_modules/@waku/proto": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@waku/proto/-/proto-0.0.14.tgz", + "integrity": "sha512-8zKVHrKzzKQfZBVnpSmJ6G8H1Zd4Gqms1tj3L6K2WCE/NQDR8wJtFwziab3dJ/5rKUTjfPAWFJ57RN97ltzxGA==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "protons-runtime": "^5.4.0" + }, + "engines": { + "node": ">=22" + } + }, "packages/rln/node_modules/assertion-error": { "version": "2.0.1", "license": "MIT", diff --git a/packages/rln/.eslintrc.cjs b/packages/rln/.eslintrc.cjs index c49a7edc19..b274db12d2 100644 --- a/packages/rln/.eslintrc.cjs +++ b/packages/rln/.eslintrc.cjs @@ -3,5 +3,10 @@ module.exports = { tsconfigRootDir: __dirname, project: "./tsconfig.dev.json" }, - ignorePatterns: ["src/resources/**/*"] + ignorePatterns: ["src/resources/**/*"], + overrides: [ + { + files: ["*.config.ts", "*.config.js"] + } + ] }; diff --git a/packages/rln/README.md b/packages/rln/README.md index 3de34d55a1..653cf4e998 100644 --- a/packages/rln/README.md +++ b/packages/rln/README.md @@ -12,6 +12,18 @@ This package provides RLN functionality for the Waku protocol, enabling rate-lim npm install @waku/rln ``` +## Smart Contract Type Generation + +We use `wagmi` to generate TypeScript bindings for interacting with the RLN smart contracts. + +When changes are pushed to the `waku-rlnv2-contract` repository, run the following script to fetch and build the latest contracts and generate the TypeScript bindings: + +``` +npm run setup:contract-abi +``` + +Note that we commit/bundle the generated typings, so it's not necessary to run this script unless the contracts are updated. + ## Usage ```typescript @@ -20,11 +32,6 @@ import { RLN } from '@waku/rln'; // Usage examples coming soon ``` -## Constants - -- Implementation contract: 0xde2260ca49300357d5af4153cda0d18f7b3ea9b3 -- Proxy contract: 0xb9cd878c90e49f797b4431fbf4fb333108cb90e6 - ## License -MIT OR Apache-2.0 \ No newline at end of file +MIT OR Apache-2.0 diff --git a/packages/rln/generate_contract_abi.js b/packages/rln/generate_contract_abi.js new file mode 100644 index 0000000000..5044cb5b41 --- /dev/null +++ b/packages/rln/generate_contract_abi.js @@ -0,0 +1,66 @@ +import { execSync } from "child_process"; +import { existsSync, rmSync } from "fs"; +import { dirname, join } from "path"; +import process from "process"; +import { fileURLToPath } from "url"; + +// Get script directory (equivalent to BASH_SOURCE in bash) +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const CONTRACT_DIR = join(__dirname, "waku-rlnv2-contract"); +const REPO_URL = "git@github.com:waku-org/waku-rlnv2-contract.git"; + +/** + * Execute a shell command and print output in real-time + * @param {string} command - The command to execute + * @param {object} options - Options for execSync + */ +function exec(command, options = {}) { + execSync(command, { + stdio: "inherit", + cwd: options.cwd || __dirname, + ...options + }); +} + +async function main() { + try { + console.log("📦 Setting up waku-rlnv2-contract..."); + + // Remove existing directory if it exists + if (existsSync(CONTRACT_DIR)) { + console.log("🗑️ Removing existing waku-rlnv2-contract directory..."); + rmSync(CONTRACT_DIR, { recursive: true, force: true }); + } + + // Clone the repository + console.log("📥 Cloning waku-rlnv2-contract..."); + exec(`git clone ${REPO_URL} ${CONTRACT_DIR}`); + + // Install dependencies + console.log("📦 Installing dependencies..."); + exec("npm install", { cwd: CONTRACT_DIR }); + + // Build contracts with Foundry + console.log("🔨 Building contracts with Foundry..."); + exec("forge build", { cwd: CONTRACT_DIR }); + + // Generate ABIs with wagmi + console.log("⚙️ Generating ABIs with wagmi..."); + exec("npx wagmi generate"); + + console.log("✅ Contract ABIs generated successfully!"); + } catch (error) { + console.log( + "❌ Error generating contract ABIs:", + error instanceof Error ? error.message : error + ); + process.exit(1); + } +} + +main().catch((error) => { + console.log(error); + process.exit(1); +}); diff --git a/packages/rln/package.json b/packages/rln/package.json index 6d1962b6a0..45f074cf29 100644 --- a/packages/rln/package.json +++ b/packages/rln/package.json @@ -43,7 +43,8 @@ "watch:build": "tsc -p tsconfig.json -w", "watch:test": "mocha --watch", "prepublish": "npm run build", - "reset-hard": "git clean -dfx -e .idea && git reset --hard && npm i && npm run build" + "reset-hard": "git clean -dfx -e .idea && git reset --hard && npm i && npm run build", + "setup:contract-abi": "node generate_contract_abi.js" }, "engines": { "node": ">=22" @@ -54,12 +55,13 @@ "@rollup/plugin-node-resolve": "^15.2.3", "@types/chai": "^5.0.1", "@types/chai-spies": "^1.0.6", - "@waku/interfaces": "0.0.34", "@types/deep-equal-in-any-order": "^1.0.4", "@types/lodash": "^4.17.15", "@types/sinon": "^17.0.3", + "@wagmi/cli": "^2.7.0", "@waku/build-utils": "^1.0.0", - "@waku/message-encryption": "^0.0.38", + "@waku/interfaces": "0.0.34", + "@waku/message-encryption": "^0.0.37", "deep-equal-in-any-order": "^2.0.6", "fast-check": "^3.23.2", "rollup-plugin-copy": "^3.5.0" @@ -76,18 +78,19 @@ ], "dependencies": { "@chainsafe/bls-keystore": "3.0.0", + "@noble/hashes": "^1.2.0", + "@wagmi/core": "^2.22.1", "@waku/core": "^0.0.40", "@waku/utils": "^0.0.27", - "@noble/hashes": "^1.2.0", "@waku/zerokit-rln-wasm": "^0.2.1", - "ethereum-cryptography": "^3.1.0", - "ethers": "^5.7.2", - "lodash": "^4.17.21", - "uuid": "^11.0.5", "chai": "^5.1.2", "chai-as-promised": "^8.0.1", "chai-spies": "^1.1.0", "chai-subset": "^1.6.0", - "sinon": "^19.0.2" + "ethereum-cryptography": "^3.1.0", + "lodash": "^4.17.21", + "sinon": "^19.0.2", + "uuid": "^11.0.5", + "viem": "^2.38.4" } } diff --git a/packages/rln/src/contract/abi/price_calculator.ts b/packages/rln/src/contract/abi/price_calculator.ts deleted file mode 100644 index 8199e7764b..0000000000 --- a/packages/rln/src/contract/abi/price_calculator.ts +++ /dev/null @@ -1,93 +0,0 @@ -export const PRICE_CALCULATOR_ABI = [ - { - inputs: [ - { internalType: "address", name: "_token", type: "address" }, - { - internalType: "uint256", - name: "_pricePerMessagePerEpoch", - type: "uint256" - } - ], - stateMutability: "nonpayable", - type: "constructor" - }, - { inputs: [], name: "OnlyTokensAllowed", type: "error" }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "address", - name: "previousOwner", - type: "address" - }, - { - indexed: true, - internalType: "address", - name: "newOwner", - type: "address" - } - ], - name: "OwnershipTransferred", - type: "event" - }, - { - inputs: [{ internalType: "uint32", name: "_rateLimit", type: "uint32" }], - name: "calculate", - outputs: [ - { internalType: "address", name: "", type: "address" }, - { internalType: "uint256", name: "", type: "uint256" } - ], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "owner", - outputs: [{ internalType: "address", name: "", type: "address" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "pricePerMessagePerEpoch", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "renounceOwnership", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "_token", type: "address" }, - { - internalType: "uint256", - name: "_pricePerMessagePerEpoch", - type: "uint256" - } - ], - name: "setTokenAndPrice", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [], - name: "token", - outputs: [{ internalType: "address", name: "", type: "address" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [{ internalType: "address", name: "newOwner", type: "address" }], - name: "transferOwnership", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } -]; diff --git a/packages/rln/src/contract/abi/rln.ts b/packages/rln/src/contract/abi/rln.ts deleted file mode 100644 index 858ec25de5..0000000000 --- a/packages/rln/src/contract/abi/rln.ts +++ /dev/null @@ -1,646 +0,0 @@ -export const RLN_ABI = [ - { inputs: [], stateMutability: "nonpayable", type: "constructor" }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "CannotEraseActiveMembership", - type: "error" - }, - { inputs: [], name: "CannotExceedMaxTotalRateLimit", type: "error" }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "CannotExtendNonGracePeriodMembership", - type: "error" - }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "InvalidIdCommitment", - type: "error" - }, - { inputs: [], name: "InvalidMembershipRateLimit", type: "error" }, - { - inputs: [ - { internalType: "uint256", name: "startIndex", type: "uint256" }, - { internalType: "uint256", name: "endIndex", type: "uint256" } - ], - name: "InvalidPaginationQuery", - type: "error" - }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "MembershipDoesNotExist", - type: "error" - }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "NonHolderCannotEraseGracePeriodMembership", - type: "error" - }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "NonHolderCannotExtend", - type: "error" - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "address", - name: "previousAdmin", - type: "address" - }, - { - indexed: false, - internalType: "address", - name: "newAdmin", - type: "address" - } - ], - name: "AdminChanged", - type: "event" - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "address", - name: "beacon", - type: "address" - } - ], - name: "BeaconUpgraded", - type: "event" - }, - { - anonymous: false, - inputs: [ - { indexed: false, internalType: "uint8", name: "version", type: "uint8" } - ], - name: "Initialized", - type: "event" - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "uint256", - name: "idCommitment", - type: "uint256" - }, - { - indexed: false, - internalType: "uint32", - name: "membershipRateLimit", - type: "uint32" - }, - { indexed: false, internalType: "uint32", name: "index", type: "uint32" } - ], - name: "MembershipErased", - type: "event" - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "uint256", - name: "idCommitment", - type: "uint256" - }, - { - indexed: false, - internalType: "uint32", - name: "membershipRateLimit", - type: "uint32" - }, - { indexed: false, internalType: "uint32", name: "index", type: "uint32" } - ], - name: "MembershipExpired", - type: "event" - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "uint256", - name: "idCommitment", - type: "uint256" - }, - { - indexed: false, - internalType: "uint32", - name: "membershipRateLimit", - type: "uint32" - }, - { indexed: false, internalType: "uint32", name: "index", type: "uint32" }, - { - indexed: false, - internalType: "uint256", - name: "newGracePeriodStartTimestamp", - type: "uint256" - } - ], - name: "MembershipExtended", - type: "event" - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "uint256", - name: "idCommitment", - type: "uint256" - }, - { - indexed: false, - internalType: "uint256", - name: "membershipRateLimit", - type: "uint256" - }, - { indexed: false, internalType: "uint32", name: "index", type: "uint32" } - ], - name: "MembershipRegistered", - type: "event" - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "address", - name: "previousOwner", - type: "address" - }, - { - indexed: true, - internalType: "address", - name: "newOwner", - type: "address" - } - ], - name: "OwnershipTransferred", - type: "event" - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: "address", - name: "implementation", - type: "address" - } - ], - name: "Upgraded", - type: "event" - }, - { - inputs: [], - name: "MAX_MEMBERSHIP_SET_SIZE", - outputs: [{ internalType: "uint32", name: "", type: "uint32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "MERKLE_TREE_DEPTH", - outputs: [{ internalType: "uint8", name: "", type: "uint8" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "Q", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "activeDurationForNewMemberships", - outputs: [{ internalType: "uint32", name: "", type: "uint32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "currentTotalRateLimit", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "deployedBlockNumber", - outputs: [{ internalType: "uint32", name: "", type: "uint32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "holder", type: "address" }, - { internalType: "address", name: "token", type: "address" } - ], - name: "depositsToWithdraw", - outputs: [{ internalType: "uint256", name: "balance", type: "uint256" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "uint256[]", name: "idCommitments", type: "uint256[]" } - ], - name: "eraseMemberships", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "uint256[]", name: "idCommitments", type: "uint256[]" }, - { internalType: "bool", name: "eraseFromMembershipSet", type: "bool" } - ], - name: "eraseMemberships", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "uint256[]", name: "idCommitments", type: "uint256[]" } - ], - name: "extendMemberships", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "getMembershipInfo", - outputs: [ - { internalType: "uint32", name: "", type: "uint32" }, - { internalType: "uint32", name: "", type: "uint32" }, - { internalType: "uint256", name: "", type: "uint256" } - ], - stateMutability: "view", - type: "function" - }, - { - inputs: [{ internalType: "uint40", name: "index", type: "uint40" }], - name: "getMerkleProof", - outputs: [{ internalType: "uint256[20]", name: "", type: "uint256[20]" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "uint32", name: "startIndex", type: "uint32" }, - { internalType: "uint32", name: "endIndex", type: "uint32" } - ], - name: "getRateCommitmentsInRangeBoundsInclusive", - outputs: [{ internalType: "uint256[]", name: "", type: "uint256[]" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "gracePeriodDurationForNewMemberships", - outputs: [{ internalType: "uint32", name: "", type: "uint32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [{ internalType: "uint256", name: "", type: "uint256" }], - name: "indicesOfLazilyErasedMemberships", - outputs: [{ internalType: "uint32", name: "", type: "uint32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "_priceCalculator", type: "address" }, - { internalType: "uint32", name: "_maxTotalRateLimit", type: "uint32" }, - { - internalType: "uint32", - name: "_minMembershipRateLimit", - type: "uint32" - }, - { - internalType: "uint32", - name: "_maxMembershipRateLimit", - type: "uint32" - }, - { internalType: "uint32", name: "_activeDuration", type: "uint32" }, - { internalType: "uint32", name: "_gracePeriod", type: "uint32" } - ], - name: "initialize", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "uint256", name: "_idCommitment", type: "uint256" } - ], - name: "isExpired", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "uint256", name: "_idCommitment", type: "uint256" } - ], - name: "isInGracePeriod", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "isInMembershipSet", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "isValidIdCommitment", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "pure", - type: "function" - }, - { - inputs: [{ internalType: "uint32", name: "rateLimit", type: "uint32" }], - name: "isValidMembershipRateLimit", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "maxMembershipRateLimit", - outputs: [{ internalType: "uint32", name: "", type: "uint32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "maxTotalRateLimit", - outputs: [{ internalType: "uint32", name: "", type: "uint32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "uint256", name: "_idCommitment", type: "uint256" } - ], - name: "membershipExpirationTimestamp", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" } - ], - name: "memberships", - outputs: [ - { internalType: "uint256", name: "depositAmount", type: "uint256" }, - { internalType: "uint32", name: "activeDuration", type: "uint32" }, - { - internalType: "uint256", - name: "gracePeriodStartTimestamp", - type: "uint256" - }, - { internalType: "uint32", name: "gracePeriodDuration", type: "uint32" }, - { internalType: "uint32", name: "rateLimit", type: "uint32" }, - { internalType: "uint32", name: "index", type: "uint32" }, - { internalType: "address", name: "holder", type: "address" }, - { internalType: "address", name: "token", type: "address" } - ], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "merkleTree", - outputs: [ - { internalType: "uint40", name: "maxIndex", type: "uint40" }, - { internalType: "uint40", name: "numberOfLeaves", type: "uint40" } - ], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "minMembershipRateLimit", - outputs: [{ internalType: "uint32", name: "", type: "uint32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "nextFreeIndex", - outputs: [{ internalType: "uint32", name: "", type: "uint32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "owner", - outputs: [{ internalType: "address", name: "", type: "address" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "priceCalculator", - outputs: [ - { internalType: "contract IPriceCalculator", name: "", type: "address" } - ], - stateMutability: "view", - type: "function" - }, - { - inputs: [], - name: "proxiableUUID", - outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "uint256", name: "idCommitment", type: "uint256" }, - { internalType: "uint32", name: "rateLimit", type: "uint32" }, - { - internalType: "uint256[]", - name: "idCommitmentsToErase", - type: "uint256[]" - } - ], - name: "register", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "owner", type: "address" }, - { internalType: "uint256", name: "deadline", type: "uint256" }, - { internalType: "uint8", name: "v", type: "uint8" }, - { internalType: "bytes32", name: "r", type: "bytes32" }, - { internalType: "bytes32", name: "s", type: "bytes32" }, - { internalType: "uint256", name: "idCommitment", type: "uint256" }, - { internalType: "uint32", name: "rateLimit", type: "uint32" }, - { - internalType: "uint256[]", - name: "idCommitmentsToErase", - type: "uint256[]" - } - ], - name: "registerWithPermit", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [], - name: "renounceOwnership", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [], - name: "root", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { - internalType: "uint32", - name: "_activeDurationForNewMembership", - type: "uint32" - } - ], - name: "setActiveDuration", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { - internalType: "uint32", - name: "_gracePeriodDurationForNewMembership", - type: "uint32" - } - ], - name: "setGracePeriodDuration", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { - internalType: "uint32", - name: "_maxMembershipRateLimit", - type: "uint32" - } - ], - name: "setMaxMembershipRateLimit", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "uint32", name: "_maxTotalRateLimit", type: "uint32" } - ], - name: "setMaxTotalRateLimit", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { - internalType: "uint32", - name: "_minMembershipRateLimit", - type: "uint32" - } - ], - name: "setMinMembershipRateLimit", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "_priceCalculator", type: "address" } - ], - name: "setPriceCalculator", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [{ internalType: "address", name: "newOwner", type: "address" }], - name: "transferOwnership", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "newImplementation", type: "address" } - ], - name: "upgradeTo", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "newImplementation", type: "address" }, - { internalType: "bytes", name: "data", type: "bytes" } - ], - name: "upgradeToAndCall", - outputs: [], - stateMutability: "payable", - type: "function" - }, - { - inputs: [{ internalType: "address", name: "token", type: "address" }], - name: "withdraw", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } -]; diff --git a/packages/rln/src/contract/constants.ts b/packages/rln/src/contract/constants.ts index 7869e65bc6..9e895424d1 100644 --- a/packages/rln/src/contract/constants.ts +++ b/packages/rln/src/contract/constants.ts @@ -1,16 +1,15 @@ -import { PRICE_CALCULATOR_ABI } from "./abi/price_calculator.js"; -import { RLN_ABI } from "./abi/rln.js"; +import { linearPriceCalculatorAbi, wakuRlnV2Abi } from "./wagmi/generated.js"; export const RLN_CONTRACT = { chainId: 59141, address: "0xb9cd878c90e49f797b4431fbf4fb333108cb90e6", - abi: RLN_ABI + abi: wakuRlnV2Abi }; export const PRICE_CALCULATOR_CONTRACT = { chainId: 59141, address: "0xBcfC0660Df69f53ab409F32bb18A3fb625fcE644", - abi: PRICE_CALCULATOR_ABI + abi: linearPriceCalculatorAbi }; /** diff --git a/packages/rln/src/contract/price_calculator.spec.ts b/packages/rln/src/contract/price_calculator.spec.ts index 2b36585509..e2fa53b1ec 100644 --- a/packages/rln/src/contract/price_calculator.spec.ts +++ b/packages/rln/src/contract/price_calculator.spec.ts @@ -1,28 +1,39 @@ import { expect, use } from "chai"; import chaiAsPromised from "chai-as-promised"; -import { ethers } from "ethers"; import sinon from "sinon"; import { RLNBaseContract } from "./rln_base_contract.js"; use(chaiAsPromised); -function createMockRLNBaseContract(provider: any): RLNBaseContract { +function createMockRLNBaseContract( + mockContract: any, + mockRpcClient: any +): RLNBaseContract { const dummy = Object.create(RLNBaseContract.prototype); - dummy.contract = { provider }; + dummy.contract = mockContract; + dummy.rpcClient = mockRpcClient; return dummy as RLNBaseContract; } describe("RLNBaseContract.getPriceForRateLimit (unit)", function () { - let provider: any; - let calculateStub: sinon.SinonStub; - let mockContractFactory: any; + let mockContract: any; + let mockRpcClient: any; + let priceCalculatorReadStub: sinon.SinonStub; + let readContractStub: sinon.SinonStub; beforeEach(() => { - provider = {}; - calculateStub = sinon.stub(); - mockContractFactory = function () { - return { calculate: calculateStub }; + priceCalculatorReadStub = sinon.stub(); + readContractStub = sinon.stub(); + + mockContract = { + read: { + priceCalculator: priceCalculatorReadStub + } + }; + + mockRpcClient = { + readContract: readContractStub }; }); @@ -32,35 +43,53 @@ describe("RLNBaseContract.getPriceForRateLimit (unit)", function () { it("returns token and price for valid calculate", async () => { const fakeToken = "0x1234567890abcdef1234567890abcdef12345678"; - const fakePrice = ethers.BigNumber.from(42); - calculateStub.resolves([fakeToken, fakePrice]); + const fakePrice = 42n; + const priceCalculatorAddress = "0xabcdef1234567890abcdef1234567890abcdef12"; + + priceCalculatorReadStub.resolves(priceCalculatorAddress); + readContractStub.resolves([fakeToken, fakePrice]); + + const rlnBase = createMockRLNBaseContract(mockContract, mockRpcClient); + const result = await rlnBase.getPriceForRateLimit(20); - const rlnBase = createMockRLNBaseContract(provider); - const result = await rlnBase.getPriceForRateLimit(20, mockContractFactory); expect(result.token).to.equal(fakeToken); - expect(result.price).to.not.be.null; - if (result.price) { - expect(result.price.eq(fakePrice)).to.be.true; - } - expect(calculateStub.calledOnceWith(20)).to.be.true; + expect(result.price).to.equal(fakePrice); + expect(priceCalculatorReadStub.calledOnce).to.be.true; + expect(readContractStub.calledOnce).to.be.true; + + const readContractCall = readContractStub.getCall(0); + expect(readContractCall.args[0]).to.deep.include({ + address: priceCalculatorAddress, + functionName: "calculate", + args: [20] + }); }); it("throws if calculate throws", async () => { - calculateStub.rejects(new Error("fail")); + const priceCalculatorAddress = "0xabcdef1234567890abcdef1234567890abcdef12"; + + priceCalculatorReadStub.resolves(priceCalculatorAddress); + readContractStub.rejects(new Error("fail")); + + const rlnBase = createMockRLNBaseContract(mockContract, mockRpcClient); + await expect(rlnBase.getPriceForRateLimit(20)).to.be.rejectedWith("fail"); - const rlnBase = createMockRLNBaseContract(provider); - await expect( - rlnBase.getPriceForRateLimit(20, mockContractFactory) - ).to.be.rejectedWith("fail"); - expect(calculateStub.calledOnceWith(20)).to.be.true; + expect(priceCalculatorReadStub.calledOnce).to.be.true; + expect(readContractStub.calledOnce).to.be.true; }); - it("throws if calculate returns malformed data", async () => { - calculateStub.resolves([null, null]); + it("returns null values if calculate returns malformed data", async () => { + const priceCalculatorAddress = "0xabcdef1234567890abcdef1234567890abcdef12"; + + priceCalculatorReadStub.resolves(priceCalculatorAddress); + readContractStub.resolves([null, null]); + + const rlnBase = createMockRLNBaseContract(mockContract, mockRpcClient); + const result = await rlnBase.getPriceForRateLimit(20); - const rlnBase = createMockRLNBaseContract(provider); - const result = await rlnBase.getPriceForRateLimit(20, mockContractFactory); expect(result.token).to.be.null; expect(result.price).to.be.null; + expect(priceCalculatorReadStub.calledOnce).to.be.true; + expect(readContractStub.calledOnce).to.be.true; }); }); diff --git a/packages/rln/src/contract/rln_base_contract.ts b/packages/rln/src/contract/rln_base_contract.ts index 9934360be3..75c4f7a4ef 100644 --- a/packages/rln/src/contract/rln_base_contract.ts +++ b/packages/rln/src/contract/rln_base_contract.ts @@ -1,92 +1,74 @@ import { Logger } from "@waku/utils"; -import { ethers } from "ethers"; +import { + type Address, + decodeEventLog, + getContract, + type GetContractReturnType, + type Hash, + type PublicClient, + type WalletClient +} from "viem"; import { IdentityCredential } from "../identity.js"; -import { DecryptedCredentials } from "../keystore/types.js"; +import type { DecryptedCredentials } from "../keystore/types.js"; +import type { RpcClient } from "../utils/index.js"; -import { RLN_ABI } from "./abi/rln.js"; import { DEFAULT_RATE_LIMIT, - PRICE_CALCULATOR_CONTRACT, - RATE_LIMIT_PARAMS + RATE_LIMIT_PARAMS, + RLN_CONTRACT } from "./constants.js"; import { - CustomQueryOptions, - FetchMembersOptions, - Member, MembershipInfo, - MembershipRegisteredEvent, MembershipState, - RLNContractInitOptions + RLNContractOptions } from "./types.js"; +import { iPriceCalculatorAbi, wakuRlnV2Abi } from "./wagmi/generated.js"; const log = new Logger("rln:contract:base"); export class RLNBaseContract { - public contract: ethers.Contract; - private deployBlock: undefined | number; + public contract: GetContractReturnType< + typeof wakuRlnV2Abi, + PublicClient | WalletClient + >; + public rpcClient: RpcClient; private rateLimit: number; private minRateLimit?: number; private maxRateLimit?: number; - protected _members: Map = new Map(); - private _membersFilter: ethers.EventFilter; - private _membershipErasedFilter: ethers.EventFilter; - private _membersExpiredFilter: ethers.EventFilter; - /** * Private constructor for RLNBaseContract. Use static create() instead. */ - protected constructor(options: RLNContractInitOptions) { - const { - address, - signer, - rateLimit = DEFAULT_RATE_LIMIT, - contract - } = options; + protected constructor(options: RLNContractOptions) { + const { address, rpcClient, rateLimit = DEFAULT_RATE_LIMIT } = options; log.info("Initializing RLNBaseContract", { address, rateLimit }); - this.contract = contract || new ethers.Contract(address, RLN_ABI, signer); + this.rpcClient = rpcClient; + this.contract = getContract({ + address, + abi: wakuRlnV2Abi, + client: this.rpcClient + }); this.rateLimit = rateLimit; - - try { - log.info("Setting up event filters"); - // Initialize event filters - this._membersFilter = this.contract.filters.MembershipRegistered(); - this._membershipErasedFilter = this.contract.filters.MembershipErased(); - this._membersExpiredFilter = this.contract.filters.MembershipExpired(); - log.info("Event filters initialized successfully"); - } catch (error) { - log.error("Failed to initialize event filters", { error }); - throw new Error( - "Failed to initialize event filters: " + (error as Error).message - ); - } - - // Initialize members and subscriptions - this.fetchMembers() - .then(() => { - this.subscribeToMembers(); - }) - .catch((error) => { - log.error("Failed to initialize members", { error }); - }); } /** * Static async factory to create and initialize RLNBaseContract */ public static async create( - options: RLNContractInitOptions + options: RLNContractOptions ): Promise { const instance = new RLNBaseContract(options); + const [min, max] = await Promise.all([ - instance.contract.minMembershipRateLimit(), - instance.contract.maxMembershipRateLimit() + instance.contract.read.minMembershipRateLimit(), + instance.contract.read.maxMembershipRateLimit() ]); - instance.minRateLimit = ethers.BigNumber.from(min).toNumber(); - instance.maxRateLimit = ethers.BigNumber.from(max).toNumber(); + + instance.minRateLimit = min; + instance.maxRateLimit = max; instance.validateRateLimit(instance.rateLimit); return instance; @@ -106,13 +88,6 @@ export class RLNBaseContract { return this.contract.address; } - /** - * Gets the contract provider - */ - public get provider(): ethers.providers.Provider { - return this.contract.provider; - } - /** * Gets the minimum allowed rate limit (cached) */ @@ -136,8 +111,7 @@ export class RLNBaseContract { * @returns Promise The maximum total rate limit in messages per epoch */ public async getMaxTotalRateLimit(): Promise { - const maxTotalRate = await this.contract.maxTotalRateLimit(); - return maxTotalRate.toNumber(); + return await this.contract.read.maxTotalRateLimit(); } /** @@ -145,8 +119,7 @@ export class RLNBaseContract { * @returns Promise The current total rate limit usage in messages per epoch */ public async getCurrentTotalRateLimit(): Promise { - const currentTotal = await this.contract.currentTotalRateLimit(); - return currentTotal.toNumber(); + return Number(await this.contract.read.currentTotalRateLimit()); } /** @@ -154,11 +127,10 @@ export class RLNBaseContract { * @returns Promise The remaining rate limit that can be allocated */ public async getRemainingTotalRateLimit(): Promise { - const [maxTotal, currentTotal] = await Promise.all([ - this.contract.maxTotalRateLimit(), - this.contract.currentTotalRateLimit() - ]); - return Number(maxTotal) - Number(currentTotal); + return ( + (await this.contract.read.maxTotalRateLimit()) - + Number(await this.contract.read.currentTotalRateLimit()) + ); } /** @@ -170,233 +142,35 @@ export class RLNBaseContract { this.rateLimit = newRateLimit; } - public get members(): Member[] { - const sortedMembers = Array.from(this._members.values()).sort( - (left, right) => left.index.toNumber() - right.index.toNumber() - ); - return sortedMembers; - } - - public async fetchMembers(options: FetchMembersOptions = {}): Promise { - const registeredMemberEvents = await RLNBaseContract.queryFilter( - this.contract, - { - fromBlock: this.deployBlock, - ...options, - membersFilter: this.membersFilter - } - ); - const removedMemberEvents = await RLNBaseContract.queryFilter( - this.contract, - { - fromBlock: this.deployBlock, - ...options, - membersFilter: this.membershipErasedFilter - } - ); - const expiredMemberEvents = await RLNBaseContract.queryFilter( - this.contract, - { - fromBlock: this.deployBlock, - ...options, - membersFilter: this.membersExpiredFilter - } - ); - - const events = [ - ...registeredMemberEvents, - ...removedMemberEvents, - ...expiredMemberEvents - ]; - this.processEvents(events); - } - - public static async queryFilter( - contract: ethers.Contract, - options: CustomQueryOptions - ): Promise { - const FETCH_CHUNK = 5; - const BLOCK_RANGE = 3000; - - const { - fromBlock, - membersFilter, - fetchRange = BLOCK_RANGE, - fetchChunks = FETCH_CHUNK - } = options; - - if (fromBlock === undefined) { - return contract.queryFilter(membersFilter); - } - - if (!contract.provider) { - throw Error("No provider found on the contract."); - } - - const toBlock = await contract.provider.getBlockNumber(); - - if (toBlock - fromBlock < fetchRange) { - return contract.queryFilter(membersFilter, fromBlock, toBlock); - } - - const events: ethers.Event[][] = []; - const chunks = RLNBaseContract.splitToChunks( - fromBlock, - toBlock, - fetchRange - ); - - for (const portion of RLNBaseContract.takeN<[number, number]>( - chunks, - fetchChunks - )) { - const promises = portion.map(([left, right]) => - RLNBaseContract.ignoreErrors( - contract.queryFilter(membersFilter, left, right), - [] - ) - ); - const fetchedEvents = await Promise.all(promises); - events.push(fetchedEvents.flatMap((v) => v)); - } - - return events.flatMap((v) => v); - } - - public processEvents(events: ethers.Event[]): void { - const toRemoveTable = new Map(); - const toInsertTable = new Map(); - - events.forEach((evt) => { - if (!evt.args) { - return; - } - - if ( - evt.event === "MembershipErased" || - evt.event === "MembershipExpired" - ) { - let index = evt.args.index; - - if (!index) { - return; - } - - if (typeof index === "number" || typeof index === "string") { - index = ethers.BigNumber.from(index); - } - - const toRemoveVal = toRemoveTable.get(evt.blockNumber); - if (toRemoveVal != undefined) { - toRemoveVal.push(index.toNumber()); - toRemoveTable.set(evt.blockNumber, toRemoveVal); - } else { - toRemoveTable.set(evt.blockNumber, [index.toNumber()]); - } - } else if (evt.event === "MembershipRegistered") { - let eventsPerBlock = toInsertTable.get(evt.blockNumber); - if (eventsPerBlock == undefined) { - eventsPerBlock = []; - } - - eventsPerBlock.push(evt); - toInsertTable.set(evt.blockNumber, eventsPerBlock); - } - }); - } - - public static splitToChunks( - from: number, - to: number, - step: number - ): Array<[number, number]> { - const chunks: Array<[number, number]> = []; - - let left = from; - while (left < to) { - const right = left + step < to ? left + step : to; - - chunks.push([left, right] as [number, number]); - - left = right; - } - - return chunks; - } - - public static *takeN(array: T[], size: number): Iterable { - let start = 0; - - while (start < array.length) { - const portion = array.slice(start, start + size); - - yield portion; - - start += size; - } - } - - public static async ignoreErrors( - promise: Promise, - defaultValue: T - ): Promise { - try { - return await promise; - } catch (err: unknown) { - if (err instanceof Error) { - log.info(`Ignoring an error during query: ${err.message}`); - } else { - log.info(`Ignoring an unknown error during query`); - } - return defaultValue; - } + /** + * Gets the Merkle tree root for RLN proof verification + * @returns Promise The Merkle tree root + * + */ + public async getMerkleRoot(): Promise { + return this.contract.read.root(); } - public subscribeToMembers(): void { - this.contract.on( - this.membersFilter, - ( - _idCommitment: bigint, - _membershipRateLimit: ethers.BigNumber, - _index: ethers.BigNumber, - event: ethers.Event - ) => { - this.processEvents([event]); - } - ); - - this.contract.on( - this.membershipErasedFilter, - ( - _idCommitment: bigint, - _membershipRateLimit: ethers.BigNumber, - _index: ethers.BigNumber, - event: ethers.Event - ) => { - this.processEvents([event]); - } - ); - - this.contract.on( - this.membersExpiredFilter, - ( - _idCommitment: bigint, - _membershipRateLimit: ethers.BigNumber, - _index: ethers.BigNumber, - event: ethers.Event - ) => { - this.processEvents([event]); - } - ); + /** + * Gets the Merkle proof for a member at a given index + * @param index The index of the member in the membership set + * @returns Promise Array of 20 Merkle proof elements + * + */ + public async getMerkleProof(index: number): Promise { + return await this.contract.read.getMerkleProof([index]); } public async getMembershipInfo( idCommitmentBigInt: bigint ): Promise { try { - const membershipData = - await this.contract.memberships(idCommitmentBigInt); - const currentBlock = await this.contract.provider.getBlockNumber(); + const membershipData = await this.contract.read.memberships([ + idCommitmentBigInt + ]); + + const currentBlock = await this.rpcClient.getBlockNumber(); + const [ depositAmount, activeDuration, @@ -408,12 +182,13 @@ export class RLNBaseContract { token ] = membershipData; - const gracePeriodEnd = gracePeriodStartTimestamp.add(gracePeriodDuration); + const gracePeriodEnd = + Number(gracePeriodStartTimestamp) + Number(gracePeriodDuration); let state: MembershipState; - if (currentBlock < gracePeriodStartTimestamp.toNumber()) { + if (currentBlock < Number(gracePeriodStartTimestamp)) { state = MembershipState.Active; - } else if (currentBlock < gracePeriodEnd.toNumber()) { + } else if (currentBlock < gracePeriodEnd) { state = MembershipState.GracePeriod; } else { state = MembershipState.Expired; @@ -422,9 +197,9 @@ export class RLNBaseContract { return { index, idCommitment: idCommitmentBigInt.toString(), - rateLimit: Number(rateLimit), - startBlock: gracePeriodStartTimestamp.toNumber(), - endBlock: gracePeriodEnd.toNumber(), + rateLimit: rateLimit, + startBlock: Number(gracePeriodStartTimestamp), + endBlock: gracePeriodEnd, state, depositAmount, activeDuration, @@ -438,43 +213,75 @@ export class RLNBaseContract { } } - public async extendMembership( - idCommitmentBigInt: bigint - ): Promise { - const tx = await this.contract.extendMemberships([idCommitmentBigInt]); - await tx.wait(); - return tx; + public async extendMembership(idCommitmentBigInt: bigint): Promise { + if (!this.rpcClient.account) { + throw new Error( + "Failed to extendMembership: no account set in wallet client" + ); + } + try { + await this.contract.simulate.extendMemberships([[idCommitmentBigInt]], { + chain: this.rpcClient.chain, + account: this.rpcClient.account.address + }); + } catch (err) { + throw new Error("Simulating extending membership failed: " + err); + } + const hash = await this.contract.write.extendMemberships( + [[idCommitmentBigInt]], + { + account: this.rpcClient.account, + chain: this.rpcClient.chain + } + ); + + await this.rpcClient.waitForTransactionReceipt({ hash }); + return hash; } public async eraseMembership( idCommitmentBigInt: bigint, eraseFromMembershipSet: boolean = true - ): Promise { + ): Promise { if ( !(await this.isExpired(idCommitmentBigInt)) || !(await this.isInGracePeriod(idCommitmentBigInt)) ) { throw new Error("Membership is not expired or in grace period"); } + if (!this.rpcClient.account) { + throw new Error( + "Failed to eraseMembership: no account set in wallet client" + ); + } - const estimatedGas = await this.contract.estimateGas[ - "eraseMemberships(uint256[],bool)" - ]([idCommitmentBigInt], eraseFromMembershipSet); - const gasLimit = estimatedGas.add(10000); + try { + await this.contract.simulate.eraseMemberships( + [[idCommitmentBigInt], eraseFromMembershipSet], + { + chain: this.rpcClient.chain, + account: this.rpcClient.account.address + } + ); + } catch (err) { + throw new Error("Error simulating eraseMemberships: " + err); + } - const tx = await this.contract["eraseMemberships(uint256[],bool)"]( - [idCommitmentBigInt], - eraseFromMembershipSet, - { gasLimit } + const hash = await this.contract.write.eraseMemberships( + [[idCommitmentBigInt], eraseFromMembershipSet], + { + chain: this.rpcClient.chain, + account: this.rpcClient.account + } ); - await tx.wait(); - return tx; + await this.rpcClient.waitForTransactionReceipt({ hash }); + return hash; } public async registerMembership( idCommitmentBigInt: bigint, rateLimit: number = DEFAULT_RATE_LIMIT - ): Promise { + ): Promise { if ( rateLimit < RATE_LIMIT_PARAMS.MIN_RATE || rateLimit > RATE_LIMIT_PARAMS.MAX_RATE @@ -483,21 +290,72 @@ export class RLNBaseContract { `Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}` ); } - return this.contract.register(idCommitmentBigInt, rateLimit, []); + if (!this.rpcClient.account) { + throw new Error( + "Failed to registerMembership: no account set in wallet client" + ); + } + try { + await this.contract.simulate.register( + [idCommitmentBigInt, rateLimit, []], + { + chain: this.rpcClient.chain, + account: this.rpcClient.account.address + } + ); + } catch (err) { + throw new Error("Failed to simulate register membership: " + err); + } + + const hash = await this.contract.write.register( + [idCommitmentBigInt, rateLimit, []], + { + chain: this.rpcClient.chain, + account: this.rpcClient.account + } + ); + await this.rpcClient.waitForTransactionReceipt({ hash }); + return hash; } - public async withdraw(token: string, walletAddress: string): Promise { + /** + * Withdraw deposited tokens after membership is erased. + * The smart contract validates that the sender is the holder of the membership, + * and will only send tokens to that address. + * @param token - Token address to withdraw + */ + public async withdraw(token: string): Promise { + if (!this.rpcClient.account) { + throw new Error("Failed to withdraw: no account set in wallet client"); + } + try { - const tx = await this.contract.withdraw(token, walletAddress); - await tx.wait(); - } catch (error) { - log.error(`Error in withdraw: ${(error as Error).message}`); + await this.contract.simulate.withdraw([token as Address], { + chain: this.rpcClient.chain, + account: this.rpcClient.account.address + }); + } catch (err) { + throw new Error("Error simulating withdraw: " + err); } + + const hash = await this.contract.write.withdraw([token as Address], { + chain: this.rpcClient.chain, + account: this.rpcClient.account + }); + + await this.rpcClient.waitForTransactionReceipt({ hash }); + return hash; } public async registerWithIdentity( identity: IdentityCredential ): Promise { try { + if (!this.rpcClient.account) { + throw new Error( + "Failed to registerWithIdentity: no account set in wallet client" + ); + } + log.info( `Registering identity with rate limit: ${this.rateLimit} messages/epoch` ); @@ -520,62 +378,71 @@ export class RLNBaseContract { ); } - const estimatedGas = await this.contract.estimateGas.register( - identity.IDCommitmentBigInt, - this.rateLimit, - [] + await this.contract.simulate.register( + [identity.IDCommitmentBigInt, this.rateLimit, []], + { + chain: this.rpcClient.chain, + account: this.rpcClient.account.address + } + ); + + const hash: Hash = await this.contract.write.register( + [identity.IDCommitmentBigInt, this.rateLimit, []], + { + chain: this.rpcClient.chain, + account: this.rpcClient.account + } ); - const gasLimit = estimatedGas.add(10000); - - const txRegisterResponse: ethers.ContractTransaction = - await this.contract.register( - identity.IDCommitmentBigInt, - this.rateLimit, - [], - { - gasLimit - } - ); - const txRegisterReceipt = await txRegisterResponse.wait(); + const txRegisterReceipt = await this.rpcClient.waitForTransactionReceipt({ + hash + }); - if (txRegisterReceipt.status === 0) { + if (txRegisterReceipt.status === "reverted") { throw new Error("Transaction failed on-chain"); } - const memberRegistered = txRegisterReceipt.events?.find( - (event: ethers.Event) => event.event === "MembershipRegistered" - ); + // Parse MembershipRegistered event from logs + const memberRegisteredLog = txRegisterReceipt.logs.find((log) => { + try { + const decoded = decodeEventLog({ + abi: wakuRlnV2Abi, + data: log.data, + topics: log.topics + }); + return decoded.eventName === "MembershipRegistered"; + } catch { + return false; + } + }); - if (!memberRegistered || !memberRegistered.args) { + if (!memberRegisteredLog) { log.error( "Failed to register membership: No MembershipRegistered event found" ); return undefined; } - const decodedData: MembershipRegisteredEvent = { - idCommitment: memberRegistered.args.idCommitment, - membershipRateLimit: memberRegistered.args.membershipRateLimit, - index: memberRegistered.args.index - }; + // Decode the event + const decoded = decodeEventLog({ + abi: wakuRlnV2Abi, + data: memberRegisteredLog.data, + topics: memberRegisteredLog.topics, + eventName: "MembershipRegistered" + }); log.info( - `Successfully registered membership with index ${decodedData.index} ` + - `and rate limit ${decodedData.membershipRateLimit}` + `Successfully registered membership with index ${decoded.args.index} ` + + `and rate limit ${decoded.args.membershipRateLimit}` ); - const network = await this.contract.provider.getNetwork(); - const address = this.contract.address; - const membershipId = Number(decodedData.index); - return { identity, membership: { - address, - treeIndex: membershipId, - chainId: network.chainId.toString(), - rateLimit: decodedData.membershipRateLimit.toNumber() + address: this.contract.address, + treeIndex: decoded.args.index, + chainId: String(RLN_CONTRACT.chainId), + rateLimit: Number(decoded.args.membershipRateLimit) } }; } catch (error) { @@ -608,78 +475,6 @@ export class RLNBaseContract { } } - public async registerWithPermitAndErase( - identity: IdentityCredential, - permit: { - owner: string; - deadline: number; - v: number; - r: string; - s: string; - }, - idCommitmentsToErase: string[] - ): Promise { - try { - log.info( - `Registering identity with permit and rate limit: ${this.rateLimit} messages/epoch` - ); - - const txRegisterResponse: ethers.ContractTransaction = - await this.contract.registerWithPermit( - permit.owner, - permit.deadline, - permit.v, - permit.r, - permit.s, - identity.IDCommitmentBigInt, - this.rateLimit, - idCommitmentsToErase.map((id) => ethers.BigNumber.from(id)) - ); - const txRegisterReceipt = await txRegisterResponse.wait(); - - const memberRegistered = txRegisterReceipt.events?.find( - (event: ethers.Event) => event.event === "MembershipRegistered" - ); - - if (!memberRegistered || !memberRegistered.args) { - log.error( - "Failed to register membership with permit: No MembershipRegistered event found" - ); - return undefined; - } - - const decodedData: MembershipRegisteredEvent = { - idCommitment: memberRegistered.args.idCommitment, - membershipRateLimit: memberRegistered.args.membershipRateLimit, - index: memberRegistered.args.index - }; - - log.info( - `Successfully registered membership with permit. Index: ${decodedData.index}, ` + - `Rate limit: ${decodedData.membershipRateLimit}, Erased ${idCommitmentsToErase.length} commitments` - ); - - const network = await this.contract.provider.getNetwork(); - const address = this.contract.address; - const membershipId = Number(decodedData.index); - - return { - identity, - membership: { - address, - treeIndex: membershipId, - chainId: network.chainId.toString(), - rateLimit: decodedData.membershipRateLimit.toNumber() - } - }; - } catch (error) { - log.error( - `Error in registerWithPermitAndErase: ${(error as Error).message}` - ); - return undefined; - } - } - /** * Validates that the rate limit is within the allowed range (sync) * @throws Error if the rate limit is outside the allowed range @@ -695,50 +490,17 @@ export class RLNBaseContract { } } - private get membersFilter(): ethers.EventFilter { - if (!this._membersFilter) { - throw Error("Members filter was not initialized."); - } - return this._membersFilter; - } - - private get membershipErasedFilter(): ethers.EventFilter { - if (!this._membershipErasedFilter) { - throw Error("MembershipErased filter was not initialized."); - } - return this._membershipErasedFilter; - } - - private get membersExpiredFilter(): ethers.EventFilter { - if (!this._membersExpiredFilter) { - throw Error("MembersExpired filter was not initialized."); - } - return this._membersExpiredFilter; - } - - private async getMemberIndex( - idCommitmentBigInt: bigint - ): Promise { - try { - const events = await this.contract.queryFilter( - this.contract.filters.MembershipRegistered(idCommitmentBigInt) - ); - if (events.length === 0) return undefined; - - // Get the most recent registration event - const event = events[events.length - 1]; - return event.args?.index; - } catch (error) { - return undefined; - } + private async getMemberIndex(idCommitmentBigInt: bigint): Promise { + // Current version of the contract has the index at position 5 in the membership struct + return (await this.contract.read.memberships([idCommitmentBigInt]))[5]; } public async getMembershipStatus( idCommitment: bigint ): Promise<"expired" | "grace" | "active"> { const [isExpired, isInGrace] = await Promise.all([ - this.contract.isExpired(idCommitment), - this.contract.isInGracePeriod(idCommitment) + this.contract.read.isExpired([idCommitment]), + this.contract.read.isInGracePeriod([idCommitment]) ]); if (isExpired) return "expired"; @@ -753,7 +515,7 @@ export class RLNBaseContract { */ public async isExpired(idCommitmentBigInt: bigint): Promise { try { - return await this.contract.isExpired(idCommitmentBigInt); + return await this.contract.read.isExpired([idCommitmentBigInt]); } catch (error) { log.error("Error in isExpired:", error); return false; @@ -767,7 +529,7 @@ export class RLNBaseContract { */ public async isInGracePeriod(idCommitmentBigInt: bigint): Promise { try { - return await this.contract.isInGracePeriod(idCommitmentBigInt); + return await this.contract.read.isInGracePeriod([idCommitmentBigInt]); } catch (error) { log.error("Error in isInGracePeriod:", error); return false; @@ -779,21 +541,18 @@ export class RLNBaseContract { * @param rateLimit The rate limit to calculate the price for * @param contractFactory Optional factory for creating the contract (for testing) */ - public async getPriceForRateLimit( - rateLimit: number, - contractFactory?: typeof import("ethers").Contract - ): Promise<{ + public async getPriceForRateLimit(rateLimit: number): Promise<{ token: string | null; - price: import("ethers").BigNumber | null; + price: bigint | null; }> { - const provider = this.contract.provider; - const ContractCtor = contractFactory || ethers.Contract; - const priceCalculator = new ContractCtor( - PRICE_CALCULATOR_CONTRACT.address, - PRICE_CALCULATOR_CONTRACT.abi, - provider - ); - const [token, price] = await priceCalculator.calculate(rateLimit); + const address = await this.contract.read.priceCalculator(); + const [token, price] = await this.rpcClient.readContract({ + address, + abi: iPriceCalculatorAbi, + functionName: "calculate", + args: [rateLimit] + }); + // Defensive: if token or price is null/undefined, return nulls if (!token || !price) { return { token: null, price: null }; diff --git a/packages/rln/src/contract/types.ts b/packages/rln/src/contract/types.ts index bd0247bb6a..479ed412bb 100644 --- a/packages/rln/src/contract/types.ts +++ b/packages/rln/src/contract/types.ts @@ -1,28 +1,22 @@ -import { ethers } from "ethers"; +import { Address } from "viem"; -export interface CustomQueryOptions extends FetchMembersOptions { - membersFilter: ethers.EventFilter; -} +import { RpcClient } from "../utils/index.js"; export type Member = { idCommitment: string; - index: ethers.BigNumber; + index: bigint; }; export interface RLNContractOptions { - signer: ethers.Signer; - address: string; + rpcClient: RpcClient; + address: Address; rateLimit?: number; } -export interface RLNContractInitOptions extends RLNContractOptions { - contract?: ethers.Contract; -} - export interface MembershipRegisteredEvent { idCommitment: string; - membershipRateLimit: ethers.BigNumber; - index: ethers.BigNumber; + membershipRateLimit: bigint; + index: bigint; } export type FetchMembersOptions = { @@ -32,13 +26,13 @@ export type FetchMembersOptions = { }; export interface MembershipInfo { - index: ethers.BigNumber; + index: number; idCommitment: string; rateLimit: number; startBlock: number; endBlock: number; state: MembershipState; - depositAmount: ethers.BigNumber; + depositAmount: bigint; activeDuration: number; gracePeriodDuration: number; holder: string; diff --git a/packages/rln/src/contract/wagmi/generated.ts b/packages/rln/src/contract/wagmi/generated.ts new file mode 100644 index 0000000000..f13fa0c2ce --- /dev/null +++ b/packages/rln/src/contract/wagmi/generated.ts @@ -0,0 +1,977 @@ +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// IPriceCalculator +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +export const iPriceCalculatorAbi = [ + { + type: "function", + inputs: [{ name: "_rateLimit", internalType: "uint32", type: "uint32" }], + name: "calculate", + outputs: [ + { name: "", internalType: "address", type: "address" }, + { name: "", internalType: "uint256", type: "uint256" } + ], + stateMutability: "view" + } +] as const; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// LinearPriceCalculator +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +export const linearPriceCalculatorAbi = [ + { + type: "constructor", + inputs: [ + { name: "_token", internalType: "address", type: "address" }, + { + name: "_pricePerMessagePerEpoch", + internalType: "uint256", + type: "uint256" + } + ], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [{ name: "_rateLimit", internalType: "uint32", type: "uint32" }], + name: "calculate", + outputs: [ + { name: "", internalType: "address", type: "address" }, + { name: "", internalType: "uint256", type: "uint256" } + ], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "owner", + outputs: [{ name: "", internalType: "address", type: "address" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "pricePerMessagePerEpoch", + outputs: [{ name: "", internalType: "uint256", type: "uint256" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { name: "_token", internalType: "address", type: "address" }, + { + name: "_pricePerMessagePerEpoch", + internalType: "uint256", + type: "uint256" + } + ], + name: "setTokenAndPrice", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [], + name: "token", + outputs: [{ name: "", internalType: "address", type: "address" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [{ name: "newOwner", internalType: "address", type: "address" }], + name: "transferOwnership", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "previousOwner", + internalType: "address", + type: "address", + indexed: true + }, + { + name: "newOwner", + internalType: "address", + type: "address", + indexed: true + } + ], + name: "OwnershipTransferred" + }, + { type: "error", inputs: [], name: "OnlyTokensAllowed" } +] as const; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// MembershipUpgradeable +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +export const membershipUpgradeableAbi = [ + { + type: "function", + inputs: [], + name: "activeDurationForNewMemberships", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "currentTotalRateLimit", + outputs: [{ name: "", internalType: "uint256", type: "uint256" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "holder", internalType: "address", type: "address" }, + { name: "token", internalType: "address", type: "address" } + ], + name: "depositsToWithdraw", + outputs: [{ name: "balance", internalType: "uint256", type: "uint256" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "gracePeriodDurationForNewMemberships", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [{ name: "", internalType: "uint256", type: "uint256" }], + name: "indicesOfLazilyErasedMemberships", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "_idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "isExpired", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "_idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "isInGracePeriod", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [{ name: "rateLimit", internalType: "uint32", type: "uint32" }], + name: "isValidMembershipRateLimit", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "maxMembershipRateLimit", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "maxTotalRateLimit", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "_idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "membershipExpirationTimestamp", + outputs: [{ name: "", internalType: "uint256", type: "uint256" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "memberships", + outputs: [ + { name: "depositAmount", internalType: "uint256", type: "uint256" }, + { name: "activeDuration", internalType: "uint32", type: "uint32" }, + { + name: "gracePeriodStartTimestamp", + internalType: "uint256", + type: "uint256" + }, + { name: "gracePeriodDuration", internalType: "uint32", type: "uint32" }, + { name: "rateLimit", internalType: "uint32", type: "uint32" }, + { name: "index", internalType: "uint32", type: "uint32" }, + { name: "holder", internalType: "address", type: "address" }, + { name: "token", internalType: "address", type: "address" } + ], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "minMembershipRateLimit", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "nextFreeIndex", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "priceCalculator", + outputs: [ + { name: "", internalType: "contract IPriceCalculator", type: "address" } + ], + stateMutability: "view" + }, + { + type: "event", + anonymous: false, + inputs: [ + { name: "version", internalType: "uint8", type: "uint8", indexed: false } + ], + name: "Initialized" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "idCommitment", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { + name: "membershipRateLimit", + internalType: "uint32", + type: "uint32", + indexed: false + }, + { name: "index", internalType: "uint32", type: "uint32", indexed: false } + ], + name: "MembershipErased" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "idCommitment", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { + name: "membershipRateLimit", + internalType: "uint32", + type: "uint32", + indexed: false + }, + { name: "index", internalType: "uint32", type: "uint32", indexed: false } + ], + name: "MembershipExpired" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "idCommitment", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { + name: "membershipRateLimit", + internalType: "uint32", + type: "uint32", + indexed: false + }, + { name: "index", internalType: "uint32", type: "uint32", indexed: false }, + { + name: "newGracePeriodStartTimestamp", + internalType: "uint256", + type: "uint256", + indexed: false + } + ], + name: "MembershipExtended" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "idCommitment", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { + name: "membershipRateLimit", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { name: "index", internalType: "uint32", type: "uint32", indexed: false } + ], + name: "MembershipRegistered" + } +] as const; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// WakuRlnV2 +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +export const wakuRlnV2Abi = [ + { type: "constructor", inputs: [], stateMutability: "nonpayable" }, + { + type: "function", + inputs: [], + name: "MAX_MEMBERSHIP_SET_SIZE", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "MERKLE_TREE_DEPTH", + outputs: [{ name: "", internalType: "uint8", type: "uint8" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "Q", + outputs: [{ name: "", internalType: "uint256", type: "uint256" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "activeDurationForNewMemberships", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "currentTotalRateLimit", + outputs: [{ name: "", internalType: "uint256", type: "uint256" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "deployedBlockNumber", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "holder", internalType: "address", type: "address" }, + { name: "token", internalType: "address", type: "address" } + ], + name: "depositsToWithdraw", + outputs: [{ name: "balance", internalType: "uint256", type: "uint256" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "idCommitments", internalType: "uint256[]", type: "uint256[]" } + ], + name: "eraseMemberships", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { name: "idCommitments", internalType: "uint256[]", type: "uint256[]" }, + { name: "eraseFromMembershipSet", internalType: "bool", type: "bool" } + ], + name: "eraseMemberships", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { name: "idCommitments", internalType: "uint256[]", type: "uint256[]" } + ], + name: "extendMemberships", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "getMembershipInfo", + outputs: [ + { name: "", internalType: "uint32", type: "uint32" }, + { name: "", internalType: "uint32", type: "uint32" }, + { name: "", internalType: "uint256", type: "uint256" } + ], + stateMutability: "view" + }, + { + type: "function", + inputs: [{ name: "index", internalType: "uint40", type: "uint40" }], + name: "getMerkleProof", + outputs: [{ name: "", internalType: "uint256[20]", type: "uint256[20]" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "startIndex", internalType: "uint32", type: "uint32" }, + { name: "endIndex", internalType: "uint32", type: "uint32" } + ], + name: "getRateCommitmentsInRangeBoundsInclusive", + outputs: [{ name: "", internalType: "uint256[]", type: "uint256[]" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "gracePeriodDurationForNewMemberships", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [{ name: "", internalType: "uint256", type: "uint256" }], + name: "indicesOfLazilyErasedMemberships", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "_priceCalculator", internalType: "address", type: "address" }, + { name: "_maxTotalRateLimit", internalType: "uint32", type: "uint32" }, + { + name: "_minMembershipRateLimit", + internalType: "uint32", + type: "uint32" + }, + { + name: "_maxMembershipRateLimit", + internalType: "uint32", + type: "uint32" + }, + { name: "_activeDuration", internalType: "uint32", type: "uint32" }, + { name: "_gracePeriod", internalType: "uint32", type: "uint32" } + ], + name: "initialize", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { name: "_idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "isExpired", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "_idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "isInGracePeriod", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "isInMembershipSet", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "isValidIdCommitment", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "pure" + }, + { + type: "function", + inputs: [{ name: "rateLimit", internalType: "uint32", type: "uint32" }], + name: "isValidMembershipRateLimit", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "maxMembershipRateLimit", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "maxTotalRateLimit", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "_idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "membershipExpirationTimestamp", + outputs: [{ name: "", internalType: "uint256", type: "uint256" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "memberships", + outputs: [ + { name: "depositAmount", internalType: "uint256", type: "uint256" }, + { name: "activeDuration", internalType: "uint32", type: "uint32" }, + { + name: "gracePeriodStartTimestamp", + internalType: "uint256", + type: "uint256" + }, + { name: "gracePeriodDuration", internalType: "uint32", type: "uint32" }, + { name: "rateLimit", internalType: "uint32", type: "uint32" }, + { name: "index", internalType: "uint32", type: "uint32" }, + { name: "holder", internalType: "address", type: "address" }, + { name: "token", internalType: "address", type: "address" } + ], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "merkleTree", + outputs: [ + { name: "maxIndex", internalType: "uint40", type: "uint40" }, + { name: "numberOfLeaves", internalType: "uint40", type: "uint40" } + ], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "minMembershipRateLimit", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "nextFreeIndex", + outputs: [{ name: "", internalType: "uint32", type: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "owner", + outputs: [{ name: "", internalType: "address", type: "address" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "priceCalculator", + outputs: [ + { name: "", internalType: "contract IPriceCalculator", type: "address" } + ], + stateMutability: "view" + }, + { + type: "function", + inputs: [], + name: "proxiableUUID", + outputs: [{ name: "", internalType: "bytes32", type: "bytes32" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" }, + { name: "rateLimit", internalType: "uint32", type: "uint32" }, + { + name: "idCommitmentsToErase", + internalType: "uint256[]", + type: "uint256[]" + } + ], + name: "register", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [], + name: "root", + outputs: [{ name: "", internalType: "uint256", type: "uint256" }], + stateMutability: "view" + }, + { + type: "function", + inputs: [ + { + name: "_activeDurationForNewMembership", + internalType: "uint32", + type: "uint32" + } + ], + name: "setActiveDuration", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { + name: "_gracePeriodDurationForNewMembership", + internalType: "uint32", + type: "uint32" + } + ], + name: "setGracePeriodDuration", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { + name: "_maxMembershipRateLimit", + internalType: "uint32", + type: "uint32" + } + ], + name: "setMaxMembershipRateLimit", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { name: "_maxTotalRateLimit", internalType: "uint32", type: "uint32" } + ], + name: "setMaxTotalRateLimit", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { + name: "_minMembershipRateLimit", + internalType: "uint32", + type: "uint32" + } + ], + name: "setMinMembershipRateLimit", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { name: "_priceCalculator", internalType: "address", type: "address" } + ], + name: "setPriceCalculator", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [{ name: "newOwner", internalType: "address", type: "address" }], + name: "transferOwnership", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { name: "newImplementation", internalType: "address", type: "address" } + ], + name: "upgradeTo", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + inputs: [ + { name: "newImplementation", internalType: "address", type: "address" }, + { name: "data", internalType: "bytes", type: "bytes" } + ], + name: "upgradeToAndCall", + outputs: [], + stateMutability: "payable" + }, + { + type: "function", + inputs: [{ name: "token", internalType: "address", type: "address" }], + name: "withdraw", + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "previousAdmin", + internalType: "address", + type: "address", + indexed: false + }, + { + name: "newAdmin", + internalType: "address", + type: "address", + indexed: false + } + ], + name: "AdminChanged" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "beacon", + internalType: "address", + type: "address", + indexed: true + } + ], + name: "BeaconUpgraded" + }, + { + type: "event", + anonymous: false, + inputs: [ + { name: "version", internalType: "uint8", type: "uint8", indexed: false } + ], + name: "Initialized" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "idCommitment", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { + name: "membershipRateLimit", + internalType: "uint32", + type: "uint32", + indexed: false + }, + { name: "index", internalType: "uint32", type: "uint32", indexed: false } + ], + name: "MembershipErased" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "idCommitment", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { + name: "membershipRateLimit", + internalType: "uint32", + type: "uint32", + indexed: false + }, + { name: "index", internalType: "uint32", type: "uint32", indexed: false } + ], + name: "MembershipExpired" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "idCommitment", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { + name: "membershipRateLimit", + internalType: "uint32", + type: "uint32", + indexed: false + }, + { name: "index", internalType: "uint32", type: "uint32", indexed: false }, + { + name: "newGracePeriodStartTimestamp", + internalType: "uint256", + type: "uint256", + indexed: false + } + ], + name: "MembershipExtended" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "idCommitment", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { + name: "membershipRateLimit", + internalType: "uint256", + type: "uint256", + indexed: false + }, + { name: "index", internalType: "uint32", type: "uint32", indexed: false } + ], + name: "MembershipRegistered" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "previousOwner", + internalType: "address", + type: "address", + indexed: true + }, + { + name: "newOwner", + internalType: "address", + type: "address", + indexed: true + } + ], + name: "OwnershipTransferred" + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "implementation", + internalType: "address", + type: "address", + indexed: true + } + ], + name: "Upgraded" + }, + { + type: "error", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "CannotEraseActiveMembership" + }, + { type: "error", inputs: [], name: "CannotExceedMaxTotalRateLimit" }, + { + type: "error", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "CannotExtendNonGracePeriodMembership" + }, + { + type: "error", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "InvalidIdCommitment" + }, + { type: "error", inputs: [], name: "InvalidMembershipRateLimit" }, + { + type: "error", + inputs: [ + { name: "startIndex", internalType: "uint256", type: "uint256" }, + { name: "endIndex", internalType: "uint256", type: "uint256" } + ], + name: "InvalidPaginationQuery" + }, + { + type: "error", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "MembershipDoesNotExist" + }, + { + type: "error", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "NonHolderCannotEraseGracePeriodMembership" + }, + { + type: "error", + inputs: [ + { name: "idCommitment", internalType: "uint256", type: "uint256" } + ], + name: "NonHolderCannotExtend" + } +] as const; diff --git a/packages/rln/src/credentials_manager.ts b/packages/rln/src/credentials_manager.ts index b82d9421f1..25a61898f2 100644 --- a/packages/rln/src/credentials_manager.ts +++ b/packages/rln/src/credentials_manager.ts @@ -1,5 +1,5 @@ import { Logger } from "@waku/utils"; -import { ethers } from "ethers"; +import { publicActions } from "viem"; import { RLN_CONTRACT } from "./contract/constants.js"; import { RLNBaseContract } from "./contract/rln_base_contract.js"; @@ -10,7 +10,7 @@ import type { } from "./keystore/index.js"; import { KeystoreEntity, Password } from "./keystore/types.js"; import { RegisterMembershipOptions, StartRLNOptions } from "./types.js"; -import { extractMetaMaskSigner } from "./utils/index.js"; +import { createViemClientFromWindow, RpcClient } from "./utils/index.js"; import { Zerokit } from "./zerokit.js"; const log = new Logger("rln:credentials"); @@ -24,7 +24,7 @@ export class RLNCredentialsManager { protected starting = false; public contract: undefined | RLNBaseContract; - public signer: undefined | ethers.Signer; + public rpcClient: undefined | RpcClient; protected keystore = Keystore.create(); public credentials: undefined | DecryptedCredentials; @@ -36,10 +36,6 @@ export class RLNCredentialsManager { this.zerokit = zerokit; } - public get provider(): undefined | ethers.providers.Provider { - return this.contract?.provider; - } - public async start(options: StartRLNOptions = {}): Promise { if (this.started || this.starting) { log.info("RLNCredentialsManager already started or starting"); @@ -59,10 +55,8 @@ export class RLNCredentialsManager { log.info("Credentials successfully decrypted"); } - const { signer, address, rateLimit } = await this.determineStartOptions( - options, - credentials - ); + const { rpcClient, address, rateLimit } = + await this.determineStartOptions(options, credentials); log.info(`Using contract address: ${address}`); @@ -72,10 +66,10 @@ export class RLNCredentialsManager { } this.credentials = credentials; - this.signer = signer!; + this.rpcClient = rpcClient!; this.contract = await RLNBaseContract.create({ - address: address!, - signer: signer!, + address: address! as `0x${string}`, + rpcClient: this.rpcClient, rateLimit: rateLimit ?? this.zerokit.rateLimit }); @@ -134,7 +128,7 @@ export class RLNCredentialsManager { protected async determineStartOptions( options: StartRLNOptions, credentials: KeystoreEntity | undefined - ): Promise { + ): Promise { let chainId = credentials?.membership.chainId; const address = credentials?.membership.address || @@ -146,11 +140,14 @@ export class RLNCredentialsManager { log.info(`Using Linea contract with chainId: ${chainId}`); } - const signer = options.signer || (await extractMetaMaskSigner()); - const currentChainId = await signer.getChainId(); + const rpcClient: RpcClient = options.walletClient + ? options.walletClient.extend(publicActions) + : await createViemClientFromWindow(); + + const currentChainId = rpcClient.chain?.id; log.info(`Current chain ID: ${currentChainId}`); - if (chainId && chainId !== currentChainId.toString()) { + if (chainId && chainId !== currentChainId?.toString()) { log.error( `Chain ID mismatch: contract=${chainId}, current=${currentChainId}` ); @@ -160,7 +157,7 @@ export class RLNCredentialsManager { } return { - signer, + rpcClient, address }; } @@ -206,9 +203,9 @@ export class RLNCredentialsManager { protected async verifyCredentialsAgainstContract( credentials: KeystoreEntity ): Promise { - if (!this.contract) { + if (!this.contract || !this.rpcClient) { throw Error( - "Failed to verify chain coordinates: no contract initialized." + "Failed to verify chain coordinates: no contract or viem client initialized." ); } @@ -221,8 +218,7 @@ export class RLNCredentialsManager { } const chainId = credentials.membership.chainId; - const network = await this.contract.provider.getNetwork(); - const currentChainId = network.chainId; + const currentChainId = await this.rpcClient.getChainId(); if (chainId !== currentChainId.toString()) { throw Error( `Failed to verify chain coordinates: credentials chainID=${chainId} is not equal to registryContract chainID=${currentChainId}` diff --git a/packages/rln/src/index.ts b/packages/rln/src/index.ts index 3348e370f7..b2bd28e3e3 100644 --- a/packages/rln/src/index.ts +++ b/packages/rln/src/index.ts @@ -1,11 +1,10 @@ -import { RLN_ABI } from "./contract/abi/rln.js"; import { RLN_CONTRACT } from "./contract/index.js"; import { RLNBaseContract } from "./contract/rln_base_contract.js"; import { createRLN } from "./create.js"; import { IdentityCredential } from "./identity.js"; import { Keystore } from "./keystore/index.js"; import { RLNInstance } from "./rln.js"; -import { extractMetaMaskSigner } from "./utils/index.js"; +import { createViemClientFromWindow } from "./utils/index.js"; export { RLNBaseContract, @@ -14,10 +13,16 @@ export { RLNInstance, IdentityCredential, RLN_CONTRACT, - extractMetaMaskSigner, - RLN_ABI + createViemClientFromWindow }; +export { + wakuRlnV2Abi, + linearPriceCalculatorAbi, + iPriceCalculatorAbi, + membershipUpgradeableAbi +} from "./contract/wagmi/generated.js"; + export type { DecryptedCredentials, EncryptedCredentials, diff --git a/packages/rln/src/types.ts b/packages/rln/src/types.ts index 7980c8aea9..55497de0a4 100644 --- a/packages/rln/src/types.ts +++ b/packages/rln/src/types.ts @@ -1,4 +1,4 @@ -import { ethers } from "ethers"; +import { WalletClient } from "viem"; import { IdentityCredential } from "./identity.js"; import { @@ -8,9 +8,9 @@ import { export type StartRLNOptions = { /** - * If not set - will extract MetaMask account and get signer from it. + * If not set - will attempt to create from provider injected in window. */ - signer?: ethers.Signer; + walletClient?: WalletClient; /** * If not set - will use default SEPOLIA_CONTRACT address. */ diff --git a/packages/rln/src/utils/index.ts b/packages/rln/src/utils/index.ts index dddc0b8285..a5cf32937e 100644 --- a/packages/rln/src/utils/index.ts +++ b/packages/rln/src/utils/index.ts @@ -1,4 +1,4 @@ -export { extractMetaMaskSigner } from "./metamask.js"; +export { createViemClientFromWindow, RpcClient } from "./rpcClient.js"; export { BytesUtils } from "./bytes.js"; export { sha256, poseidonHash } from "./hash.js"; export { dateToEpoch, epochIntToBytes, epochBytesToInt } from "./epoch.js"; diff --git a/packages/rln/src/utils/metamask.ts b/packages/rln/src/utils/metamask.ts deleted file mode 100644 index 83c7066106..0000000000 --- a/packages/rln/src/utils/metamask.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ethers } from "ethers"; - -export const extractMetaMaskSigner = async (): Promise => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const ethereum = (window as any).ethereum; - - if (!ethereum) { - throw Error( - "Missing or invalid Ethereum provider. Please install a Web3 wallet such as MetaMask." - ); - } - - await ethereum.request({ method: "eth_requestAccounts" }); - const provider = new ethers.providers.Web3Provider(ethereum, "any"); - - return provider.getSigner(); -}; diff --git a/packages/rln/src/utils/rpcClient.ts b/packages/rln/src/utils/rpcClient.ts new file mode 100644 index 0000000000..585adf1a9d --- /dev/null +++ b/packages/rln/src/utils/rpcClient.ts @@ -0,0 +1,57 @@ +import "viem/window"; +import { + type Address, + createWalletClient, + custom, + PublicActions, + publicActions, + WalletClient +} from "viem"; +import { lineaSepolia } from "viem/chains"; + +export type RpcClient = WalletClient & PublicActions; + +/** + * Checks window for injected Ethereum provider, requests user to connect, and creates an RPC client object + * capable of performing both read and write operations on the blockchain. + * + * If the wallet is not connected to the Linea Sepolia network, it will attempt to switch to it. + * If the wallet does not have the Linea Sepolia network added, it will attempt to add it. + */ +export const createViemClientFromWindow = async (): Promise => { + const ethereum = window.ethereum; + + if (!ethereum) { + throw Error( + "Missing or invalid Ethereum provider. Please install a Web3 wallet such as MetaMask." + ); + } + + const [account] = await ethereum.request({ method: "eth_requestAccounts" }); + + const rpcClient: RpcClient = createWalletClient({ + account: account as Address, + chain: lineaSepolia, + transport: custom(window.ethereum!) + }).extend(publicActions); + + // Ensure wallet is connected to Linea Sepolia + try { + await rpcClient.switchChain({ id: lineaSepolia.id }); + } catch (error: unknown) { + // This error code indicates that the chain has not been added to the wallet + if ( + typeof error === "object" && + error !== null && + "code" in error && + error.code === 4902 + ) { + await rpcClient.addChain({ chain: lineaSepolia }); + await rpcClient.switchChain({ id: lineaSepolia.id }); + } else { + throw error; + } + } + + return rpcClient; +}; diff --git a/packages/rln/tsconfig.dev.json b/packages/rln/tsconfig.dev.json index 96c99dc461..d53a621c4f 100644 --- a/packages/rln/tsconfig.dev.json +++ b/packages/rln/tsconfig.dev.json @@ -1,3 +1,4 @@ { - "extends": "../../tsconfig.dev" -} \ No newline at end of file + "extends": "../../tsconfig.dev", + "exclude": ["wagmi.config.ts"] +} diff --git a/packages/rln/tsconfig.json b/packages/rln/tsconfig.json index 6f8756934b..f31944719d 100644 --- a/packages/rln/tsconfig.json +++ b/packages/rln/tsconfig.json @@ -6,5 +6,5 @@ "tsBuildInfoFile": "dist/.tsbuildinfo" }, "include": ["src"], - "exclude": ["src/**/*.spec.ts", "src/test_utils"] -} \ No newline at end of file + "exclude": ["wagmi.config.ts", "src/**/*.spec.ts", "src/test_utils"] +} diff --git a/packages/rln/wagmi.config.ts b/packages/rln/wagmi.config.ts new file mode 100644 index 0000000000..bffe3f0576 --- /dev/null +++ b/packages/rln/wagmi.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from "@wagmi/cli"; +import { foundry } from "@wagmi/cli/plugins"; + +export default defineConfig({ + out: "src/contract/wagmi/generated.ts", + plugins: [ + foundry({ + project: "./waku-rlnv2-contract", + artifacts: "out", + include: [ + "WakuRlnV2.sol/**", + "Membership.sol/**", + "LinearPriceCalculator.sol/**", + "IPriceCalculator.sol/**" + ] + }) + ] +});