diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 31a088e05..0bc9484e7 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -21,7 +21,7 @@ jobs: # TODO - periodically check if conditional services are supported; https://github.com/actions/runner/issues/822 services: devnet: - image: ${{ (inputs.use-devnet) && 'shardlabs/starknet-devnet-rs:0.2.0' || '' }} + image: ${{ (inputs.use-devnet) && 'shardlabs/starknet-devnet-rs:0.2.2' || '' }} ports: - 5050:5050 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e36c8729..ee158e829 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,51 @@ +## [6.20.2](https://github.com/starknet-io/starknet.js/compare/v6.20.1...v6.20.2) (2024-11-28) + +### Bug Fixes + +- bump typejs 0.7.10 ([#1275](https://github.com/starknet-io/starknet.js/issues/1275)) ([edfa6dd](https://github.com/starknet-io/starknet.js/commit/edfa6dd2466933d4c4b8a8dacb7220778b2669fc)) + +## [6.20.1](https://github.com/starknet-io/starknet.js/compare/v6.20.0...v6.20.1) (2024-11-28) + +### Bug Fixes + +- bump types js ([#1273](https://github.com/starknet-io/starknet.js/issues/1273)) ([34dee40](https://github.com/starknet-io/starknet.js/commit/34dee407af2d9487050f3cf6f2969068937960d6)) + +# [6.20.0](https://github.com/starknet-io/starknet.js/compare/v6.19.0...v6.20.0) (2024-11-27) + +### Features + +- add support for parsing emitted events ([#1227](https://github.com/starknet-io/starknet.js/issues/1227)) ([321ecae](https://github.com/starknet-io/starknet.js/commit/321ecaed2af5828501eccb982dc8744ec3e90f1f)) + +# [6.19.0](https://github.com/starknet-io/starknet.js/compare/v6.18.1...v6.19.0) (2024-11-26) + +### Features + +- Ledger signer 221 ([#1246](https://github.com/starknet-io/starknet.js/issues/1246)) ([03e2d50](https://github.com/starknet-io/starknet.js/commit/03e2d50edee7e2e9e8efb678a97effaf09adee70)) + +## [6.18.1](https://github.com/starknet-io/starknet.js/compare/v6.18.0...v6.18.1) (2024-11-25) + +### Bug Fixes + +- expose BatchClient class ([8d3a0de](https://github.com/starknet-io/starknet.js/commit/8d3a0de85230e6f08edebf67b6581d6e79ea191a)) + +# [6.18.0](https://github.com/starknet-io/starknet.js/compare/v6.17.0...v6.18.0) (2024-11-18) + +### Features + +- WalletAccount non-breaking temp solution ([#1259](https://github.com/starknet-io/starknet.js/issues/1259)) ([84b267c](https://github.com/starknet-io/starknet.js/commit/84b267cd20122de2954fe9fd87b50503b19c1baa)) + +# [6.17.0](https://github.com/starknet-io/starknet.js/compare/v6.16.0...v6.17.0) (2024-10-24) + +### Features + +- Cairo u96 implementation ([#1247](https://github.com/starknet-io/starknet.js/issues/1247)) ([06f0a80](https://github.com/starknet-io/starknet.js/commit/06f0a8013405ded4685d0a21c4f245a25e7d4827)) + +# [6.16.0](https://github.com/starknet-io/starknet.js/compare/v6.15.0...v6.16.0) (2024-10-24) + +### Features + +- increase rpc error information propagation ([#1213](https://github.com/starknet-io/starknet.js/issues/1213)) ([947e8b4](https://github.com/starknet-io/starknet.js/commit/947e8b457d08a7533139072b911ace4e382988ae)) + # [6.15.0](https://github.com/starknet-io/starknet.js/compare/v6.14.1...v6.15.0) (2024-10-16) ### Features diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db79f3f9d..6803fa0bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -111,4 +111,4 @@ For major changes that markedly transform the existing API or significantly alte If you want to contribute but have any questions, concerns or doubts, feel free to ping maintainers. Ideally create a pull request with `WIP` (Work in progress) in its title and ask questions in the pull request description. -You can also ask your query on our dedicated channel for [Starknet.js](https://discord.com/channels/793094838509764618/927918707613786162) on the [Starknet Discord](https://discord.com/invite/YgsdxEx3) +You can also ask your query on our dedicated channel for [Starknet.js](https://discord.com/channels/793094838509764618/1270119831559078061) on the [Starknet Discord](https://discord.com/invite/Ft6Xtzdg) diff --git a/README.md b/README.md index 8e89567a1..05405f092 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ Play with [Code Examples](https://github.com/PhilippeR26/starknet.js-workshop-ty If you consider to contribute to this project please read [CONTRIBUTING.md](https://github.com/starknet-io/starknet.js/blob/main/CONTRIBUTING.md) first. -You can also join our dedicated channel for [Starknet.js](https://discord.com/channels/793094838509764618/927918707613786162) on the [Starknet Discord](https://discord.com/invite/YgsdxEx3) +You can also join our dedicated channel for [Starknet.js](https://discord.com/channels/793094838509764618/1270119831559078061) on the [Starknet Discord](https://discord.com/invite/Ft6Xtzdg) ## ❤️ Special Thanks diff --git a/__mocks__/cairo/cairo282/u96.cairo b/__mocks__/cairo/cairo282/u96.cairo new file mode 100644 index 000000000..726ff70d7 --- /dev/null +++ b/__mocks__/cairo/cairo282/u96.cairo @@ -0,0 +1,27 @@ +use core::circuit::u96; + +#[starknet::interface] +trait ITestU96 { + //fn test_u384(self:@TContractState)->u384; + fn test_u96(self: @TContractState, inp: u96) -> u96; +} + +#[starknet::contract] +mod test_u96 { + use core::circuit::u96; + + #[storage] + struct Storage { + gift_id: u128, + } + + #[abi(embed_v0)] + impl TestU96 of super::ITestU96 { + fn test_u96(self: @ContractState, inp: u96) -> u96 { + let a: felt252 = inp.into(); + let b = a + 1; + let c: u96 = b.try_into().unwrap(); + c + } + } +} diff --git a/__mocks__/cairo/cairo282/u96.casm b/__mocks__/cairo/cairo282/u96.casm new file mode 100644 index 000000000..e51b557fa --- /dev/null +++ b/__mocks__/cairo/cairo282/u96.casm @@ -0,0 +1 @@ +{"prime":"0x800000000000011000000000000000000000000000000000000000000000001","compiler_version":"2.8.2","bytecode":["0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xb1","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480a7ffc7fff8000","0x10780017fff7fff","0x8","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x86","0x480080007fff8000","0xa0680017fff8000","0x12","0x4824800180007ffe","0x1000000000000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080007ff57fff","0x482480017ffe8000","0xf0000000ffffffddffffffffffffffff","0x480080017ff37fff","0x400080027ff27ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x71","0x402780017fff7fff","0x1","0x400080007ff87ffe","0x482480017ffe8000","0xffffffff000000000000000000000000","0x400080017ff77fff","0x482480017ff78000","0x2","0x48307ff880007ff9","0x20680017fff7fff","0x4","0x10780017fff7fff","0x10","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ff17fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x78","0x482480017fff8000","0x77","0x480080007fff8000","0xa0680017fff8000","0x9","0x4824800180007fef","0x0","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff77fff","0x10780017fff7fff","0x39","0x4824800180007fef","0x0","0x400080007ff87fff","0x482480017ff58000","0x1","0xa0680017fff8000","0x12","0x4824800180007ffe","0x1000000000000000000000000","0x4844800180008002","0x8000000000000110000000000000000","0x4830800080017ffe","0x480080017ff37fff","0x482480017ffe8000","0xf0000000ffffffddffffffffffffffff","0x480080027ff17fff","0x400080037ff07ffb","0x402480017fff7ffb","0xffffffffffffffffffffffffffffffff","0x20680017fff7fff","0x15","0x402780017fff7fff","0x1","0x400080017ff67ffe","0x482480017ffe8000","0xffffffff000000000000000000000000","0x400080027ff57fff","0x40780017fff7fff","0x1","0x400080007fff7ffc","0x482480017ff48000","0x3","0x48127ffa7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x482480017ffa8000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7074696f6e3a3a756e77726170206661696c65642e","0x400080007ffe7fff","0x482480017fee8000","0x4","0x48127ff47fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff58000","0x1","0x48127fea7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x482480017ff28000","0x3","0x10780017fff7fff","0x5","0x40780017fff7fff","0x8","0x48127ff27fff8000","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x48127ffd7fff8000","0x48127fed7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe"],"bytecode_segment_lengths":[197],"hints":[[0,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[34,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-1},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x1000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[38,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[63,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[82,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"AP","offset":-16}},"dst":{"register":"AP","offset":0}}}]],[96,[{"TestLessThan":{"lhs":{"BinOp":{"op":"Add","a":{"register":"AP","offset":-1},"b":{"Immediate":"0x0"}}},"rhs":{"Immediate":"0x1000000000000000000000000"},"dst":{"register":"AP","offset":0}}}]],[100,[{"LinearSplit":{"value":{"Deref":{"register":"AP","offset":-1}},"scalar":{"Immediate":"0x8000000000000110000000000000000"},"max_x":{"Immediate":"0xfffffffffffffffffffffffffffffffe"},"x":{"register":"AP","offset":0},"y":{"register":"AP","offset":1}}}]],[118,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[131,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[146,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[168,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[182,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]]],"entry_points_by_type":{"EXTERNAL":[{"selector":"0x1853d8ddb27dca06517f780f15cc4dcc7d1c02edae57fec4b58627bbf65aaaa","offset":0,"builtins":["range_check"]}],"L1_HANDLER":[],"CONSTRUCTOR":[]}} \ No newline at end of file diff --git a/__mocks__/cairo/cairo282/u96.sierra.json b/__mocks__/cairo/cairo282/u96.sierra.json new file mode 100644 index 000000000..a2569cd36 --- /dev/null +++ b/__mocks__/cairo/cairo282/u96.sierra.json @@ -0,0 +1,328 @@ +{ + "sierra_program": [ + "0x1", + "0x6", + "0x0", + "0x2", + "0x8", + "0x2", + "0x8b", + "0x75", + "0x15", + "0x52616e6765436865636b", + "0x800000000000000100000000000000000000000000000000", + "0x436f6e7374", + "0x800000000000000000000000000000000000000000000002", + "0x1", + "0x13", + "0x2", + "0x4661696c656420746f20646573657269616c697a6520706172616d202331", + "0x4f7574206f6620676173", + "0x4f7074696f6e3a3a756e77726170206661696c65642e", + "0x4172726179", + "0x800000000000000300000000000000000000000000000001", + "0x536e617073686f74", + "0x800000000000000700000000000000000000000000000001", + "0x4", + "0x537472756374", + "0x800000000000000700000000000000000000000000000002", + "0x0", + "0x1baeba72e79e9db2587cf44fedb2f3700b2075a5e8e39a562584862c4b71f62", + "0x5", + "0x2ee1e2b1b89f8c495f200e4956278a4d47395fe262f27b52e5865c9524c08c3", + "0x6", + "0x4275696c74696e436f737473", + "0x800000000000000700000000000000000000000000000000", + "0x53797374656d", + "0x800000000000000f00000000000000000000000000000001", + "0x16a4c8d7c05909052238a862d8cc3e7975bf05a07b3a69c6b28951083a6d672", + "0x800000000000000300000000000000000000000000000003", + "0xb", + "0x456e756d", + "0x9931c641b913035ae674b400b61a51476d506bbe8bba2ff8a6272790aba9e6", + "0x7", + "0xc", + "0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473", + "0x426f756e646564496e74", + "0xffffffffffffffffffffffff", + "0x426f78", + "0x800000000000000700000000000000000000000000000003", + "0x29d7d57c04a880978e7b3689f6218e507f3be17588744b58dc17762447ad0e7", + "0x11", + "0x10", + "0x66656c74323532", + "0x4761734275696c74696e", + "0x2f", + "0x7265766f6b655f61705f747261636b696e67", + "0x77697468647261775f676173", + "0x6272616e63685f616c69676e", + "0x7374727563745f6465636f6e737472756374", + "0x656e61626c655f61705f747261636b696e67", + "0x73746f72655f74656d70", + "0x61727261795f736e617073686f745f706f705f66726f6e74", + "0x656e756d5f696e6974", + "0x12", + "0x6a756d70", + "0x7374727563745f636f6e737472756374", + "0x656e756d5f6d61746368", + "0x756e626f78", + "0x72656e616d65", + "0x646f776e63617374", + "0xf", + "0x64697361626c655f61705f747261636b696e67", + "0x64726f70", + "0x61727261795f6e6577", + "0x636f6e73745f61735f696d6d656469617465", + "0xe", + "0x61727261795f617070656e64", + "0xd", + "0x14", + "0xa", + "0x6765745f6275696c74696e5f636f737473", + "0x9", + "0x77697468647261775f6761735f616c6c", + "0x757063617374", + "0x8", + "0x66656c743235325f616464", + "0x736e617073686f745f74616b65", + "0x3", + "0x83", + "0xffffffffffffffff", + "0x75", + "0x64", + "0x16", + "0x60", + "0x17", + "0x18", + "0x19", + "0x2b", + "0x1a", + "0x1b", + "0x1c", + "0x1d", + "0x1e", + "0x1f", + "0x20", + "0x21", + "0x22", + "0x23", + "0x52", + "0x24", + "0x25", + "0x26", + "0x27", + "0x28", + "0x29", + "0x2a", + "0x45", + "0x2c", + "0x2d", + "0x2e", + "0x30", + "0x31", + "0x32", + "0x33", + "0x34", + "0x35", + "0x36", + "0x37", + "0x38", + "0x39", + "0x3a", + "0x3b", + "0x3c", + "0x3d", + "0x3e", + "0x3f", + "0x40", + "0x68", + "0x41", + "0x42", + "0x43", + "0x44", + "0x46", + "0x47", + "0x48", + "0x49", + "0x4a", + "0x4b", + "0x4c", + "0x523", + "0x11100f050e0d06050c0b0a0706050403090706050403080706050403020100", + "0x51d0515121c101b121a100219181705070605040316051512111014051312", + "0x261e06050e2515121a10240712071123220706050403210520051f121c1e0f", + "0x605053312050532123116050530122f122e122d2c022b182a290528052712", + "0x39350505380507350507342905053712363505053214050532120735050734", + "0x3c05053e2805053e1405053e123d3c050605073b060505320605053a060505", + "0x3244050532050743050734210505371d05053706050542410505400605053f", + "0xf05054c124b4a05054006053c050749124847050532124643050532450505", + "0x402905053e070505404d05054012074305073420050537160505370f05053e", + "0x12071220160750140f074f070512070512124f051212124e1605053e050505", + "0x4f074a0516120f054f050f051412124f05120f124a054f054d054d12124f05", + "0x5471243054f0547054a1221054f0545052012124f051207121d0521454707", + "0x54f053c0521123c054f05121d12124f0512071212280512451241054f0521", + "0x12071235055128054f074105431241054f052905471243054f051d054a1229", + "0xf07281244054f054405291244054f0506053c1206054f0528054112124f05", + "0x55074f074305161200054f0500051412124f051207125405535200074f0744", + "0x12124f0556054412124f0555050612124f05123512124f0512071258055756", + "0x755125a054f055a0529125a054f0512541259054f05125212124f05520500", + "0x125e054f055d0559125d054f055b5c0758125c054f051256125b054f055a59", + "0x5e054f055e055c1207054f0507055b1214054f0514055a1200054f05000514", + "0x54f05125d12124f0558050612124f05123512124f051207125e0714000f05", + "0x12124f05120712646307626160074f075f14004d5f125f054f055f055e125f", + "0x67054f056705291267054f05666507631266054f0512611265054f05520560", + "0x5690560126b054f05125212124f0512071257056a6968074f076760072812", + "0x56612124f052c0565126e2c074f056d0564126d054f056c6b0755126c054f", + "0x5a1268054f056805141271054f057005681270054f056f0567126f054f056e", + "0x120712710761680f0571054f0571055c1207054f0507055b1261054f056105", + "0x5737207551273054f057305291273054f0512691272054f05125212124f05", + "0x5705141277054f057605591276054f05747507581275054f0512561274054f", + "0x570f0577054f0577055c1207054f0507055b1261054f0561055a1257054f05", + "0x1279054f0512571278054f05125212124f0552050012124f05120712770761", + "0x54f057a7b0758127b054f051256127a054f05797807551279054f05790529", + "0x4f0507055b1264054f0564055a1263054f05630514127d054f057c0559127c", + "0x1412124f0543050612124f051207127d0764630f057d054f057d055c120705", + "0x4f0543050612124f0535056b12124f05120712127f051245127e054f055405", + "0x291281054f05126c1280054f05125212124f051235127e054f050f05141212", + "0x6a054f05828307581283054f0512561282054f05818007551281054f058105", + "0x54f0507055b1214054f0514055a127e054f057e05141284054f056a055912", + "0x125212124f054d056d12124f051207128407147e0f0584054f0584055c1207", + "0x12561287054f05868507551286054f058605291286054f0512571285054f05", + "0x5a1216054f05160514128a054f058905591289054f05878807581288054f05", + "0x120f058a0720160f058a054f058a055c1207054f0507055b1220054f052005", + "0x124d070512434544120f164544" + ], + "sierra_program_debug_info": { + "type_names": [ + [0, "RangeCheck"], + [ + 1, + "Const" + ], + [2, "Const"], + [3, "Const"], + [4, "Array"], + [5, "Snapshot>"], + [6, "core::array::Span::"], + [7, "Tuple>"], + [8, "Const"], + [9, "BuiltinCosts"], + [10, "System"], + [11, "core::panics::Panic"], + [12, "Tuple>"], + [13, "core::panics::PanicResult::<(core::array::Span::,)>"], + [14, "Const"], + [15, "BoundedInt<0, 79228162514264337593543950335>"], + [16, "Unit"], + [17, "Box"], + [18, "core::option::Option::>"], + [19, "felt252"], + [20, "GasBuiltin"] + ], + "libfunc_names": [ + [0, "revoke_ap_tracking"], + [1, "withdraw_gas"], + [2, "branch_align"], + [3, "struct_deconstruct>"], + [4, "enable_ap_tracking"], + [5, "store_temp"], + [6, "array_snapshot_pop_front"], + [7, "enum_init>, 0>"], + [8, "store_temp>>"], + [9, "store_temp>>"], + [10, "jump"], + [11, "struct_construct"], + [12, "enum_init>, 1>"], + [13, "enum_match>>"], + [14, "unbox"], + [15, "rename"], + [16, "store_temp"], + [17, "downcast>"], + [18, "disable_ap_tracking"], + [19, "drop>>"], + [20, "drop>"], + [21, "drop>"], + [22, "array_new"], + [ + 23, + "const_as_immediate>" + ], + [24, "array_append"], + [25, "struct_construct"], + [26, "struct_construct>>"], + [27, "enum_init,)>, 1>"], + [28, "store_temp"], + [29, "store_temp"], + [30, "store_temp,)>>"], + [31, "get_builtin_costs"], + [32, "store_temp"], + [33, "withdraw_gas_all"], + [34, "upcast, felt252>"], + [35, "const_as_immediate>"], + [36, "felt252_add"], + [37, "snapshot_take>"], + [38, "drop>"], + [39, "struct_construct>"], + [40, "struct_construct>>"], + [41, "enum_init,)>, 0>"], + [ + 42, + "const_as_immediate>" + ], + [43, "const_as_immediate>"], + [44, "drop"], + [ + 45, + "const_as_immediate>" + ], + [46, "drop>"] + ], + "user_func_names": [[0, "test_u96::test_u96::__wrapper__TestU96__test_u96"]] + }, + "contract_class_version": "0.1.0", + "entry_points_by_type": { + "EXTERNAL": [ + { + "selector": "0x1853d8ddb27dca06517f780f15cc4dcc7d1c02edae57fec4b58627bbf65aaaa", + "function_idx": 0 + } + ], + "L1_HANDLER": [], + "CONSTRUCTOR": [] + }, + "abi": [ + { + "type": "impl", + "name": "TestU96", + "interface_name": "test_u96::ITestU96" + }, + { + "type": "interface", + "name": "test_u96::ITestU96", + "items": [ + { + "type": "function", + "name": "test_u96", + "inputs": [ + { + "name": "inp", + "type": "core::internal::bounded_int::BoundedInt::<0, 79228162514264337593543950335>" + } + ], + "outputs": [ + { + "type": "core::internal::bounded_int::BoundedInt::<0, 79228162514264337593543950335>" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "test_u96::test_u96::Event", + "kind": "enum", + "variants": [] + } + ] +} diff --git a/__tests__/account.test.ts b/__tests__/account.test.ts index fc1916225..1d53bfcfd 100644 --- a/__tests__/account.test.ts +++ b/__tests__/account.test.ts @@ -6,6 +6,7 @@ import { Contract, DeclareDeployUDCResponse, Provider, + RpcError, TransactionType, cairo, constants, @@ -366,12 +367,13 @@ describe('deploy and test Wallet', () => { }; const details = { maxFee: 0n }; - await expect(account.execute(transaction, details)).rejects.toThrow( - /zero|Transaction must commit to pay a positive amount on fee./ - ); - await expect(account.execute(transaction, undefined, details)).rejects.toThrow( - /zero|Transaction must commit to pay a positive amount on fee./ - ); + const error1: RpcError = await account.execute(transaction, details).catch((e) => e); + expect(error1).toBeInstanceOf(RpcError); + expect(error1.isType('INSUFFICIENT_MAX_FEE')).toBe(true); + + const error2: RpcError = await account.execute(transaction, undefined, details).catch((e) => e); + expect(error2).toBeInstanceOf(RpcError); + expect(error2.isType('INSUFFICIENT_MAX_FEE')).toBe(true); }); test('execute with custom nonce', async () => { diff --git a/__tests__/cairo1v2.test.ts b/__tests__/cairo1v2.test.ts index f8a1bda40..c49069dd2 100644 --- a/__tests__/cairo1v2.test.ts +++ b/__tests__/cairo1v2.test.ts @@ -1,5 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; + import { Account, BigNumberish, @@ -864,7 +865,7 @@ describe('Cairo 1', () => { ]; const tx = await provider.waitForTransaction(transaction_hash); const myEvents = eventContract.parseEvents(tx); - return expect(myEvents).toStrictEqual(shouldBe); + expect(myEvents[0]).toMatchEventStructure(shouldBe[0]); }); test('parse event returning a nested struct', async () => { @@ -882,7 +883,7 @@ describe('Cairo 1', () => { ]; const tx = await provider.waitForTransaction(transaction_hash); const myEvents = eventContract.parseEvents(tx); - return expect(myEvents).toStrictEqual(shouldBe); + expect(myEvents[0]).toMatchEventStructure(shouldBe[0]); }); test('parse tx returning multiple similar events', async () => { @@ -928,7 +929,8 @@ describe('Cairo 1', () => { const { transaction_hash } = await account.execute([callData1, callData2]); const tx = await provider.waitForTransaction(transaction_hash); const myEvents = eventContract.parseEvents(tx); - return expect(myEvents).toStrictEqual(shouldBe); + expect(myEvents[0]).toMatchEventStructure(shouldBe[0]); + expect(myEvents[1]).toMatchEventStructure(shouldBe[1]); }); test('parse tx returning multiple different events', async () => { const shouldBe: types.ParsedEvents = [ @@ -964,7 +966,8 @@ describe('Cairo 1', () => { const { transaction_hash } = await account.execute([callData1, callData2]); const tx = await provider.waitForTransaction(transaction_hash); const myEvents = eventContract.parseEvents(tx); - return expect(myEvents).toStrictEqual(shouldBe); + expect(myEvents[0]).toMatchEventStructure(shouldBe[0]); + expect(myEvents[1]).toMatchEventStructure(shouldBe[1]); }); test('parsing nested events from Cairo components', () => { @@ -1033,6 +1036,9 @@ describe('Cairo 1', () => { maker_source: 418413900385n, taker_source: 418413900385n, }, + block_hash: '0x39f27ab4cd508ab99e818512b261a7e4ae01072eb4ec8bb86aeb64755f99f2c', + block_number: 69198, + transaction_hash: '0x4e38fcce79c115b6fe2c486e3514efc1bd4da386b91c104e97230177d0bf181', }, ]); // From component `DepositComponent`, event `Deposit` (same event name than next) @@ -1085,6 +1091,9 @@ describe('Cairo 1', () => { funder: 1466771120193999006693452314154095230636738457276435850562375218974960297344n, amount: 4956000000000000n, }, + block_hash: '0x31afd649a5042cb1855ce820708a555eab62fe6ea07a2a538fa9100cdc80383', + block_number: 69198, + transaction_hash: '0x7768860d79bfb4c8463d215abea3c267899e373407c6882077f7447051c50de', }, ]); const parsedEventNestedDeposit2 = events.parseEvents( @@ -1101,6 +1110,9 @@ describe('Cairo 1', () => { funder: 1466771120193999006693452314154095230636738457276435850562375218974960297344n, amount: 4956000000000000n, }, + block_hash: '0x39f27ab4cd508ab99e818512b261a7e4ae01072eb4ec8bb86aeb64755f99f2c', + block_number: 69198, + transaction_hash: '0x2d5210e5334a83306abe6f7f5e7e65cd1feed72ad3b8e359a2f4614fa948e1d', }, ]); @@ -1125,6 +1137,9 @@ describe('Cairo 1', () => { to: 2087021424722619777119509474943472645767659996348769578120564519014510906823n, value: 4956000000000000n, }, + block_hash: '0x39f27ab4cd508ab99e818512b261a7e4ae01072eb4ec8bb86aeb64755f99f2c', + block_number: 69198, + transaction_hash: '0x2da31a929a9848e9630906275a75a531e1718d4830501e10b0bccacd55f6fe0', }, ]); }); diff --git a/__tests__/cairo1v2_typed.test.ts b/__tests__/cairo1v2_typed.test.ts index d6a2777dc..5feefc07b 100644 --- a/__tests__/cairo1v2_typed.test.ts +++ b/__tests__/cairo1v2_typed.test.ts @@ -806,7 +806,7 @@ describe('Cairo 1', () => { ]; const tx = await provider.waitForTransaction(transaction_hash); const events = eventContract.parseEvents(tx); - return expect(events).toStrictEqual(shouldBe); + expect(events[0]).toMatchEventStructure(shouldBe[0]); }); test('parse event returning a nested struct', async () => { @@ -824,8 +824,7 @@ describe('Cairo 1', () => { ]; const tx = await provider.waitForTransaction(transaction_hash); const events = eventContract.parseEvents(tx); - - return expect(events).toStrictEqual(shouldBe); + expect(events[0]).toMatchEventStructure(shouldBe[0]); }); test('parse tx returning multiple similar events', async () => { @@ -871,7 +870,8 @@ describe('Cairo 1', () => { const { transaction_hash } = await account.execute([callData1, callData2]); const tx = await provider.waitForTransaction(transaction_hash); const events = eventContract.parseEvents(tx); - return expect(events).toStrictEqual(shouldBe); + expect(events[0]).toMatchEventStructure(shouldBe[0]); + expect(events[1]).toMatchEventStructure(shouldBe[1]); }); test('parse tx returning multiple different events', async () => { const shouldBe: types.ParsedEvents = [ @@ -907,7 +907,8 @@ describe('Cairo 1', () => { const { transaction_hash } = await account.execute([callData1, callData2]); const tx = await provider.waitForTransaction(transaction_hash); const events = eventContract.parseEvents(tx); - return expect(events).toStrictEqual(shouldBe); + expect(events[0]).toMatchEventStructure(shouldBe[0]); + expect(events[1]).toMatchEventStructure(shouldBe[1]); }); }); diff --git a/__tests__/cairov24onward.test.ts b/__tests__/cairov24onward.test.ts index 9898109d8..267b1bf65 100644 --- a/__tests__/cairov24onward.test.ts +++ b/__tests__/cairov24onward.test.ts @@ -208,7 +208,7 @@ describe('Cairo v2.4 onwards', () => { }); }); - describe('Cairo2.6.0 Sierra1.5.0', () => { + describe('Cairo v2.6.0 Sierra1.5.0', () => { test('declare Sierra 1.5.0', async () => { const declare260Response = await account.declareIfNot({ contract: contracts.C260.sierra, @@ -375,4 +375,36 @@ describe('Cairo v2.4 onwards', () => { await expect(nonZeroContract.call('send_nonZero_u256', [myU512])).rejects.toThrow(); }); }); + + describe('Cairo v2.8.2 u96', () => { + let u96Contract: Contract; + + beforeAll(async () => { + const { deploy } = await account.declareAndDeploy({ + contract: contracts.U96.sierra, + casm: contracts.U96.casm, + }); + u96Contract = new Contract(contracts.U96.sierra.abi, deploy.contract_address, account); + }); + + test('u96 compile', async () => { + const myU96: bigint = 2n ** 90n; + const expectedValue = '1237940039285380274899124224'; + const myCalldata1 = CallData.compile([myU96]); + expect(myCalldata1).toEqual([expectedValue]); + const myCallData = new CallData(u96Contract.abi); + const myCalldata = myCallData.compile('test_u96', { + inp: myU96, + }); + expect(myCalldata).toEqual([expectedValue]); + const myCall = u96Contract.populate('test_u96', { inp: myU96 }); + expect(myCall.calldata).toEqual([expectedValue]); + }); + + test('u96 call', async () => { + const value = 2n ** 80n; + const res0 = await u96Contract.call('test_u96', [value]); + expect(res0).toBe(value + 1n); + }); + }); }); diff --git a/__tests__/config/customMatchers.ts b/__tests__/config/customMatchers.ts new file mode 100644 index 000000000..32cc0db51 --- /dev/null +++ b/__tests__/config/customMatchers.ts @@ -0,0 +1,32 @@ +// @ts-nocheck + +declare global { + namespace jest { + interface Matchers { + toMatchEventStructure(expected: any): R; + } + } +} + +const customMatchers = { + toMatchEventStructure(received: any, expected: any): any { + const { block_hash, block_number, transaction_hash, ...eventData } = received; + + // Check if required properties exist + const hasRequiredProps = block_hash && block_number && transaction_hash; + + // Check if event data matches + const eventDataMatches = this.equals(eventData, expected); + + return { + actual: received, + pass: hasRequiredProps && eventDataMatches, + message: () => + `Expected event to match structure with dynamic properties.\n\n` + + `Expected: ${this.utils.printExpected(expected)}\n` + + `Received: ${this.utils.printReceived(eventData)}`, + }; + }, +}; + +export default customMatchers; diff --git a/__tests__/config/fixtures.ts b/__tests__/config/fixtures.ts index 73bb0253e..68e5f674c 100644 --- a/__tests__/config/fixtures.ts +++ b/__tests__/config/fixtures.ts @@ -75,6 +75,7 @@ const compiledContracts = { }, 'starknetId' ), + U96: 'cairo282/u96', }; export const contracts = mapContractSets(compiledContracts); diff --git a/__tests__/config/jest.setup.ts b/__tests__/config/jest.setup.ts index 21078680d..b6c79fb8a 100644 --- a/__tests__/config/jest.setup.ts +++ b/__tests__/config/jest.setup.ts @@ -9,6 +9,12 @@ import 'isomorphic-fetch'; /* eslint-disable no-console */ import { register } from 'fetch-intercept'; +import customMatchers from './customMatchers'; + +beforeAll(() => { + expect.extend(customMatchers); +}); + const util = require('util'); jest.setTimeout(50 * 60 * 1000); diff --git a/__tests__/contract.test.ts b/__tests__/contract.test.ts index 2ca4fb950..229176c24 100644 --- a/__tests__/contract.test.ts +++ b/__tests__/contract.test.ts @@ -4,10 +4,10 @@ import { ContractFactory, ParsedEvents, RawArgs, + SuccessfulTransactionReceiptResponse, json, shortString, stark, - SuccessfulTransactionReceiptResponse, } from '../src'; import { CallData } from '../src/utils/calldata'; import { felt, isCairo1Abi, tuple, uint256 } from '../src/utils/calldata/cairo'; @@ -144,7 +144,7 @@ describe('contract module', () => { }, }, ]; - return expect(events).toStrictEqual(shouldBe); + expect(events[0]).toMatchEventStructure(shouldBe[0]); }); }); diff --git a/__tests__/rpcChannel.test.ts b/__tests__/rpcChannel.test.ts index f82ee7ab5..8d0a0d01a 100644 --- a/__tests__/rpcChannel.test.ts +++ b/__tests__/rpcChannel.test.ts @@ -1,4 +1,4 @@ -import { RPC07 } from '../src'; +import { LibraryError, RPC07, RpcError } from '../src'; import { createBlockForDevnet, getTestProvider } from './config/fixtures'; import { initializeMatcher } from './config/schema'; @@ -15,4 +15,29 @@ describe('RPC 0.7.0', () => { const response = await channel.getBlockWithReceipts('latest'); expect(response).toMatchSchemaRef('BlockWithTxReceipts'); }); + + test('RPC error handling', async () => { + const fetchSpy = jest.spyOn(channel, 'fetch'); + fetchSpy.mockResolvedValue({ + json: async () => ({ + jsonrpc: '2.0', + error: { + code: 24, + message: 'Block not found', + }, + id: 0, + }), + } as any); + + expect.assertions(3); + try { + // @ts-expect-error + await channel.fetchEndpoint('starknet_chainId'); + } catch (error) { + expect(error).toBeInstanceOf(LibraryError); + expect(error).toBeInstanceOf(RpcError); + expect((error as RpcError).isType('BLOCK_NOT_FOUND')).toBe(true); + } + fetchSpy.mockRestore(); + }); }); diff --git a/__tests__/utils/calldata/cairo.test.ts b/__tests__/utils/calldata/cairo.test.ts index 4b7aaf7f7..8e42bcee9 100644 --- a/__tests__/utils/calldata/cairo.test.ts +++ b/__tests__/utils/calldata/cairo.test.ts @@ -26,6 +26,7 @@ import { getAbiContractVersion, tuple, felt, + isTypeU96, } from '../../../src/utils/calldata/cairo'; import { ETH_ADDRESS, Literal, Uint, type ContractVersion, NON_ZERO_PREFIX } from '../../../src'; import { @@ -326,3 +327,15 @@ describe('felt', () => { expect(felts).toEqual(['1952805748', '256', '1234']); }); }); + +describe('u96', () => { + test('should return true if given type is u96', () => { + expect( + isTypeU96('core::internal::bounded_int::BoundedInt::<0, 79228162514264337593543950335>') + ).toEqual(true); + }); + + test('should return false if given type is not u96', () => { + expect(isTypeU96('core::bool')).toEqual(false); + }); +}); diff --git a/__tests__/utils/errors.test.ts b/__tests__/utils/errors.test.ts new file mode 100644 index 000000000..fd50aff21 --- /dev/null +++ b/__tests__/utils/errors.test.ts @@ -0,0 +1,22 @@ +import { RPC, RpcError } from '../../src'; + +describe('Error utility tests', () => { + test('RpcError', () => { + const baseError: RPC.Errors.UNEXPECTED_ERROR = { + code: 63, + message: 'An unexpected error occurred', + data: 'data', + }; + const method = 'GET'; + const error = new RpcError(baseError, method, method); + + expect(error.baseError).toBe(baseError); + expect(error.message).toMatch(/^RPC: \S+ with params \S+/); + expect(error.code).toEqual(baseError.code); + expect(error.request.method).toEqual(method); + expect(error.request.params).toEqual(method); + + expect(error.isType('BLOCK_NOT_FOUND')).toBe(false); + expect(error.isType('UNEXPECTED_ERROR')).toBe(true); + }); +}); diff --git a/__tests__/utils/ethSigner.test.ts b/__tests__/utils/ethSigner.test.ts index 8a1e8e986..a806c14aa 100644 --- a/__tests__/utils/ethSigner.test.ts +++ b/__tests__/utils/ethSigner.test.ts @@ -10,7 +10,8 @@ import { encode, eth, extractContractHashes, - getLedgerPathBuffer, + getLedgerPathBuffer111, + getLedgerPathBuffer221, hash, num, stark, @@ -354,12 +355,25 @@ describe('Ethereum signer', () => { describe('Ledger Signer', () => { // signature of Ledger can't be tested automatically. // So, just the test of the path encoding. - test('getLedgerPathBuffer', () => { - const path = getLedgerPathBuffer(3, 'AstroAPP'); + + // Ledger APP v1.1.1 + test('getLedgerPathBuffer111', () => { + const path = getLedgerPathBuffer111(3, 'AstroAPP'); expect(path).toEqual( new Uint8Array([ 128, 0, 10, 85, 71, 65, 233, 201, 95, 192, 123, 107, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, ]) ); }); + + // Ledger APP v2.2.1 + test('getLedgerPathBuffer', () => { + const path = getLedgerPathBuffer221(3, 'AstroAPP'); + expect(path).toEqual( + new Uint8Array([ + 128, 0, 10, 85, 199, 65, 233, 201, 223, 192, 123, 107, 128, 0, 0, 0, 128, 0, 0, 3, 0, 0, 0, + 0, + ]) + ); + }); }); diff --git a/__tests__/utils/events.test.ts b/__tests__/utils/events.test.ts index c2dcadf89..e3d61824c 100644 --- a/__tests__/utils/events.test.ts +++ b/__tests__/utils/events.test.ts @@ -193,10 +193,13 @@ describe('parseEvents', () => { }, }; - const event: RPC.Event = { + const event: RPC.EmittedEvent = { from_address: 'test_address', keys: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'], data: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'], + block_hash: '0x1234', + block_number: 567, + transaction_hash: '0x789', }; const parsedEvents = parseEvents([event], abiEvents, abiStructs, abiEnums); @@ -206,6 +209,94 @@ describe('parseEvents', () => { cairo_event_struct: { test_name: 1708719217404197029088109386680815809747762070431461851150711916567020191623n, }, + block_hash: '0x1234', + block_number: 567, + transaction_hash: '0x789', + }, + ]; + + expect(parsedEvents).toStrictEqual(result); + }); + + test('should return parsed emitted events', () => { + const abiEventAndVariantName = 'cairo_event_struct'; + const abiCairoEventStruct: AbiEvent = { + kind: 'struct', + members: [ + { + name: 'test_name', + type: 'test_type', + kind: 'data', + }, + ], + name: abiEventAndVariantName, + type: 'event', + }; + + const abiCairoEventEnum: CairoEventVariant = { + kind: 'enum', + variants: [ + { + name: 'test_name', + type: abiEventAndVariantName, + kind: 'data', + }, + ], + name: 'test_cairo_event', + type: 'event', + }; + + const abiEvents = getAbiEvents([getInterfaceAbi(), abiCairoEventStruct, abiCairoEventEnum]); + + const abiStructs: AbiStructs = { + abi_structs: { + members: [ + { + name: 'test_name', + type: 'test_type', + offset: 1, + }, + ], + size: 2, + name: 'cairo_event_struct', + type: 'struct', + }, + }; + + const abiEnums: AbiEnums = { + abi_enums: { + variants: [ + { + name: 'test_name', + type: 'cairo_event_struct_variant', + offset: 1, + }, + ], + size: 2, + name: 'test_cairo_event', + type: 'enum', + }, + }; + + const event: RPC.EmittedEvent = { + from_address: 'test_address', + keys: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'], + data: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'], + block_hash: '0x26b160f10156dea0639bec90696772c640b9706a47f5b8c52ea1abe5858b34d', + block_number: 1, + transaction_hash: '0x26b160f10156dea0639bec90696772c640b9706a47f5b8c52ea1abe5858b34c', + }; + + const parsedEvents = parseEvents([event], abiEvents, abiStructs, abiEnums); + + const result = [ + { + cairo_event_struct: { + test_name: 1708719217404197029088109386680815809747762070431461851150711916567020191623n, + }, + block_hash: '0x26b160f10156dea0639bec90696772c640b9706a47f5b8c52ea1abe5858b34d', + block_number: 1, + transaction_hash: '0x26b160f10156dea0639bec90696772c640b9706a47f5b8c52ea1abe5858b34c', }, ]; @@ -272,10 +363,13 @@ describe('parseEvents', () => { }, }; - const event: RPC.Event = { + const event: RPC.EmittedEvent = { from_address: 'test_address', keys: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'], data: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'], + block_hash: '0x1234', + block_number: 567, + transaction_hash: '0x789', }; abiEvents['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'].name = ''; diff --git a/package-lock.json b/package-lock.json index 0cba31f88..303ac6f3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "starknet", - "version": "6.15.0", + "version": "6.20.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "starknet", - "version": "6.15.0", + "version": "6.20.2", "license": "MIT", "dependencies": { "@noble/curves": "~1.3.0", @@ -18,7 +18,7 @@ "isomorphic-fetch": "^3.0.0", "lossless-json": "^4.0.1", "pako": "^2.0.4", - "starknet-types-07": "npm:@starknet-io/types-js@^0.7.7", + "starknet-types-07": "npm:@starknet-io/types-js@^0.7.10", "ts-mixer": "^6.0.3" }, "devDependencies": { @@ -4225,224 +4225,234 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz", - "integrity": "sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz", + "integrity": "sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.1.tgz", - "integrity": "sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz", + "integrity": "sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.1.tgz", - "integrity": "sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz", + "integrity": "sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.1.tgz", - "integrity": "sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz", + "integrity": "sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz", + "integrity": "sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz", + "integrity": "sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.1.tgz", - "integrity": "sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz", + "integrity": "sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.1.tgz", - "integrity": "sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz", + "integrity": "sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.1.tgz", - "integrity": "sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz", + "integrity": "sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.1.tgz", - "integrity": "sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz", + "integrity": "sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.1.tgz", - "integrity": "sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz", + "integrity": "sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.1.tgz", - "integrity": "sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz", + "integrity": "sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.1.tgz", - "integrity": "sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz", + "integrity": "sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.1.tgz", - "integrity": "sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz", + "integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.1.tgz", - "integrity": "sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz", + "integrity": "sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.1.tgz", - "integrity": "sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz", + "integrity": "sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.1.tgz", - "integrity": "sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz", + "integrity": "sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.1.tgz", - "integrity": "sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz", + "integrity": "sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -5281,11 +5291,10 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true, - "license": "MIT" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true }, "node_modules/@types/graceful-fs": { "version": "4.1.9", @@ -7446,11 +7455,10 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -13970,8 +13978,6 @@ }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "inBundle": true, "license": "ISC", @@ -13989,8 +13995,6 @@ }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "inBundle": true, "license": "MIT", @@ -14003,8 +14007,6 @@ }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "inBundle": true, "license": "MIT" @@ -14028,8 +14030,6 @@ }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "inBundle": true, "license": "MIT", @@ -14045,8 +14045,6 @@ }, "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", - "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "dev": true, "inBundle": true, "license": "ISC" @@ -14216,8 +14214,6 @@ }, "node_modules/npm/node_modules/@npmcli/name-from-folder": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz", - "integrity": "sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==", "dev": true, "inBundle": true, "license": "ISC", @@ -14227,8 +14223,6 @@ }, "node_modules/npm/node_modules/@npmcli/node-gyp": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", - "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", "dev": true, "inBundle": true, "license": "ISC", @@ -14268,8 +14262,6 @@ }, "node_modules/npm/node_modules/@npmcli/query": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/query/-/query-3.1.0.tgz", - "integrity": "sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==", "dev": true, "inBundle": true, "license": "ISC", @@ -14308,8 +14300,6 @@ }, "node_modules/npm/node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "inBundle": true, "license": "MIT", @@ -14394,8 +14384,6 @@ }, "node_modules/npm/node_modules/@tufjs/canonical-json": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", - "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", "dev": true, "inBundle": true, "license": "MIT", @@ -14418,8 +14406,6 @@ }, "node_modules/npm/node_modules/abbrev": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", "dev": true, "inBundle": true, "license": "ISC", @@ -14429,8 +14415,6 @@ }, "node_modules/npm/node_modules/agent-base": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "inBundle": true, "license": "MIT", @@ -14443,8 +14427,6 @@ }, "node_modules/npm/node_modules/aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "inBundle": true, "license": "MIT", @@ -14458,8 +14440,6 @@ }, "node_modules/npm/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "inBundle": true, "license": "MIT", @@ -14469,8 +14449,6 @@ }, "node_modules/npm/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "inBundle": true, "license": "MIT", @@ -14483,24 +14461,18 @@ }, "node_modules/npm/node_modules/aproba": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/archy": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "inBundle": true, "license": "MIT" @@ -14534,8 +14506,6 @@ }, "node_modules/npm/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "inBundle": true, "license": "MIT", @@ -14568,8 +14538,6 @@ }, "node_modules/npm/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "inBundle": true, "license": "MIT", @@ -14582,8 +14550,6 @@ }, "node_modules/npm/node_modules/chownr": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, "inBundle": true, "license": "ISC", @@ -14593,8 +14559,6 @@ }, "node_modules/npm/node_modules/ci-info": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", "dev": true, "funding": [ { @@ -14622,8 +14586,6 @@ }, "node_modules/npm/node_modules/clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "inBundle": true, "license": "MIT", @@ -14633,8 +14595,6 @@ }, "node_modules/npm/node_modules/cli-columns": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-columns/-/cli-columns-4.0.0.tgz", - "integrity": "sha512-XW2Vg+w+L9on9wtwKpyzluIPCWXjaBahI7mTcYjx+BVIYD9c3yqcv/yKC7CmdCZat4rq2yiE1UMSJC5ivKfMtQ==", "dev": true, "inBundle": true, "license": "MIT", @@ -14657,8 +14617,6 @@ }, "node_modules/npm/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "inBundle": true, "license": "MIT", @@ -14671,24 +14629,18 @@ }, "node_modules/npm/node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/common-ancestor-path": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/cross-spawn": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "inBundle": true, "license": "MIT", @@ -14718,8 +14670,6 @@ }, "node_modules/npm/node_modules/cssesc": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "inBundle": true, "license": "MIT", @@ -14732,8 +14682,6 @@ }, "node_modules/npm/node_modules/debug": { "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, "inBundle": true, "license": "MIT", @@ -14757,8 +14705,6 @@ }, "node_modules/npm/node_modules/diff": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "inBundle": true, "license": "BSD-3-Clause", @@ -14768,24 +14714,18 @@ }, "node_modules/npm/node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/encoding": { "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, "inBundle": true, "license": "MIT", @@ -14796,8 +14736,6 @@ }, "node_modules/npm/node_modules/env-paths": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "inBundle": true, "license": "MIT", @@ -14807,24 +14745,18 @@ }, "node_modules/npm/node_modules/err-code": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/exponential-backoff": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", "dev": true, "inBundle": true, "license": "Apache-2.0" }, "node_modules/npm/node_modules/fastest-levenshtein": { "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, "inBundle": true, "license": "MIT", @@ -14834,8 +14766,6 @@ }, "node_modules/npm/node_modules/foreground-child": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", "dev": true, "inBundle": true, "license": "ISC", @@ -14852,8 +14782,6 @@ }, "node_modules/npm/node_modules/fs-minipass": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", "dev": true, "inBundle": true, "license": "ISC", @@ -14889,16 +14817,12 @@ }, "node_modules/npm/node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/hosted-git-info": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, "inBundle": true, "license": "ISC", @@ -14911,16 +14835,12 @@ }, "node_modules/npm/node_modules/http-cache-semantics": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true, "inBundle": true, "license": "BSD-2-Clause" }, "node_modules/npm/node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "inBundle": true, "license": "MIT", @@ -14947,8 +14867,6 @@ }, "node_modules/npm/node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "inBundle": true, "license": "MIT", @@ -14974,8 +14892,6 @@ }, "node_modules/npm/node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "inBundle": true, "license": "MIT", @@ -14985,8 +14901,6 @@ }, "node_modules/npm/node_modules/indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, "inBundle": true, "license": "MIT", @@ -15023,8 +14937,6 @@ }, "node_modules/npm/node_modules/ip-address": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, "inBundle": true, "license": "MIT", @@ -15038,8 +14950,6 @@ }, "node_modules/npm/node_modules/ip-regex": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz", - "integrity": "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==", "dev": true, "inBundle": true, "license": "MIT", @@ -15064,8 +14974,6 @@ }, "node_modules/npm/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "inBundle": true, "license": "MIT", @@ -15075,24 +14983,18 @@ }, "node_modules/npm/node_modules/is-lambda": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/jackspeak": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", @@ -15111,8 +15013,6 @@ }, "node_modules/npm/node_modules/jsbn": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "dev": true, "inBundle": true, "license": "MIT" @@ -15128,8 +15028,6 @@ }, "node_modules/npm/node_modules/json-stringify-nice": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", - "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", "dev": true, "inBundle": true, "license": "ISC", @@ -15139,8 +15037,6 @@ }, "node_modules/npm/node_modules/jsonparse": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true, "engines": [ "node >= 0.2.0" @@ -15150,16 +15046,12 @@ }, "node_modules/npm/node_modules/just-diff": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz", - "integrity": "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/just-diff-apply": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.5.0.tgz", - "integrity": "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==", "dev": true, "inBundle": true, "license": "MIT" @@ -15364,8 +15256,6 @@ }, "node_modules/npm/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "inBundle": true, "license": "ISC", @@ -15381,8 +15271,6 @@ }, "node_modules/npm/node_modules/minipass": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "inBundle": true, "license": "ISC", @@ -15392,8 +15280,6 @@ }, "node_modules/npm/node_modules/minipass-collect": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "dev": true, "inBundle": true, "license": "ISC", @@ -15423,8 +15309,6 @@ }, "node_modules/npm/node_modules/minipass-flush": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dev": true, "inBundle": true, "license": "ISC", @@ -15449,8 +15333,6 @@ }, "node_modules/npm/node_modules/minipass-pipeline": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dev": true, "inBundle": true, "license": "ISC", @@ -15475,8 +15357,6 @@ }, "node_modules/npm/node_modules/minipass-sized": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dev": true, "inBundle": true, "license": "ISC", @@ -15501,8 +15381,6 @@ }, "node_modules/npm/node_modules/minizlib": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "inBundle": true, "license": "MIT", @@ -15528,8 +15406,6 @@ }, "node_modules/npm/node_modules/mkdirp": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, "inBundle": true, "license": "MIT", @@ -15542,8 +15418,6 @@ }, "node_modules/npm/node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "inBundle": true, "license": "MIT" @@ -15559,8 +15433,6 @@ }, "node_modules/npm/node_modules/negotiator": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, "inBundle": true, "license": "MIT", @@ -15618,8 +15490,6 @@ }, "node_modules/npm/node_modules/normalize-package-data": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", - "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, "inBundle": true, "license": "BSD-2-Clause", @@ -15634,8 +15504,6 @@ }, "node_modules/npm/node_modules/npm-audit-report": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-5.0.0.tgz", - "integrity": "sha512-EkXrzat7zERmUhHaoren1YhTxFwsOu5jypE84k6632SXTHcQE1z8V51GC6GVZt8LxkC+tbBcKMUBZAgk8SUSbw==", "dev": true, "inBundle": true, "license": "ISC", @@ -15657,8 +15525,6 @@ }, "node_modules/npm/node_modules/npm-install-checks": { "version": "6.3.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", - "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", "dev": true, "inBundle": true, "license": "BSD-2-Clause", @@ -15671,8 +15537,6 @@ }, "node_modules/npm/node_modules/npm-normalize-package-bin": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", "dev": true, "inBundle": true, "license": "ISC", @@ -15697,8 +15561,6 @@ }, "node_modules/npm/node_modules/npm-packlist": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", - "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", "dev": true, "inBundle": true, "license": "ISC", @@ -15767,8 +15629,6 @@ }, "node_modules/npm/node_modules/p-map": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "inBundle": true, "license": "MIT", @@ -15784,8 +15644,6 @@ }, "node_modules/npm/node_modules/package-json-from-dist": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", "dev": true, "inBundle": true, "license": "BlueOak-1.0.0" @@ -15823,8 +15681,6 @@ }, "node_modules/npm/node_modules/parse-conflict-json": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz", - "integrity": "sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==", "dev": true, "inBundle": true, "license": "ISC", @@ -15839,8 +15695,6 @@ }, "node_modules/npm/node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "inBundle": true, "license": "MIT", @@ -15850,8 +15704,6 @@ }, "node_modules/npm/node_modules/path-scurry": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", @@ -15899,8 +15751,6 @@ }, "node_modules/npm/node_modules/promise-all-reject-late": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", - "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", "dev": true, "inBundle": true, "license": "ISC", @@ -15910,8 +15760,6 @@ }, "node_modules/npm/node_modules/promise-call-limit": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.1.tgz", - "integrity": "sha512-utl+0x8gIDasV5X+PI5qWEPqH6fJS0pFtQ/4gZ95xfEFb/89dmh+/b895TbFDBLiafBvxD/PGTKfvxl4kH/pQg==", "dev": true, "inBundle": true, "license": "ISC", @@ -15921,16 +15769,12 @@ }, "node_modules/npm/node_modules/promise-inflight": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/promise-retry": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, "inBundle": true, "license": "MIT", @@ -15956,8 +15800,6 @@ }, "node_modules/npm/node_modules/qrcode-terminal": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", - "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", "dev": true, "inBundle": true, "bin": { @@ -15978,8 +15820,6 @@ }, "node_modules/npm/node_modules/read-cmd-shim": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", - "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", "dev": true, "inBundle": true, "license": "ISC", @@ -15989,8 +15829,6 @@ }, "node_modules/npm/node_modules/read-package-json-fast": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", - "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", "dev": true, "inBundle": true, "license": "ISC", @@ -16004,8 +15842,6 @@ }, "node_modules/npm/node_modules/retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, "inBundle": true, "license": "MIT", @@ -16015,8 +15851,6 @@ }, "node_modules/npm/node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "inBundle": true, "license": "MIT", @@ -16024,8 +15858,6 @@ }, "node_modules/npm/node_modules/semver": { "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, "inBundle": true, "license": "ISC", @@ -16038,8 +15870,6 @@ }, "node_modules/npm/node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "inBundle": true, "license": "MIT", @@ -16052,8 +15882,6 @@ }, "node_modules/npm/node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "inBundle": true, "license": "MIT", @@ -16063,8 +15891,6 @@ }, "node_modules/npm/node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "inBundle": true, "license": "ISC", @@ -16094,8 +15920,6 @@ }, "node_modules/npm/node_modules/smart-buffer": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "inBundle": true, "license": "MIT", @@ -16134,8 +15958,6 @@ }, "node_modules/npm/node_modules/spdx-correct": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "inBundle": true, "license": "Apache-2.0", @@ -16156,8 +15978,6 @@ }, "node_modules/npm/node_modules/spdx-exceptions": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true, "inBundle": true, "license": "CC-BY-3.0" @@ -16174,8 +15994,6 @@ }, "node_modules/npm/node_modules/spdx-license-ids": { "version": "3.0.18", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", "dev": true, "inBundle": true, "license": "CC0-1.0" @@ -16200,8 +16018,6 @@ }, "node_modules/npm/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "inBundle": true, "license": "MIT", @@ -16217,8 +16033,6 @@ "node_modules/npm/node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "inBundle": true, "license": "MIT", @@ -16233,8 +16047,6 @@ }, "node_modules/npm/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "inBundle": true, "license": "MIT", @@ -16248,8 +16060,6 @@ "node_modules/npm/node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "inBundle": true, "license": "MIT", @@ -16262,8 +16072,6 @@ }, "node_modules/npm/node_modules/supports-color": { "version": "9.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", - "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", "dev": true, "inBundle": true, "license": "MIT", @@ -16326,24 +16134,18 @@ }, "node_modules/npm/node_modules/text-table": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/tiny-relative-date": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz", - "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/treeverse": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", - "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", "dev": true, "inBundle": true, "license": "ISC", @@ -16367,8 +16169,6 @@ }, "node_modules/npm/node_modules/unique-filename": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", "dev": true, "inBundle": true, "license": "ISC", @@ -16381,8 +16181,6 @@ }, "node_modules/npm/node_modules/unique-slug": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", "dev": true, "inBundle": true, "license": "ISC", @@ -16395,16 +16193,12 @@ }, "node_modules/npm/node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/validate-npm-package-license": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "inBundle": true, "license": "Apache-2.0", @@ -16434,16 +16228,12 @@ }, "node_modules/npm/node_modules/walk-up-path": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", - "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/which": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "inBundle": true, "license": "ISC", @@ -16468,8 +16258,6 @@ }, "node_modules/npm/node_modules/wrap-ansi": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "inBundle": true, "license": "MIT", @@ -16488,8 +16276,6 @@ "node_modules/npm/node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "inBundle": true, "license": "MIT", @@ -16522,8 +16308,6 @@ }, "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "inBundle": true, "license": "MIT", @@ -16536,8 +16320,6 @@ }, "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "inBundle": true, "license": "MIT" @@ -16561,8 +16343,6 @@ }, "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "inBundle": true, "license": "MIT", @@ -16578,8 +16358,6 @@ }, "node_modules/npm/node_modules/write-file-atomic": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "inBundle": true, "license": "ISC", @@ -16593,8 +16371,6 @@ }, "node_modules/npm/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, "inBundle": true, "license": "ISC" @@ -17973,13 +17749,12 @@ } }, "node_modules/rollup": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.1.tgz", - "integrity": "sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz", + "integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==", "dev": true, - "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -17989,22 +17764,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.1", - "@rollup/rollup-android-arm64": "4.21.1", - "@rollup/rollup-darwin-arm64": "4.21.1", - "@rollup/rollup-darwin-x64": "4.21.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.1", - "@rollup/rollup-linux-arm-musleabihf": "4.21.1", - "@rollup/rollup-linux-arm64-gnu": "4.21.1", - "@rollup/rollup-linux-arm64-musl": "4.21.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.1", - "@rollup/rollup-linux-riscv64-gnu": "4.21.1", - "@rollup/rollup-linux-s390x-gnu": "4.21.1", - "@rollup/rollup-linux-x64-gnu": "4.21.1", - "@rollup/rollup-linux-x64-musl": "4.21.1", - "@rollup/rollup-win32-arm64-msvc": "4.21.1", - "@rollup/rollup-win32-ia32-msvc": "4.21.1", - "@rollup/rollup-win32-x64-msvc": "4.21.1", + "@rollup/rollup-android-arm-eabi": "4.27.4", + "@rollup/rollup-android-arm64": "4.27.4", + "@rollup/rollup-darwin-arm64": "4.27.4", + "@rollup/rollup-darwin-x64": "4.27.4", + "@rollup/rollup-freebsd-arm64": "4.27.4", + "@rollup/rollup-freebsd-x64": "4.27.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.27.4", + "@rollup/rollup-linux-arm-musleabihf": "4.27.4", + "@rollup/rollup-linux-arm64-gnu": "4.27.4", + "@rollup/rollup-linux-arm64-musl": "4.27.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4", + "@rollup/rollup-linux-riscv64-gnu": "4.27.4", + "@rollup/rollup-linux-s390x-gnu": "4.27.4", + "@rollup/rollup-linux-x64-gnu": "4.27.4", + "@rollup/rollup-linux-x64-musl": "4.27.4", + "@rollup/rollup-win32-arm64-msvc": "4.27.4", + "@rollup/rollup-win32-ia32-msvc": "4.27.4", + "@rollup/rollup-win32-x64-msvc": "4.27.4", "fsevents": "~2.3.2" } }, @@ -18737,10 +18514,9 @@ }, "node_modules/starknet-types-07": { "name": "@starknet-io/types-js", - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@starknet-io/types-js/-/types-js-0.7.7.tgz", - "integrity": "sha512-WLrpK7LIaIb8Ymxu6KF/6JkGW1sso988DweWu7p5QY/3y7waBIiPvzh27D9bX5KIJNRDyOoOVoHVEKYUYWZ/RQ==", - "license": "MIT" + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@starknet-io/types-js/-/types-js-0.7.10.tgz", + "integrity": "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w==" }, "node_modules/stream-combiner2": { "version": "1.1.1", diff --git a/package.json b/package.json index 1d08156c5..18f9496e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "starknet", - "version": "6.15.0", + "version": "6.20.2", "description": "JavaScript library for Starknet", "license": "MIT", "repository": { @@ -106,7 +106,7 @@ "isomorphic-fetch": "^3.0.0", "lossless-json": "^4.0.1", "pako": "^2.0.4", - "starknet-types-07": "npm:@starknet-io/types-js@^0.7.7", + "starknet-types-07": "npm:@starknet-io/types-js@^0.7.10", "ts-mixer": "^6.0.3" }, "lint-staged": { diff --git a/scripts/generateRpcErrorMap.js b/scripts/generateRpcErrorMap.js new file mode 100644 index 000000000..0460d9d97 --- /dev/null +++ b/scripts/generateRpcErrorMap.js @@ -0,0 +1,24 @@ +// Processes the RPC specification error types and logs the output to simplify the generation +// of an error aggregating TS type and error code mapping object. Currently used in: +// - src/types/errors.ts +// - src/utils/errors/rpc.ts + +const starknet_api_openrpc = require('starknet_specs/api/starknet_api_openrpc.json'); +const starknet_trace_api_openrpc = require('starknet_specs/api/starknet_trace_api_openrpc.json'); +const starknet_write_api = require('starknet_specs/api/starknet_write_api.json'); + +const errorNameCodeMap = Object.fromEntries( + Object.entries({ + ...starknet_trace_api_openrpc.components.errors, + ...starknet_write_api.components.errors, + ...starknet_api_openrpc.components.errors, + }) + .map((e) => [e[0], e[1].code]) + .sort((a, b) => a[1] - b[1]) +); + +console.log('errorCodes:'); +console.log(errorNameCodeMap); +console.log(); +console.log('errorTypes:'); +Object.keys(errorNameCodeMap).forEach((n) => console.log(`${n}: Errors.${n};`)); diff --git a/src/channel/rpc_0_6.ts b/src/channel/rpc_0_6.ts index 491ae32f9..f9bc221e3 100644 --- a/src/channel/rpc_0_6.ts +++ b/src/channel/rpc_0_6.ts @@ -1,5 +1,5 @@ import { NetworkName, StarknetChainId } from '../constants'; -import { LibraryError } from '../provider/errors'; +import { LibraryError, RpcError } from '../utils/errors'; import { AccountInvocationItem, AccountInvocations, @@ -11,6 +11,7 @@ import { DeployAccountContractTransaction, Invocation, InvocationsDetailsWithNonce, + RPC_ERROR, RpcProviderOptions, TransactionType, getEstimateFeeBulkOptions, @@ -118,11 +119,7 @@ export class RpcChannel { protected errorHandler(method: string, params: any, rpcError?: JRPC.Error, otherError?: any) { if (rpcError) { - const { code, message, data } = rpcError; - throw new LibraryError( - `RPC: ${method} with params ${stringify(params, null, 2)}\n - ${code}: ${message}: ${stringify(data)}` - ); + throw new RpcError(rpcError as RPC_ERROR, method, params); } if (otherError instanceof LibraryError) { throw otherError; diff --git a/src/channel/rpc_0_7.ts b/src/channel/rpc_0_7.ts index cd14a8b77..1918fc880 100644 --- a/src/channel/rpc_0_7.ts +++ b/src/channel/rpc_0_7.ts @@ -1,5 +1,5 @@ import { NetworkName, StarknetChainId } from '../constants'; -import { LibraryError } from '../provider/errors'; +import { LibraryError, RpcError } from '../utils/errors'; import { AccountInvocationItem, AccountInvocations, @@ -11,6 +11,7 @@ import { DeployAccountContractTransaction, Invocation, InvocationsDetailsWithNonce, + RPC_ERROR, RpcProviderOptions, TransactionType, getEstimateFeeBulkOptions, @@ -118,11 +119,7 @@ export class RpcChannel { protected errorHandler(method: string, params: any, rpcError?: JRPC.Error, otherError?: any) { if (rpcError) { - const { code, message, data } = rpcError; - throw new LibraryError( - `RPC: ${method} with params ${stringify(params, null, 2)}\n - ${code}: ${message}: ${stringify(data)}` - ); + throw new RpcError(rpcError as RPC_ERROR, method, params); } if (otherError instanceof LibraryError) { throw otherError; diff --git a/src/constants.ts b/src/constants.ts index 06137197a..df6697149 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -82,3 +82,9 @@ export const SNIP9_V1_INTERFACE_ID = '0x68cfd18b92d1907b8ba3cc324900277f5a3622099431ea85dd8089255e4181'; export const SNIP9_V2_INTERFACE_ID = '0x1d1144bb2138366ff28d8e9ab57456b1d332ac42196230c3a602003c89872'; + +// Ledger signer +// 0x80 +export const HARDENING_BYTE = 128; +// 0x80000000 +export const HARDENING_4BYTES = 2147483648n; diff --git a/src/contract/default.ts b/src/contract/default.ts index 221d2c448..9599a6ee8 100644 --- a/src/contract/default.ts +++ b/src/contract/default.ts @@ -23,14 +23,16 @@ import { Result, AbiStruct, ValidateType, + type SuccessfulTransactionReceiptResponse, } from '../types'; import assert from '../utils/assert'; -import { CallData, cairo } from '../utils/calldata'; +import { cairo, CallData } from '../utils/calldata'; import { createAbiParser } from '../utils/calldata/parser'; import { getAbiEvents, parseEvents as parseRawEvents } from '../utils/events/index'; import { cleanHex } from '../utils/num'; import { ContractInterface } from './interface'; import type { GetTransactionReceiptResponse } from '../utils/transactionReceipt'; +import type { INVOKE_TXN_RECEIPT } from '../types/provider/spec'; export type TypedContractV2 = AbiWanTypedContract & Contract; @@ -329,15 +331,32 @@ export class Contract implements ContractInterface { } public parseEvents(receipt: GetTransactionReceiptResponse): ParsedEvents { - return parseRawEvents( - (receipt as InvokeTransactionReceiptResponse).events?.filter( - (event) => cleanHex(event.from_address) === cleanHex(this.address), - [] - ) || [], - this.events, - this.structs, - CallData.getAbiEnum(this.abi) - ); + let parsed: ParsedEvents; + receipt.match({ + success: (txR: SuccessfulTransactionReceiptResponse) => { + const emittedEvents = + (txR as InvokeTransactionReceiptResponse).events + ?.map((event) => { + return { + block_hash: (txR as INVOKE_TXN_RECEIPT).block_hash, + block_number: (txR as INVOKE_TXN_RECEIPT).block_number, + transaction_hash: (txR as INVOKE_TXN_RECEIPT).transaction_hash, + ...event, + }; + }) + .filter((event) => cleanHex(event.from_address) === cleanHex(this.address), []) || []; + parsed = parseRawEvents( + emittedEvents, + this.events, + this.structs, + CallData.getAbiEnum(this.abi) + ); + }, + _: () => { + throw Error('This transaction was not successful.'); + }, + }); + return parsed!; } public isCairo1(): boolean { diff --git a/src/index.ts b/src/index.ts index 4804e4b9f..e62bfadfc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,6 +36,7 @@ export * as selector from './utils/hash/selector'; export * as events from './utils/events'; export * as outsideExecution from './utils/outsideExecution'; export * as src5 from './utils/src5'; +export * from './utils/batch'; export * from './utils/responseParser'; export * from './utils/cairoDataTypes/uint256'; export * from './utils/cairoDataTypes/uint512'; diff --git a/src/provider/index.ts b/src/provider/index.ts index 0a1e83ad2..027a54085 100644 --- a/src/provider/index.ts +++ b/src/provider/index.ts @@ -1,7 +1,7 @@ import { RpcProvider } from './rpc'; export { RpcProvider as Provider } from './extensions/default'; // backward-compatibility -export * from './errors'; +export * from '../utils/errors'; export * from './interface'; export * from './extensions/default'; diff --git a/src/provider/rpc.ts b/src/provider/rpc.ts index 28ee0cdb5..74e49bc6a 100644 --- a/src/provider/rpc.ts +++ b/src/provider/rpc.ts @@ -44,7 +44,7 @@ import { RPCResponseParser } from '../utils/responseParser/rpc'; import { formatSignature } from '../utils/stark'; import { GetTransactionReceiptResponse, ReceiptTx } from '../utils/transactionReceipt'; import { getMessageHash, validateTypedData } from '../utils/typedData'; -import { LibraryError } from './errors'; +import { LibraryError } from '../utils/errors'; import { ProviderInterface } from './interface'; export class RpcProvider implements ProviderInterface { diff --git a/src/signer/index.ts b/src/signer/index.ts index ff56b157a..2a5e89a79 100644 --- a/src/signer/index.ts +++ b/src/signer/index.ts @@ -1,4 +1,10 @@ export * from './interface'; export * from './default'; export * from './ethSigner'; -export * from './ledgerSigner'; +export { + LedgerSigner111, + getLedgerPathBuffer111, + LedgerSigner111 as LedgerSigner, + getLedgerPathBuffer111 as getLedgerPathBuffer, +} from './ledgerSigner111'; +export { LedgerSigner221, getLedgerPathBuffer221 } from './ledgerSigner221'; diff --git a/src/signer/ledgerSigner.ts b/src/signer/ledgerSigner111.ts similarity index 62% rename from src/signer/ledgerSigner.ts rename to src/signer/ledgerSigner111.ts index 005212c5f..e08144d61 100644 --- a/src/signer/ledgerSigner.ts +++ b/src/signer/ledgerSigner111.ts @@ -12,6 +12,7 @@ import type { TypedData, Call, Signature, + LedgerPathCalculation, } from '../types'; import assert from '../utils/assert'; import { CallData } from '../utils/calldata'; @@ -38,13 +39,15 @@ import { ETransactionVersion3 } from '../types/api'; type _Transport = any; /** - * Signer for accounts using a Ledger Nano S+/X signature + * Signer for accounts using a Ledger Nano S+/X signature (Starknet Ledger APP version 1.1.1) + * + * The Ledger has to be connected, unlocked and the Starknet APP has to be selected prior of use of this class. */ -export class LedgerSigner = any> implements SignerInterface { +export class LedgerSigner111 = any> implements SignerInterface { readonly transporter: Transport; // this is a hack to allow the '@ledgerhq/hw-transport' type to be used as a dev dependency but not exposed in the production build - private _transporter: _Transport; + protected _transporter: _Transport; readonly accountID: number; @@ -52,7 +55,7 @@ export class LedgerSigner = any> implements S readonly pathBuffer: Uint8Array; - private appVersion: string; + protected appVersion: string; protected pubKey: string; @@ -63,16 +66,24 @@ export class LedgerSigner = any> implements S * @param {Transport} transport 5 transports are available to handle USB, bluetooth, Node, Web, Mobile. * See Guides for more details. * @param {number} accountID ID of Ledger Nano (can handle 2**31 accounts). - * @param {string} [eip2645application='LedgerW'] A wallet is defined by an ERC2645 derivation path (6 items). - * One item is the `application`. Default value is `LedgerW`. + * @param {string} [eip2645application='LedgerW'] A wallet is defined by an ERC2645 derivation path (6 items), + * and one item is the `application` and can be customized. + * Default value is `LedgerW`. + * @param {LedgerPathCalculation} [pathFunction=getLedgerPathBuffer111] + * defines the function that will calculate the path. By default `getLedgerPathBuffer111` is selected. * @example * ```typescript * import TransportNodeHid from "@ledgerhq/hw-transport-node-hid"; * const myNodeTransport = await TransportNodeHid.create(); - * const myLedgerSigner = new LedgerSigner(myNodeTransport, 0); + * const myLedgerSigner = new LedgerSigner111(myNodeTransport, 0); * ``` */ - constructor(transport: Transport, accountID: number, eip2645application: string = 'LedgerW') { + constructor( + transport: Transport, + accountID: number, + eip2645application: string = 'LedgerW', + pathFunction: LedgerPathCalculation = getLedgerPathBuffer111 + ) { assert(accountID >= 0, 'Ledger account ID shall not be a negative number.'); assert(accountID <= MASK_31, 'Ledger account ID shall be < 2**31.'); assert(!!eip2645application, 'Ledger application name shall not be empty.'); @@ -83,12 +94,17 @@ export class LedgerSigner = any> implements S this.fullPubKey = ''; this.eip2645applicationName = eip2645application; this.appVersion = ''; - this.pathBuffer = getLedgerPathBuffer(this.accountID, this.eip2645applicationName); + this.pathBuffer = pathFunction(this.accountID, this.eip2645applicationName); } /** * provides the Starknet public key * @returns an hex string : 64 characters are Point X coordinate. + * @example + * ```typescript + * const result = await myLedgerSigner.getPubKey(); + * // result= "0x03681417ba3e1f050dd3ccdceb8d22b5e44fa70ee7844d472c6a768bded5174e" + * ``` */ public async getPubKey(): Promise { if (!this.pubKey) await this.getPublicKeys(); @@ -98,6 +114,11 @@ export class LedgerSigner = any> implements S /** * provides the full public key (with parity prefix) * @returns an hex string : 2 first characters are the parity, the 64 following characters are Point X coordinate. 64 last characters are Point Y coordinate. + * @example + * ```typescript + * const result = await myLedgerSigner.getFullPubKey(); + * // result= "0x0403681417ba3e1f050dd3ccdceb8d22b5e44fa70ee7844d472c6a768bded5174e03cbc86f805dcfcb0c1922dd4daf181afa289d86223a18bc856276615bcc7787" + * ``` */ public async getFullPubKey(): Promise { if (!this.fullPubKey) await this.getPublicKeys(); @@ -121,11 +142,59 @@ export class LedgerSigner = any> implements S return this.appVersion; } + /** + * Sign a TypedData message (SNIP-12) in a Ledger. + * @param {typedDataToHash} typedDataToHash A TypedData message compatible with SNIP-12. + * @param {string} accountAddress Signer account address (Hex or num string) + * @returns {Signature} The signed message. + * @example + * ```typescript + * const result = myLedgerSigner.signMessage(snip12Message, account0.address); + * // result = Signature { r: 611475243393396148729326917410546146405234155928298353899191529090923298688n, + * // s: 798839819213540985856952481651392652149797817551686626114697493101433761982n, + * // recovery: 0} + * ``` + */ public async signMessage(typedDataToHash: TypedData, accountAddress: string): Promise { const msgHash = getMessageHash(typedDataToHash, accountAddress); return this.signRaw(msgHash); } + /** + * Sign in a Ledger a V1 or a V3 transaction. This is a blind sign on the Ledger screen. + * @param {Call1[]} transactions An array of `Call` transactions (generated for example by `myContract.populate()`). + * @param {InvocationsSignerDetails} transactionsDetail An object that includes all the necessary inputs to hash the transaction. Can be `V2InvocationsSignerDetails` or `V3InvocationsSignerDetails` type. + * @returns {Signature} The signed transaction. + * @example + * ```typescript + * const txDetailsV3: V3InvocationsSignerDetails = { + * chainId: constants.StarknetChainId.SN_MAIN, + * nonce: "28", + * accountDeploymentData: [], + * paymasterData: [], + * cairoVersion: "1", + * feeDataAvailabilityMode: "L1", + * nonceDataAvailabilityMode: "L1", + * resourceBounds: { + * l1_gas: { + * max_amount: "0x2a00", + * max_price_per_unit: "0x5c00000" + * }, + * l2_gas: { + * max_amount: "0x00", + * max_price_per_unit: "0x00" + * }, + * }, + * tip: 0, + * version: "0x3", + * walletAddress: account0.address + * } + * const result = myLedgerSigner.signTransaction([call0, call1], txDetailsV3); + * // result = Signature { r: 611475243393396148729326917410546146405234155928298353899191529090923298688n, + * // s: 798839819213540985856952481651392652149797817551686626114697493101433761982n, + * // recovery: 0} + * ``` + */ public async signTransaction( transactions: Call[], transactionsDetail: InvocationsSignerDetails @@ -159,6 +228,18 @@ export class LedgerSigner = any> implements S return this.signRaw(msgHash as string); } + /** + * Sign in a Ledger the deployment of a new account. This is a blind sign on the Ledger screen. + * @param {DeployAccountSignerDetails} details An object that includes all necessary data to calculate the Hash. It can be `V2DeployAccountSignerDetails` or `V3DeployAccountSignerDetails` types. + * @returns {Signature} The deploy account signature. + * @example + * ```typescript + * const result = myLedgerSigner.signDeployAccountTransaction(details); + * // result = Signature { r: 611475243393396148729326917410546146405234155928298353899191529090923298688n, + * // s: 798839819213540985856952481651392652149797817551686626114697493101433761982n, + * // recovery: 0} + * ``` + */ public async signDeployAccountTransaction( details: DeployAccountSignerDetails ): Promise { @@ -191,12 +272,23 @@ export class LedgerSigner = any> implements S return this.signRaw(msgHash as string); } + /** + * Sign in a Ledger the declaration of a new class. This is a blind sign on the Ledger screen. + * @param {DeclareSignerDetails} details An object that includes all necessary data to calculate the Hash. It can be `V3DeclareSignerDetails` or `V2DeclareSignerDetails` types. + * @returns {Signature} The declare Signature. + * @example + * ```typescript + * const result = myLedgerSigner.signDeclareTransaction(details); + * // result = Signature { r: 611475243393396148729326917410546146405234155928298353899191529090923298688n, + * // s: 798839819213540985856952481651392652149797817551686626114697493101433761982n, + * // recovery: 0} + * ``` + */ public async signDeclareTransaction( // contractClass: ContractClass, // Should be used once class hash is present in ContractClass details: DeclareSignerDetails ): Promise { let msgHash; - if (Object.values(ETransactionVersion2).includes(details.version as any)) { const det = details as V2DeclareSignerDetails; msgHash = calculateDeclareTransactionHash({ @@ -214,11 +306,14 @@ export class LedgerSigner = any> implements S } else { throw Error('unsupported signDeclareTransaction version'); } - return this.signRaw(msgHash as string); } - private async signRaw(msgHash: string): Promise { + /** + * Internal function to sign a hash in a Ledger Nano. + * This is a blind sign in the Ledger ; no display of what you are signing. + */ + protected async signRaw(msgHash: string): Promise { addHexPrefix( buf2hex(await this._transporter.send(Number('0x5a'), 2, 0, 0, Buffer.from(this.pathBuffer))) ); @@ -236,7 +331,8 @@ export class LedgerSigner = any> implements S return sign1; } - private async getPublicKeys() { + /** internal function to get both the Starknet public key and the full public key */ + protected async getPublicKeys() { const pathBuff = this.pathBuffer; const respGetPublic = Uint8Array.from( await this._transporter.send(Number('0x5a'), 1, 0, 0, Buffer.from(pathBuff)) @@ -247,13 +343,27 @@ export class LedgerSigner = any> implements S } /** - * format the Ledger wallet path to an Uint8Array. - * EIP2645 path = 2645'/starknet'/application'/0'/accountId'/0 + * Format the Ledger wallet path to an Uint8Array + * for a Ledger Starknet DAPP v1.1.1. + * + * EIP2645 path = 2645'/starknet/application/0/accountId/0 * @param {number} accountId Id of account. < 2**31. * @param {string} [applicationName='LedgerW'] utf8 string of application name. * @returns an Uint8array of 24 bytes. + * @example + * ```typescript + * const result = getLedgerPathBuffer111(0); + * // result = Uint8Array(24) [ + * 128, 0, 10, 85, 71, 65, 233, 201, + * 43, 206, 231, 219, 0, 0, 0, 0, + * 0, 0, 0, 0, 0, 0, 0, 0 + * ] + * ``` */ -export function getLedgerPathBuffer(accountId: number, applicationName: string): Uint8Array { +export function getLedgerPathBuffer111( + accountId: number, + applicationName: string = 'LedgerW' +): Uint8Array { const path0buff = new Uint8Array([128, 0, 10, 85]); // "0x80000A55" EIP2645; const path1buff = new Uint8Array([71, 65, 233, 201]); // "starknet" const path2buff = diff --git a/src/signer/ledgerSigner221.ts b/src/signer/ledgerSigner221.ts new file mode 100644 index 000000000..ca6d3efad --- /dev/null +++ b/src/signer/ledgerSigner221.ts @@ -0,0 +1,668 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable no-bitwise */ +/* eslint no-underscore-dangle: ["error", { "allowAfterThis": true }] */ +import type { + InvocationsSignerDetails, + V2InvocationsSignerDetails, + Call, + Signature, + Calldata, + BigNumberish, + V3InvocationsSignerDetails, + LedgerPathCalculation, + DeployAccountSignerDetails, + V2DeployAccountSignerDetails, + V3DeployAccountSignerDetails, +} from '../types'; +import assert from '../utils/assert'; +import { CallData } from '../utils/calldata'; +import type { SignerInterface } from './interface'; +import { HARDENING_4BYTES, HARDENING_BYTE } from '../constants'; +import { ETransactionVersion2 } from '../types/api/rpcspec_0_6'; +import { getExecuteCalldata } from '../utils/transaction'; +import { + calculateDeployAccountTransactionHash, + calculateInvokeTransactionHash, + getSelector, +} from '../utils/hash'; +import { intDAM } from '../utils/stark'; +import { addHexPrefix, buf2hex, concatenateArrayBuffer, removeHexPrefix } from '../utils/encode'; +import { hexToBytes, stringToSha256ToArrayBuff4, toBigInt, toHex } from '../utils/num'; +import { starkCurve } from '../utils/ec'; +import { EDAMode, EDataAvailabilityMode, ETransactionVersion3 } from '../types/api'; +import { addAddressPadding } from '../utils/address'; +import { + encodeResourceBoundsL1, + encodeResourceBoundsL2, + hashDAMode, +} from '../utils/hash/transactionHash/v3'; +import { LedgerSigner111 } from './ledgerSigner111'; + +/** + * Signer for accounts using a Ledger Nano S+/X signature (Starknet Ledger APP version 2.2.1). + * + * The Ledger has to be connected, unlocked and the Starknet APP has to be selected prior of use of this class. + */ +export class LedgerSigner221 = any> + extends LedgerSigner111 + implements SignerInterface +{ + /** + * constructor of the LedgerSigner class. + * @param {Transport} transport 5 transports are available to handle USB, bluetooth, Node, Web, Mobile. + * See Guides for more details. + * @param {number} accountID ID of Ledger Nano (can handle 2**31 accounts). + * @param {string} [eip2645application='LedgerW'] A wallet is defined by an ERC2645 derivation path (6 items). + * One item is called `application` and can be customized. + * Default value is `LedgerW`. + * @param {LedgerPathCalculation} [pathFunction=getLedgerPathBuffer221] + * defines the function that will calculate the path. By default `getLedgerPathBuffer221` is selected. + * + * If you are using APP v2.2.1 with an account created with the v1.1.1, you need to use : + * ```typescript + * const myLedgerSigner = new LedgerSigner211(myNodeTransport, 0, undefined, getLedgerPathBuffer111); + * ``` + * @example + * ```typescript + * import TransportNodeHid from "@ledgerhq/hw-transport-node-hid"; + * const myNodeTransport = await TransportNodeHid.create(); + * const myLedgerSigner = new LedgerSigner211(myNodeTransport, 0); + * ``` + */ + constructor( + transport: Transport, + accountID: number, + eip2645application: string = 'LedgerW', + pathFunction: LedgerPathCalculation = getLedgerPathBuffer221 + ) { + super(transport, accountID, eip2645application, pathFunction); + } + + /** + * Sign in a Ledger a V1 or a V3 transaction. The details are displayed on the Ledger screen. + * @param {Call[]} transactions An array of `Call` transactions (generated for example by `myContract.populate()`). + * @param {InvocationsSignerDetails} transactionsDetail An object that includes all the necessary inputs to hash the transaction. Can be `V2InvocationsSignerDetails` or `V3InvocationsSignerDetails` type. + * @returns {Signature} The signed transaction. + * @example + * ```typescript + * const txDetailsV3: V3InvocationsSignerDetails = { + * chainId: constants.StarknetChainId.SN_MAIN, + * nonce: "28", + * accountDeploymentData: [], + * paymasterData: [], + * cairoVersion: "1", + * feeDataAvailabilityMode: "L1", + * nonceDataAvailabilityMode: "L1", + * resourceBounds: { + * l1_gas: { + * max_amount: "0x2a00", + * max_price_per_unit: "0x5c00000" + * }, + * l2_gas: { + * max_amount: "0x00", + * max_price_per_unit: "0x00" + * }, + * }, + * tip: 0, + * version: "0x3", + * walletAddress: account0.address + * } + * const result = myLedgerSigner.signTransaction([call0, call1], txDetailsV3); + * // result = Signature { r: 611475243393396148729326917410546146405234155928298353899191529090923298688n, + * // s: 798839819213540985856952481651392652149797817551686626114697493101433761982n, + * // recovery: 0} + * ``` + */ + public async signTransaction( + transactions: Call[], + transactionsDetail: InvocationsSignerDetails + ): Promise { + const compiledCalldata = getExecuteCalldata(transactions, transactionsDetail.cairoVersion); + // TODO: How to do generic union discriminator for all like this + if (Object.values(ETransactionVersion2).includes(transactionsDetail.version as any)) { + const det = transactionsDetail as V2InvocationsSignerDetails; + const msgHash = calculateInvokeTransactionHash({ + ...det, + senderAddress: det.walletAddress, + compiledCalldata, + version: det.version, + }); + const ledgerResponse = await this.signTxV1(det, transactions); + assert( + toBigInt(msgHash) === ledgerResponse.hash, + 'The transaction hash calculated by Starknet.js is different from the one calculated by the Ledger.' + ); // probably non compatibility with Cairo 0 + return ledgerResponse.signature; + } + if (Object.values(ETransactionVersion3).includes(transactionsDetail.version as any)) { + const det = transactionsDetail as V3InvocationsSignerDetails; + const msgHash = calculateInvokeTransactionHash({ + ...det, + senderAddress: det.walletAddress, + compiledCalldata, + version: det.version, + nonceDataAvailabilityMode: intDAM(det.nonceDataAvailabilityMode), + feeDataAvailabilityMode: intDAM(det.feeDataAvailabilityMode), + }); + const ledgerResponse = await this.signTxV3(det, transactions); + assert( + toBigInt(msgHash) === ledgerResponse.hash, + 'The transaction hash calculated by Starknet.js is different from the one calculated by the Ledger.' + ); // probably non compatibility with Cairo 0 + return ledgerResponse.signature; + } + throw Error('unsupported signTransaction version'); + } + + /** + * Sign in a Ledger the deployment of a new account. The details are displayed on the Ledger screen. + * @param {DeployAccountSignerDetails} details An object that includes all necessary data to calculate the Hash. It can be `V2DeployAccountSignerDetails` or `V3DeployAccountSignerDetails` types. + * @returns {Signature} The deploy account signature. + * @example + * ```typescript + * const result = myLedgerSigner.signDeployAccountTransaction(details); + * // result = Signature { r: 611475243393396148729326917410546146405234155928298353899191529090923298688n, + * // s: 798839819213540985856952481651392652149797817551686626114697493101433761982n, + * // recovery: 0} + * ``` + */ + public async signDeployAccountTransaction( + details: DeployAccountSignerDetails + ): Promise { + const compiledConstructorCalldata = CallData.compile(details.constructorCalldata); + let msgHash; + + if (Object.values(ETransactionVersion2).includes(details.version as any)) { + const det = details as V2DeployAccountSignerDetails; + msgHash = calculateDeployAccountTransactionHash({ + ...det, + salt: det.addressSalt, + constructorCalldata: compiledConstructorCalldata, + version: det.version, + }); + const ledgerResponse = await this.signDeployAccountV1(det); + assert( + toBigInt(msgHash) === ledgerResponse.hash, + 'The transaction hash calculated by Starknet.js is different from the one calculated by the Ledger.' + ); // probably non compatibility with Cairo 0 + return ledgerResponse.signature; + } + if (Object.values(ETransactionVersion3).includes(details.version as any)) { + const det = details as V3DeployAccountSignerDetails; + msgHash = calculateDeployAccountTransactionHash({ + ...det, + salt: det.addressSalt, + compiledConstructorCalldata, + version: det.version, + nonceDataAvailabilityMode: intDAM(det.nonceDataAvailabilityMode), + feeDataAvailabilityMode: intDAM(det.feeDataAvailabilityMode), + }); + const ledgerResponse = await this.signDeployAccountV3(det); + assert( + toBigInt(msgHash) === ledgerResponse.hash, + 'The transaction hash calculated by Starknet.js is different from the one calculated by the Ledger.' + ); // probably non compatibility with Cairo 0 + return ledgerResponse.signature; + } + throw Error('unsupported signDeployAccountTransaction version'); + } + + /** + * Internal function to convert a bigNumberish to an Uint8array of 256 bits + * @param {BigNumberish} input input value + * @returns {Uint8Array} a Uint8Array containing 32 bytes. + */ + protected convertBnToLedger(input: BigNumberish): Uint8Array { + return hexToBytes(addAddressPadding(toHex(input))); + } + + /** + * Internal function to decode the response of the Ledger signature + * @param {Uint8Array} respSign the Buffer response of the Ledger + * @returns { hash: bigint; signature: Signature } transaction hash & signature + */ + protected decodeSignatureLedger(respSign: Uint8Array): { hash: bigint; signature: Signature } { + const h = BigInt(addHexPrefix(buf2hex(respSign.subarray(0, 32)))); + const r = BigInt(addHexPrefix(buf2hex(respSign.subarray(33, 65)))); + const s = BigInt(addHexPrefix(buf2hex(respSign.subarray(65, 97)))); + const v = respSign[97]; + const sign0 = new starkCurve.Signature(r, s); + const sign1 = sign0.addRecoveryBit(v); + return { hash: h, signature: sign1 }; + } + + /** Internal function to convert a Call to an array of Uint8Array. + * @param {Call} call A Call to convert. + * @return {Uint8Array[]} Call encoded in an array of Uint8Array (each containing 7 u256). + */ + protected encodeCall(call: Call): Uint8Array[] { + const toBuf: Uint8Array = this.convertBnToLedger(call.contractAddress); + const selectorBuf: Uint8Array = hexToBytes(addAddressPadding(getSelector(call.entrypoint))); + let calldataBuf: Uint8Array = new Uint8Array([]); + if (call.calldata) { + const compiledCalldata: Calldata = CallData.compile(call.calldata); + + calldataBuf = concatenateArrayBuffer( + compiledCalldata.map((parameter: string): Uint8Array => { + const a = this.convertBnToLedger(parameter); + return a; + }) + ); + } + const callBuf: Uint8Array = concatenateArrayBuffer([toBuf, selectorBuf, calldataBuf]); + // slice data into chunks of 7 * 32 bytes + const calldatas: Uint8Array[] = []; + const chunkSize = 7 * 32; // 224 bytes + for (let i = 0; i < callBuf.length; i += chunkSize) + calldatas.push(callBuf.subarray(i, i + chunkSize)); + return calldatas; + } + + /** + * Ask the Ledger Nano to display and sign a Starknet V1 transaction. + * @param {V2InvocationsSignerDetails} txDetails All the details needed for a txV1. + * @param {Call[]} calls array of Starknet invocations + * @returns an object including the transaction Hash and the signature + * @example + * ```typescript + * const calls: Call[] = [{contractAddress: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + * entrypoint: "transfer", + * calldata:["0x11f5fc2a92ac03434a7937fe982f5e5293b65ad438a989c5b78fb8f04a12016", + * "0x9184e72a000", "0x0"]}]; + * const txDet: V2InvocationsSignerDetails = { + * walletAddress: txDetails.accountAddress, + * chainId: constants.StarknetChainId.SN_MAIN, + * cairoVersion: "1", maxFee: txDetails.max_fee, + * nonce: txDetails.nonce, version: "0x1" + * }; + * const res = await myLedgerSigner.signTxV1(txDet, calls); + * // res = {hash: + * // signature: + * // } + * ``` + */ + public async signTxV1( + txDetails: V2InvocationsSignerDetails, + calls: Call[] + ): Promise<{ hash: bigint; signature: Signature }> { + // APDU 0 for path + await this._transporter.send(Number('0x5a'), 4, 0, 0, Buffer.from(this.pathBuffer)); + /* APDU 1 = + accountAddress (32 bytes) + + max_fee (32 bytes) + + chain_id (32 bytes) + + nonce (32 bytes) + */ + const accountAddressBuf: Uint8Array = this.convertBnToLedger(txDetails.walletAddress); + const maxFeeBuf: Uint8Array = this.convertBnToLedger(txDetails.maxFee); + const chainIdBuf: Uint8Array = this.convertBnToLedger(txDetails.chainId); + const nonceBuf: Uint8Array = this.convertBnToLedger(txDetails.nonce); + const dataBuf: Uint8Array = concatenateArrayBuffer([ + accountAddressBuf, + maxFeeBuf, + chainIdBuf, + nonceBuf, + ]); + await this._transporter.send(Number('0x5a'), 4, 1, 0, Buffer.from(dataBuf)); + // APDU 2 = Nb of calls + const nbCallsBuf: Uint8Array = this.convertBnToLedger(calls.length); + await this._transporter.send(Number('0x5a'), 4, 2, 0, Buffer.from(nbCallsBuf)); + // APDU 3 = Calls + let respSign: Uint8Array = new Uint8Array(0); + // eslint-disable-next-line no-restricted-syntax + for (const call of calls) { + const calldatas: Uint8Array[] = this.encodeCall(call); + await this._transporter.send(Number('0x5a'), 4, 3, 0, Buffer.from(calldatas[0])); + if (calldatas.length > 1) { + calldatas.slice(1).forEach(async (part: Uint8Array) => { + await this._transporter.send(Number('0x5a'), 4, 3, 1, Buffer.from(part)); + }); + } + respSign = await this._transporter.send(Number('0x5a'), 4, 3, 2); + } + return this.decodeSignatureLedger(respSign); + } + + /** + * Ask to the Ledger Nano to display and sign a Starknet V3 transaction. + * @param {V3InvocationsSignerDetails} txDetails All the details needed for a txV3. + * @param {Call[]} calls array of Starknet invocations + * @returns an object including the transaction Hash and the signature + * @example + * ```typescript + * const calls: Call[] = [{contractAddress: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + * entrypoint: "transfer", + * calldata:["0x11f5fc2a92ac03434a7937fe982f5e5293b65ad438a989c5b78fb8f04a12016", + * "0x9184e72a000", "0x0"]}]; + * const txDetailsV3: V3InvocationsSignerDetails = { + * chainId: constants.StarknetChainId.SN_MAIN, + * nonce: "28", accountDeploymentData: [], + * paymasterData: [], cairoVersion: "1", + * feeDataAvailabilityMode: "L1", nonceDataAvailabilityMode: "L1", + * resourceBounds: { + * l1_gas: { max_amount: "0x2a00", max_price_per_unit: "0x5c00000" + * }, + * l2_gas: { max_amount: "0x00", max_price_per_unit: "0x00"}, + * }, tip: 0, version: "0x3", walletAddress: account0.address + * }; + * const res = await myLedgerSigner.signTxV3(txDetailsV3, calls); + * // res = {hash: + * // signature: + * // } + * ``` + */ + public async signTxV3( + txDetails: V3InvocationsSignerDetails, + calls: Call[] + ): Promise<{ hash: bigint; signature: Signature }> { + assert(txDetails.paymasterData.length <= 7, 'Paymaster data includes more than 7 items.'); + assert( + txDetails.accountDeploymentData.length <= 7, + 'accountDeploymentData includes more than 7 items' + ); + // APDU 0 for path + await this._transporter.send(Number('0x5a'), 3, 0, 0, Buffer.from(this.pathBuffer)); + /* APDU 1 = + accountAddress (32 bytes) + + tip (32 bytes) + + l1_gas_bounds (32 bytes) + + l2_gas_bounds (32 bytes) + + chain_id (32 bytes) + + nonce (32 bytes) + + data_availability_mode (32 bytes) + */ + const accountAddressBuf = this.convertBnToLedger(txDetails.walletAddress); + const tipBuf = this.convertBnToLedger(txDetails.tip); + const chainIdBuf = this.convertBnToLedger(txDetails.chainId); + const nonceBuf = this.convertBnToLedger(txDetails.nonce); + const dAModeHashBuf = this.convertBnToLedger( + hashDAMode( + txDetails.nonceDataAvailabilityMode === EDataAvailabilityMode.L1 ? EDAMode.L1 : EDAMode.L2, + txDetails.feeDataAvailabilityMode === EDataAvailabilityMode.L1 ? EDAMode.L1 : EDAMode.L2 + ) + ); + const l1_gasBuf = this.convertBnToLedger(encodeResourceBoundsL1(txDetails.resourceBounds)); + const l2_gasBuf = this.convertBnToLedger(encodeResourceBoundsL2(txDetails.resourceBounds)); + const dataBuf: Uint8Array = concatenateArrayBuffer([ + accountAddressBuf, + tipBuf, + l1_gasBuf, + l2_gasBuf, + chainIdBuf, + nonceBuf, + dAModeHashBuf, + ]); + await this._transporter.send(Number('0x5a'), 3, 1, 0, Buffer.from(dataBuf)); + // APDU 2 = paymaster data + const paymasterBuf = concatenateArrayBuffer( + txDetails.paymasterData.map((value: BigNumberish): Uint8Array => { + const a = this.convertBnToLedger(value); + return a; + }) + ); + await this._transporter.send(Number('0x5a'), 3, 2, 0, Buffer.from(paymasterBuf)); + // APDU 3 = account deployment data + const accountDeployDataBuf = concatenateArrayBuffer( + txDetails.paymasterData.map((value: BigNumberish): Uint8Array => { + const a = this.convertBnToLedger(value); + return a; + }) + ); + await this._transporter.send(Number('0x5a'), 3, 3, 0, Buffer.from(accountDeployDataBuf)); + // APDU 4 = Nb of calls + const nbCallsBuf: Uint8Array = this.convertBnToLedger(calls.length); + await this._transporter.send(Number('0x5a'), 3, 4, 0, Buffer.from(nbCallsBuf)); + // APDU 5 = Calls + let respSign: Uint8Array = new Uint8Array(0); + // eslint-disable-next-line no-restricted-syntax + for (const call of calls) { + const calldatas: Uint8Array[] = this.encodeCall(call); + await this._transporter.send(Number('0x5a'), 3, 5, 0, Buffer.from(calldatas[0])); + if (calldatas.length > 1) { + calldatas.slice(1).forEach(async (part: Uint8Array) => { + await this._transporter.send(Number('0x5a'), 3, 5, 1, Buffer.from(part)); + }); + } + respSign = await this._transporter.send(Number('0x5a'), 3, 5, 2); + } + return this.decodeSignatureLedger(respSign); + } + + /** + * Ask the Ledger Nano to display and sign a Starknet V1 account deployment. + * @param {V2DeployAccountSignerDetails} deployAccountDetail All the details needed for a V1 deploy account. + * @returns an object including the transaction Hash and the signature + * @example + * ```typescript + * const deployData: V2DeployAccountSignerDetails = + * { + * tip: 0, paymasterData: [], accountDeploymentData: [], + * nonceDataAvailabilityMode: 'L1', feeDataAvailabilityMode: 'L1', + * resourceBounds: { + * l2_gas: { max_amount: '0x0', max_price_per_unit: '0x0' }, + * l1_gas: { max_amount: '0x0', max_price_per_unit: '0x0' } + * }, + * classHash: '0x540d7f5ec7ecf317e68d48564934cb99259781b1ee3cedbbc37ec5337f8e688', + * constructorCalldata: [ + * '89832696000889662999767022750851886674077821293893187900664573372145410755' + * ], + * contractAddress: '0x32c60fba64eb96831d064bbb2319375b7b7381543abe66da872e4344bcd72a0', + * addressSalt: '0x0032d7efe2a9232f9b463e7206c68fdea4aeb13fec0cb308c6ba1d197d5922c3', + * chainId: '0x534e5f5345504f4c4941', maxFee: 55050000000000n, + * version: '0x1', nonce: 0n + *} + * const res = await myLedgerSigner.signDeployAccountV1(deployData); + * // res = {hash: + * // signature: + * // } + * ``` + */ + public async signDeployAccountV1( + deployAccountDetail: V2DeployAccountSignerDetails + ): Promise<{ hash: bigint; signature: Signature }> { + // APDU 0 for path + await this._transporter.send(Number('0x5a'), 6, 0, 0, Buffer.from(this.pathBuffer)); + /* APDU 1 = + contract_address (32 bytes) + + class_hash (32 bytes) + + contract_address_salt (32 bytes) + + chain_id (32 bytes) + + nonce (32 bytes) + */ + const accountAddressBuf: Uint8Array = this.convertBnToLedger( + deployAccountDetail.contractAddress + ); + const classHashBuf: Uint8Array = this.convertBnToLedger(deployAccountDetail.classHash); + const saltBuf: Uint8Array = this.convertBnToLedger(deployAccountDetail.addressSalt); + const chainIdBuf: Uint8Array = this.convertBnToLedger(deployAccountDetail.chainId); + const nonceBuf: Uint8Array = this.convertBnToLedger(deployAccountDetail.nonce); + const dataBuf: Uint8Array = concatenateArrayBuffer([ + accountAddressBuf, + classHashBuf, + saltBuf, + chainIdBuf, + nonceBuf, + ]); + await this._transporter.send(Number('0x5a'), 6, 1, 0, Buffer.from(dataBuf)); + // APDU 2 = Nb of calls + const maxFreeBuf: Uint8Array = this.convertBnToLedger(deployAccountDetail.maxFee); + await this._transporter.send(Number('0x5a'), 6, 2, 0, Buffer.from(maxFreeBuf)); + // APDU 3 = constructor length + const compiledConstructor = CallData.compile(deployAccountDetail.constructorCalldata); + const constructorLengthBuf: Uint8Array = this.convertBnToLedger(compiledConstructor.length); + await this._transporter.send(Number('0x5a'), 6, 3, 0, Buffer.from(constructorLengthBuf)); + // APDU 4 = constructor + const constructorBuf = concatenateArrayBuffer( + compiledConstructor.map((parameter: string): Uint8Array => { + const a = this.convertBnToLedger(parameter); + return a; + }) + ); + const constructorChunks: Uint8Array[] = []; + const chunkSize = 7 * 32; // 224 bytes + for (let i = 0; i < constructorBuf.length; i += chunkSize) + constructorChunks.push(constructorBuf.subarray(i, i + chunkSize)); + let respSign: Uint8Array = new Uint8Array(0); + // eslint-disable-next-line no-restricted-syntax + for (const chunk of constructorChunks) { + respSign = await this._transporter.send(Number('0x5a'), 6, 4, 0, Buffer.from(chunk)); + } + return this.decodeSignatureLedger(respSign); + } + + /** + *Ask the Ledger Nano to display and sign a Starknet V3 account deployment. + * @param {V3DeployAccountSignerDetails} deployAccountDetail All the details needed for a V3 deploy account. + * @returns an object including the transaction Hash and the signature + * @example + * ```typescript + * const deployData: V3DeployAccountSignerDetails = + * { + * tip: 0, paymasterData: [], accountDeploymentData: [], + * nonceDataAvailabilityMode: 'L1', feeDataAvailabilityMode: 'L1', + * resourceBounds: { + * l2_gas: { max_amount: '0x0', max_price_per_unit: '0x0' }, + * l1_gas: { max_amount: '0x226', max_price_per_unit: '0x22ecb25c00' } + * }, + * classHash: '0x540d7f5ec7ecf317e68d48564934cb99259781b1ee3cedbbc37ec5337f8e688', + * constructorCalldata: [ + * '3571125127744830445572285574469842579401255431821644822726857471463672199621' + * ], + * contractAddress: '0x4ca062add1cf12a107be1107af17981cf6e544a24d987693230ea481d3d5e34', + * addressSalt: '0x07e52f68e3160e1ef698211cdf6d3792368fe347e7e2d4a8ace14d9b248f39c5', + * chainId: '0x534e5f5345504f4c4941', maxFee: 0, + * version: '0x3', nonce: 0n + *} + * const res = await myLedgerSigner.signDeployAccountV3(deployData); + * // res = {hash: + * // signature: + * // } + * ``` + */ + public async signDeployAccountV3( + deployAccountDetail: V3DeployAccountSignerDetails + ): Promise<{ hash: bigint; signature: Signature }> { + // APDU 0 for path + await this._transporter.send(Number('0x5a'), 5, 0, 0, Buffer.from(this.pathBuffer)); + /* APDU 1 = + contract_address (32 bytes) + + chain_id (32 bytes) + + nonce (32 bytes) + + data_availability_mode (32 bytes) + + class_hash (32 bytes) + + contract_address_salt (32 bytes) + */ + const accountAddressBuf: Uint8Array = this.convertBnToLedger( + deployAccountDetail.contractAddress + ); + const chainIdBuf: Uint8Array = this.convertBnToLedger(deployAccountDetail.chainId); + const nonceBuf: Uint8Array = this.convertBnToLedger(deployAccountDetail.nonce); + const dAModeHashBuf = this.convertBnToLedger( + hashDAMode( + deployAccountDetail.nonceDataAvailabilityMode === EDataAvailabilityMode.L1 + ? EDAMode.L1 + : EDAMode.L2, + deployAccountDetail.feeDataAvailabilityMode === EDataAvailabilityMode.L1 + ? EDAMode.L1 + : EDAMode.L2 + ) + ); + const classHashBuf: Uint8Array = this.convertBnToLedger(deployAccountDetail.classHash); + const saltBuf: Uint8Array = this.convertBnToLedger(deployAccountDetail.addressSalt); + const dataBuf: Uint8Array = concatenateArrayBuffer([ + accountAddressBuf, + chainIdBuf, + nonceBuf, + dAModeHashBuf, + classHashBuf, + saltBuf, + ]); + await this._transporter.send(Number('0x5a'), 5, 1, 0, Buffer.from(dataBuf)); + // APDU 2 = fees + const tipBuf = this.convertBnToLedger(deployAccountDetail.tip); + const l1_gasBuf = this.convertBnToLedger( + encodeResourceBoundsL1(deployAccountDetail.resourceBounds) + ); + const l2_gasBuf = this.convertBnToLedger( + encodeResourceBoundsL2(deployAccountDetail.resourceBounds) + ); + const feeBuf: Uint8Array = concatenateArrayBuffer([tipBuf, l1_gasBuf, l2_gasBuf]); + await this._transporter.send(Number('0x5a'), 5, 2, 0, Buffer.from(feeBuf)); + // APDU 3 = paymaster data + const paymasterBuf = concatenateArrayBuffer( + deployAccountDetail.paymasterData.map((value: BigNumberish): Uint8Array => { + const a = this.convertBnToLedger(value); + return a; + }) + ); + await this._transporter.send(Number('0x5a'), 5, 3, 0, Buffer.from(paymasterBuf)); + // APDU 4 = constructor length + const compiledConstructor = CallData.compile(deployAccountDetail.constructorCalldata); + const constructorLengthBuf: Uint8Array = this.convertBnToLedger(compiledConstructor.length); + await this._transporter.send(Number('0x5a'), 5, 4, 0, Buffer.from(constructorLengthBuf)); + // APDU 4 = constructor + const constructorBuf = concatenateArrayBuffer( + compiledConstructor.map((parameter: string): Uint8Array => { + const a = this.convertBnToLedger(parameter); + return a; + }) + ); + const constructorChunks: Uint8Array[] = []; + const chunkSize = 7 * 32; // 224 bytes + for (let i = 0; i < constructorBuf.length; i += chunkSize) + constructorChunks.push(constructorBuf.subarray(i, i + chunkSize)); + let respSign: Uint8Array = new Uint8Array(0); + // eslint-disable-next-line no-restricted-syntax + for (const chunk of constructorChunks) { + respSign = await this._transporter.send(Number('0x5a'), 5, 5, 0, Buffer.from(chunk)); + } + return this.decodeSignatureLedger(respSign); + } +} + +/** + * Format the Ledger wallet path to an Uint8Array. + * for a Ledger Starknet DAPP v2.2.0 + * EIP2645 path = 2645'/starknet'/application'/0'/accountId'/0 + * @param {number} accountId Id of account. < 2**31. + * @param {string} [applicationName='LedgerW'] utf8 string of application name. + * @returns an Uint8array of 24 bytes. + * @example + * ```typescript + * const result = getLedgerPathBuffer211(0); + * // result = Uint8Array(24) [ + * 128, 0, 10, 85, 199, 65, 233, 201, + * 171, 206, 231, 219, 128, 0, 0, 0, + * 128, 0, 0, 0, 0, 0, 0, 0 + * ] + * ``` + */ +export function getLedgerPathBuffer221( + accountId: number, + applicationName: string = 'LedgerW' +): Uint8Array { + const path0buff = new Uint8Array([HARDENING_BYTE, 0, 10, 85]); // "0x80000A55" EIP2645; + const path1buff = new Uint8Array([71 | HARDENING_BYTE, 65, 233, 201]); // "starknet'" + const path2Base = + applicationName === 'LedgerW' + ? new Uint8Array([43, 206, 231, 219]) + : stringToSha256ToArrayBuff4(applicationName); + const path2buff = concatenateArrayBuffer([ + new Uint8Array([path2Base[0] | HARDENING_BYTE]), + path2Base.subarray(1), + ]); + const path3buff = new Uint8Array([HARDENING_BYTE, 0, 0, 0]); + const hex = toHex(BigInt(accountId) | HARDENING_4BYTES); + const padded = addHexPrefix(removeHexPrefix(hex).padStart(8, '0')); + const path4buff = hexToBytes(padded); + const path5buff = new Uint8Array([0, 0, 0, 0]); + const pathBuff = concatenateArrayBuffer([ + path0buff, + path1buff, + path2buff, + path3buff, + path4buff, + path5buff, + ]); + return pathBuff; +} diff --git a/src/types/calldata.ts b/src/types/calldata.ts index b87b33e1c..c496adccb 100644 --- a/src/types/calldata.ts +++ b/src/types/calldata.ts @@ -34,6 +34,7 @@ export const Literal = { ClassHash: 'core::starknet::class_hash::ClassHash', ContractAddress: 'core::starknet::contract_address::ContractAddress', Secp256k1Point: 'core::starknet::secp256k1::Secp256k1Point', + U96: 'core::internal::bounded_int::BoundedInt::<0, 79228162514264337593543950335>', } as const; export type Literal = ValuesType; diff --git a/src/types/contract.ts b/src/types/contract.ts index 01dacf9b0..94389c01e 100644 --- a/src/types/contract.ts +++ b/src/types/contract.ts @@ -1,7 +1,9 @@ +import { BlockHash, TransactionHash } from 'starknet-types-07'; import { CairoEnum } from './cairoEnum'; import { BigNumberish, BlockIdentifier, + BlockNumber, Calldata, ParsedStruct, RawArgsArray, @@ -44,6 +46,10 @@ export type InvokeOptions = Pick< 'maxFee' | 'nonce' | 'signature' | 'parseRequest' >; -export type ParsedEvent = { [name: string]: ParsedStruct }; +export type ParsedEvent = { [name: string]: ParsedStruct } & { + block_hash?: BlockHash; + block_number?: BlockNumber; + transaction_hash?: TransactionHash; +}; export type ParsedEvents = Array; diff --git a/src/types/errors.ts b/src/types/errors.ts new file mode 100644 index 000000000..d8ee90eb0 --- /dev/null +++ b/src/types/errors.ts @@ -0,0 +1,33 @@ +import { Errors } from 'starknet-types-07'; + +// NOTE: generated with scripts/generateRpcErrorMap.js +export type RPC_ERROR_SET = { + FAILED_TO_RECEIVE_TXN: Errors.FAILED_TO_RECEIVE_TXN; + NO_TRACE_AVAILABLE: Errors.NO_TRACE_AVAILABLE; + CONTRACT_NOT_FOUND: Errors.CONTRACT_NOT_FOUND; + BLOCK_NOT_FOUND: Errors.BLOCK_NOT_FOUND; + INVALID_TXN_INDEX: Errors.INVALID_TXN_INDEX; + CLASS_HASH_NOT_FOUND: Errors.CLASS_HASH_NOT_FOUND; + TXN_HASH_NOT_FOUND: Errors.TXN_HASH_NOT_FOUND; + PAGE_SIZE_TOO_BIG: Errors.PAGE_SIZE_TOO_BIG; + NO_BLOCKS: Errors.NO_BLOCKS; + INVALID_CONTINUATION_TOKEN: Errors.INVALID_CONTINUATION_TOKEN; + TOO_MANY_KEYS_IN_FILTER: Errors.TOO_MANY_KEYS_IN_FILTER; + CONTRACT_ERROR: Errors.CONTRACT_ERROR; + TRANSACTION_EXECUTION_ERROR: Errors.TRANSACTION_EXECUTION_ERROR; + CLASS_ALREADY_DECLARED: Errors.CLASS_ALREADY_DECLARED; + INVALID_TRANSACTION_NONCE: Errors.INVALID_TRANSACTION_NONCE; + INSUFFICIENT_MAX_FEE: Errors.INSUFFICIENT_MAX_FEE; + INSUFFICIENT_ACCOUNT_BALANCE: Errors.INSUFFICIENT_ACCOUNT_BALANCE; + VALIDATION_FAILURE: Errors.VALIDATION_FAILURE; + COMPILATION_FAILED: Errors.COMPILATION_FAILED; + CONTRACT_CLASS_SIZE_IS_TOO_LARGE: Errors.CONTRACT_CLASS_SIZE_IS_TOO_LARGE; + NON_ACCOUNT: Errors.NON_ACCOUNT; + DUPLICATE_TX: Errors.DUPLICATE_TX; + COMPILED_CLASS_HASH_MISMATCH: Errors.COMPILED_CLASS_HASH_MISMATCH; + UNSUPPORTED_TX_VERSION: Errors.UNSUPPORTED_TX_VERSION; + UNSUPPORTED_CONTRACT_CLASS_VERSION: Errors.UNSUPPORTED_CONTRACT_CLASS_VERSION; + UNEXPECTED_ERROR: Errors.UNEXPECTED_ERROR; +}; + +export type RPC_ERROR = RPC_ERROR_SET[keyof RPC_ERROR_SET]; diff --git a/src/types/index.ts b/src/types/index.ts index 08ef3e361..9c87191a0 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,12 +1,14 @@ +export * from './lib'; +export * from './provider'; + export * from './account'; +export * from './cairoEnum'; export * from './calldata'; export * from './contract'; -export * from './lib'; -export * from './provider'; +export * from './errors'; +export * from './outsideExecution'; export * from './signer'; -export * from './typedData'; -export * from './cairoEnum'; export * from './transactionReceipt'; -export * from './outsideExecution'; +export * from './typedData'; export * as RPC from './api'; diff --git a/src/types/signer.ts b/src/types/signer.ts index 80d98a8d6..f5f4c49bf 100644 --- a/src/types/signer.ts +++ b/src/types/signer.ts @@ -66,3 +66,5 @@ export type V3DeployAccountSignerDetails = Required Uint8Array; diff --git a/src/utils/calldata/cairo.ts b/src/utils/calldata/cairo.ts index 813cc87d4..3c6fbc6a1 100644 --- a/src/utils/calldata/cairo.ts +++ b/src/utils/calldata/cairo.ts @@ -147,6 +147,15 @@ export const isTypeBytes31 = (type: string) => type === 'core::bytes_31::bytes31 */ export const isTypeByteArray = (type: string) => type === 'core::byte_array::ByteArray'; +/** + * Checks if the given type is equal to the u96 type + * + * @param {string} type - The type to check. + * @returns - True if the given type is equal to u96, false otherwise. + */ +export const isTypeU96 = (type: string) => + type === 'core::internal::bounded_int::BoundedInt::<0, 79228162514264337593543950335>'; + export const isTypeSecp256k1Point = (type: string) => type === Literal.Secp256k1Point; export const isCairo1Type = (type: string) => type.includes('::'); diff --git a/src/utils/calldata/propertyOrder.ts b/src/utils/calldata/propertyOrder.ts index a6d046cdc..550c7ecae 100644 --- a/src/utils/calldata/propertyOrder.ts +++ b/src/utils/calldata/propertyOrder.ts @@ -15,6 +15,7 @@ import { isTypeSecp256k1Point, isTypeStruct, isTypeTuple, + isTypeU96, } from './cairo'; import { CairoCustomEnum, @@ -65,6 +66,9 @@ export default function orderPropsByAbi( if (isTypeByteArray(abiType)) { return unorderedItem; } + if (isTypeU96(abiType)) { + return unorderedItem; + } if (isTypeSecp256k1Point(abiType)) { return unorderedItem; } diff --git a/src/utils/calldata/validate.ts b/src/utils/calldata/validate.ts index 18f010622..21c90f7d0 100644 --- a/src/utils/calldata/validate.ts +++ b/src/utils/calldata/validate.ts @@ -199,6 +199,13 @@ const validateUint = (parameter: any, input: AbiEntry) => { ); break; } + case Literal.U96: { + assert( + param >= 0n && param <= 2n ** 96n - 1n, + `Validate: arg ${input.name} must be ${input.type} : a 96 bits number.` + ); + break; + } default: break; diff --git a/src/provider/errors.ts b/src/utils/errors/index.ts similarity index 59% rename from src/provider/errors.ts rename to src/utils/errors/index.ts index 1e2a01b4b..2207a6049 100644 --- a/src/provider/errors.ts +++ b/src/utils/errors/index.ts @@ -1,3 +1,8 @@ +/* eslint-disable max-classes-per-file */ +import { RPC, RPC_ERROR, RPC_ERROR_SET } from '../../types'; +import { stringify } from '../json'; +import rpcErrors from './rpc'; + // eslint-disable-next-line max-classes-per-file export function fixStack(target: Error, fn: Function = target.constructor) { const { captureStackTrace } = Error as any; @@ -36,20 +41,38 @@ export class CustomError extends Error { export class LibraryError extends CustomError {} -export class GatewayError extends LibraryError { +export class RpcError extends LibraryError { + public readonly request: { + method: string; + params: any; + }; + constructor( - message: string, - public errorCode: string + public readonly baseError: BaseErrorT, + method: string, + params: any ) { - super(message); + // legacy message format + super(`RPC: ${method} with params ${stringify(params, null, 2)}\n + ${baseError.code}: ${baseError.message}: ${stringify((baseError as RPC.JRPC.Error).data)}`); + + this.request = { method, params }; } -} -export class HttpError extends LibraryError { - constructor( - message: string, - public errorCode: number - ) { - super(message); + public get code() { + return this.baseError.code; + } + + /** + * Verifies the underlying RPC error, also serves as a type guard for the _baseError_ property + * @example + * ```typescript + * SomeError.isType('UNEXPECTED_ERROR'); + * ``` + */ + public isType( + typeName: N + ): this is RpcError { + return rpcErrors[typeName] === this.code; } } diff --git a/src/utils/errors/rpc.ts b/src/utils/errors/rpc.ts new file mode 100644 index 000000000..fc174034c --- /dev/null +++ b/src/utils/errors/rpc.ts @@ -0,0 +1,32 @@ +import { RPC_ERROR_SET } from '../../types'; + +// NOTE: generated with scripts/generateRpcErrorMap.js +const errorCodes: { [K in keyof RPC_ERROR_SET]: RPC_ERROR_SET[K]['code'] } = { + FAILED_TO_RECEIVE_TXN: 1, + NO_TRACE_AVAILABLE: 10, + CONTRACT_NOT_FOUND: 20, + BLOCK_NOT_FOUND: 24, + INVALID_TXN_INDEX: 27, + CLASS_HASH_NOT_FOUND: 28, + TXN_HASH_NOT_FOUND: 29, + PAGE_SIZE_TOO_BIG: 31, + NO_BLOCKS: 32, + INVALID_CONTINUATION_TOKEN: 33, + TOO_MANY_KEYS_IN_FILTER: 34, + CONTRACT_ERROR: 40, + TRANSACTION_EXECUTION_ERROR: 41, + CLASS_ALREADY_DECLARED: 51, + INVALID_TRANSACTION_NONCE: 52, + INSUFFICIENT_MAX_FEE: 53, + INSUFFICIENT_ACCOUNT_BALANCE: 54, + VALIDATION_FAILURE: 55, + COMPILATION_FAILED: 56, + CONTRACT_CLASS_SIZE_IS_TOO_LARGE: 57, + NON_ACCOUNT: 58, + DUPLICATE_TX: 59, + COMPILED_CLASS_HASH_MISMATCH: 60, + UNSUPPORTED_TX_VERSION: 61, + UNSUPPORTED_CONTRACT_CLASS_VERSION: 62, + UNEXPECTED_ERROR: 63, +}; +export default errorCodes; diff --git a/src/utils/events/index.ts b/src/utils/events/index.ts index 498d31110..ecdcdcf0a 100644 --- a/src/utils/events/index.ts +++ b/src/utils/events/index.ts @@ -194,57 +194,62 @@ function mergeAbiEvents(target: any, source: any): Object { * ``` */ export function parseEvents( - providerReceivedEvents: RPC.Event[], + providerReceivedEvents: RPC.EmittedEvent[], abiEvents: AbiEvents, abiStructs: AbiStructs, abiEnums: AbiEnums ): ParsedEvents { - const ret = providerReceivedEvents.flat().reduce((acc, recEvent: RPC.Event) => { - let abiEvent: AbiEvent | AbiEvents = abiEvents[recEvent.keys.shift() ?? 0]; - if (!abiEvent) { - return acc; - } - while (!abiEvent.name) { - const hashName = recEvent.keys.shift(); - assert(!!hashName, 'Not enough data in "keys" property of this event.'); - abiEvent = (abiEvent as AbiEvents)[hashName]; - } - // Create our final event object - const parsedEvent: ParsedEvent = {}; - parsedEvent[abiEvent.name as string] = {}; - // Remove the event's name hashed from the keys array - const keysIter = recEvent.keys[Symbol.iterator](); - const dataIter = recEvent.data[Symbol.iterator](); + const ret = providerReceivedEvents + .flat() + .reduce((acc, recEvent: RPC.EmittedEvent | RPC.Event) => { + let abiEvent: AbiEvent | AbiEvents = abiEvents[recEvent.keys.shift() ?? 0]; + if (!abiEvent) { + return acc; + } + while (!abiEvent.name) { + const hashName = recEvent.keys.shift(); + assert(!!hashName, 'Not enough data in "keys" property of this event.'); + abiEvent = (abiEvent as AbiEvents)[hashName]; + } + // Create our final event object + const parsedEvent: ParsedEvent = {}; + parsedEvent[abiEvent.name as string] = {}; + // Remove the event's name hashed from the keys array + const keysIter = recEvent.keys[Symbol.iterator](); + const dataIter = recEvent.data[Symbol.iterator](); - const abiEventKeys = - (abiEvent as CairoEventDefinition).members?.filter((it) => it.kind === 'key') || - (abiEvent as LegacyEvent).keys; - const abiEventData = - (abiEvent as CairoEventDefinition).members?.filter((it) => it.kind === 'data') || - (abiEvent as LegacyEvent).data; + const abiEventKeys = + (abiEvent as CairoEventDefinition).members?.filter((it) => it.kind === 'key') || + (abiEvent as LegacyEvent).keys; + const abiEventData = + (abiEvent as CairoEventDefinition).members?.filter((it) => it.kind === 'data') || + (abiEvent as LegacyEvent).data; - abiEventKeys.forEach((key) => { - parsedEvent[abiEvent.name as string][key.name] = responseParser( - keysIter, - key, - abiStructs, - abiEnums, - parsedEvent[abiEvent.name as string] - ); - }); + abiEventKeys.forEach((key) => { + parsedEvent[abiEvent.name as string][key.name] = responseParser( + keysIter, + key, + abiStructs, + abiEnums, + parsedEvent[abiEvent.name as string] + ); + }); - abiEventData.forEach((data) => { - parsedEvent[abiEvent.name as string][data.name] = responseParser( - dataIter, - data, - abiStructs, - abiEnums, - parsedEvent[abiEvent.name as string] - ); - }); - acc.push(parsedEvent); - return acc; - }, [] as ParsedEvents); + abiEventData.forEach((data) => { + parsedEvent[abiEvent.name as string][data.name] = responseParser( + dataIter, + data, + abiStructs, + abiEnums, + parsedEvent[abiEvent.name as string] + ); + }); + if ('block_hash' in recEvent) parsedEvent.block_hash = recEvent.block_hash; + if ('block_number' in recEvent) parsedEvent.block_number = recEvent.block_number; + if ('transaction_hash' in recEvent) parsedEvent.transaction_hash = recEvent.transaction_hash; + acc.push(parsedEvent); + return acc; + }, [] as ParsedEvents); return ret; } diff --git a/src/utils/hash/transactionHash/v3.ts b/src/utils/hash/transactionHash/v3.ts index 7746c67be..cea6030a7 100644 --- a/src/utils/hash/transactionHash/v3.ts +++ b/src/utils/hash/transactionHash/v3.ts @@ -24,17 +24,38 @@ export function hashDAMode(nonceDAMode: BigNumberish, feeDAMode: BigNumberish) { return (BigInt(nonceDAMode) << DATA_AVAILABILITY_MODE_BITS) + BigInt(feeDAMode); } -export function hashFeeField(tip: BigNumberish, bounds: ResourceBounds) { - const L1Bound = +/** + * Encode the L1&L2 gas limits of a V3 transaction + * @param {ResourceBounds} bounds object including the limits for L1 & L2 gas + * @returns {bigint} encoded data + */ +export function encodeResourceBoundsL1(bounds: ResourceBounds): bigint { + return ( (L1_GAS_NAME << RESOURCE_VALUE_OFFSET) + (BigInt(bounds.l1_gas.max_amount) << MAX_PRICE_PER_UNIT_BITS) + - BigInt(bounds.l1_gas.max_price_per_unit); + BigInt(bounds.l1_gas.max_price_per_unit) + ); +} - const L2Bound = +/** + * Encode the L2 bound of a V3 transaction + * @param {ResourceBounds} bounds + * {l1_gas: {max_amount: u64, max_price_per_unit: u128}, + * l2_gas: {max_amount: u64, max_price_per_unit: u128}} +} + * @returns {bigint} encoded data + */ +export function encodeResourceBoundsL2(bounds: ResourceBounds): bigint { + return ( (L2_GAS_NAME << RESOURCE_VALUE_OFFSET) + (BigInt(bounds.l2_gas.max_amount) << MAX_PRICE_PER_UNIT_BITS) + - BigInt(bounds.l2_gas.max_price_per_unit); + BigInt(bounds.l2_gas.max_price_per_unit) + ); +} +export function hashFeeField(tip: BigNumberish, bounds: ResourceBounds) { + const L1Bound = encodeResourceBoundsL1(bounds); + const L2Bound = encodeResourceBoundsL2(bounds); return poseidonHashMany([BigInt(tip), L1Bound, L2Bound]); } diff --git a/src/wallet/account.ts b/src/wallet/account.ts index 8a1bccffc..dbe3eb323 100644 --- a/src/wallet/account.ts +++ b/src/wallet/account.ts @@ -1,11 +1,13 @@ import type { - Signature, AccountChangeEventHandler, AddStarknetChainParameters, NetworkChangeEventHandler, + Signature, WatchAssetParameters, } from 'starknet-types-07'; + import { Account, AccountInterface } from '../account'; +import { StarknetChainId } from '../constants'; import { ProviderInterface } from '../provider'; import { AllowArray, @@ -34,20 +36,35 @@ import { watchAsset, } from './connect'; import { StarknetWalletProvider } from './types'; -import { StarknetChainId } from '../constants'; +// TODO: Remove non address constructor in next major version // Represent 'Selected Active' Account inside Connected Wallet export class WalletAccount extends Account implements AccountInterface { public address: string = ''; public walletProvider: StarknetWalletProvider; + /** + * @deprecated Use static method WalletAccount.connect or WalletAccount.connectSilent instead. Constructor {@link WalletAccount.(format:2)}. + */ constructor( providerOrOptions: ProviderOptions | ProviderInterface, walletProvider: StarknetWalletProvider, cairoVersion?: CairoVersion + ); + constructor( + providerOrOptions: ProviderOptions | ProviderInterface, + walletProvider: StarknetWalletProvider, + cairoVersion?: CairoVersion, + address?: string + ); + constructor( + providerOrOptions: ProviderOptions | ProviderInterface, + walletProvider: StarknetWalletProvider, + cairoVersion?: CairoVersion, + address: string = '' ) { - super(providerOrOptions, '', '', cairoVersion); // At this point unknown address + super(providerOrOptions, address, '', cairoVersion); // At this point unknown address this.walletProvider = walletProvider; // Update Address on change @@ -64,17 +81,14 @@ export class WalletAccount extends Account implements AccountInterface { this.channel.setChainId(res as StarknetChainId); }); - // Get and Set Address !!! Post constructor initial empty string - walletProvider - .request({ - type: 'wallet_requestAccounts', - params: { - silent_mode: false, - }, - }) - .then((res) => { - this.address = res[0].toLowerCase(); + if (!address.length) { + console.warn( + '@deprecated Use static method WalletAccount.connect or WalletAccount.connectSilent instead. Constructor {@link WalletAccount.(format:2)}.' + ); + requestAccounts(this.walletProvider).then(([accountAddress]) => { + this.address = accountAddress.toLowerCase(); }); + } } /** @@ -170,5 +184,23 @@ export class WalletAccount extends Account implements AccountInterface { return signMessage(this.walletProvider, typedData); } + static async connect( + provider: ProviderInterface, + walletProvider: StarknetWalletProvider, + cairoVersion?: CairoVersion, + silentMode: boolean = false + ) { + const [accountAddress] = await requestAccounts(walletProvider, silentMode); + return new WalletAccount(provider, walletProvider, cairoVersion, accountAddress); + } + + static async connectSilent( + provider: ProviderInterface, + walletProvider: StarknetWalletProvider, + cairoVersion?: CairoVersion + ) { + return WalletAccount.connect(provider, walletProvider, cairoVersion, true); + } + // TODO: MISSING ESTIMATES } diff --git a/www/docs/guides/automatic_cairo_ABI_parsing.md b/www/docs/guides/automatic_cairo_ABI_parsing.md index c34f3f2f3..11aebb085 100644 --- a/www/docs/guides/automatic_cairo_ABI_parsing.md +++ b/www/docs/guides/automatic_cairo_ABI_parsing.md @@ -14,7 +14,7 @@ Please take a look on the Abi-Wan [documentation](https://github.com/keep-starkn ## Usage -First, you need to wrap your ABI in a array and export it as a `const`. +First, you need to wrap your ABI in an array and export it as a `const`. Example: diff --git a/www/docs/guides/cairo_enum.md b/www/docs/guides/cairo_enum.md index 1f6b24c3b..ed89d1f73 100644 --- a/www/docs/guides/cairo_enum.md +++ b/www/docs/guides/cairo_enum.md @@ -86,7 +86,7 @@ const res2 = (await myTestContract.call('test5', [ ## Cairo Result -Cairo v2.1.0 introduces an other core Enum: `Result`. +Cairo v2.1.0 introduces another core Enum: `Result`. This Enum has 2 variants (`Ok` and `Err`) and both variants can contain data. ### Receive Cairo Result diff --git a/www/docs/guides/connect_network.md b/www/docs/guides/connect_network.md index 0a097d905..48bdc9c97 100644 --- a/www/docs/guides/connect_network.md +++ b/www/docs/guides/connect_network.md @@ -200,3 +200,17 @@ const [getBlockResponse, blockHashAndNumber, txCount] = await Promise.all([ // ... usage of getBlockResponse, blockHashAndNumber, txCount ``` + +## Error handling + +The [Starknet RPC specification](https://github.com/starkware-libs/starknet-specs) defines a set of possible errors that the RPC endpoints could return for various scenarios. If such errors arise `starknet.js` represents them with the corresponding [RpcError](../API/classes/RpcError) class where the endpoint error response information is contained within the `baseError` property. Also of note is that the class has an `isType` convenience method that verifies the base error type as shown in the example below. + +#### Example + +```typescript +try { + ... +} catch (error) { + if (error instanceof RpcError && error.isType('UNEXPECTED_ERROR')) { ... } +} +``` diff --git a/www/docs/guides/define_call_message.md b/www/docs/guides/define_call_message.md index 967007bf3..460dd4712 100644 --- a/www/docs/guides/define_call_message.md +++ b/www/docs/guides/define_call_message.md @@ -21,7 +21,7 @@ Cairo has 2 versions, involving 2 types of data: - **Cairo 0**: here, everything is felt, an integer on 251 bits. Available: array, struct, tuple, named tuple, or a mix of these elements. -- **Cairo 1**: with plethora of literal types: u8, u16, u32, usize, u64, u128, felt252, u256, bool, address, eth address, classHash. +- **Cairo 1**: with plethora of literal types: u8, u16, u32, usize, u64, u96, u128, felt252, u256, bool, address, eth address, classHash. Available: array, struct, tuple, bytes31, byteArray, enums or a mix of these elements. Starknet.js is compatible with both versions. @@ -49,7 +49,7 @@ const decimals: BigNumberish = 18; If your Cairo smart contract is waiting for a: -### felt, u8, u16, u32, usize, u64, u128, felt252, ContractAddress, EthAddress, ClassHash +### felt, u8, u16, u32, usize, u64, u96, u128, felt252, ContractAddress, EthAddress, ClassHash Starknet is waiting for a felt. You can send to Starknet.js methods: bigNumberish. @@ -538,21 +538,21 @@ const amount = res.amount; const amount = myContract.call(...); ``` -| Type in Cairo 1 | Cairo 1 code | Type expected in JS/TS | JS/TS function to recover data | -| --------------------------------------------------------- | ---------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| u8, u16, u32, usize, u64, u128, felt252, address | `func get_v()->u128` | bigint | `const res: bigint = myContract.call(...` | -| | | string representing an hex number | `const res=myContract.call(...`
`const address: string = num.toHex(res);` | -| u8, u16, u32, usize | `func get_v() -> u16` | number (53 bits max) | `const res=myContract.call(...`
`const total: number = Number(res)` | -| u256 (255 bits max) | `func get_v() -> u256` | bigint | `const res: bigint = myContract.call(...` | -| u512 (512 bits max) | `func get_v() -> u512` | bigint | `const res: bigint = myContract.call(...` | -| array of u8, u16, u32, usize, u64, u128, felt252, address | `func get_v() -> Array` | bigint[] | `const res: bigint[] = myContract.call(...` | -| bytes31 (31 ASCII characters max) | `func get_v() -> bytes31` | string | `const res: string = myContract.call(...` | -| felt252 (31 ASCII characters max) | `func get_v() -> felt252` | string | `const res = myContract.call(...`
`const title:string = shortString.decodeShortstring(res);` | -| longString | `func get_v() -> Array` | string | `const res=myContract.call(...`
`const longString = res.map( (shortStr: bigint) => { return shortString.decodeShortString( num.toHex( shortStr)) }).join("");` | -| ByteArray | `func get_v() -> ByteArray` | string | `const res: string = myContract.call(...` | -| Tuple | `func get_v() -> (felt252, u8)` | Object {"0": bigint, "1": bigint} | `const res = myContract.call(...`
`const res0: bigint = res["0"];`
`const results: bigint[] = Object.values(res)` | -| Struct | ` func get_v() -> MyStruct` | MyStruct = { account: bigint, amount: bigint} | `const res: MyStruct = myContract.call(...` | -| complex array | `func get_v() -> Array` | MyStruct[] | `const res: MyStruct[] = myContract.call(...` | +| Type in Cairo 1 | Cairo 1 code | Type expected in JS/TS | JS/TS function to recover data | +| -------------------------------------------------------------- | ---------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| u8, u16, u32, usize, u64, u96, u128, felt252, address | `func get_v()->u128` | bigint | `const res: bigint = myContract.call(...` | +| | | string representing an hex number | `const res=myContract.call(...`
`const address: string = num.toHex(res);` | +| u8, u16, u32, usize | `func get_v() -> u16` | number (53 bits max) | `const res=myContract.call(...`
`const total: number = Number(res)` | +| u256 (255 bits max) | `func get_v() -> u256` | bigint | `const res: bigint = myContract.call(...` | +| u512 (512 bits max) | `func get_v() -> u512` | bigint | `const res: bigint = myContract.call(...` | +| array of u8, u16, u32, usize, u64, u96, u128, felt252, address | `func get_v() -> Array` | bigint[] | `const res: bigint[] = myContract.call(...` | +| bytes31 (31 ASCII characters max) | `func get_v() -> bytes31` | string | `const res: string = myContract.call(...` | +| felt252 (31 ASCII characters max) | `func get_v() -> felt252` | string | `const res = myContract.call(...`
`const title:string = shortString.decodeShortstring(res);` | +| longString | `func get_v() -> Array` | string | `const res=myContract.call(...`
`const longString = res.map( (shortStr: bigint) => { return shortString.decodeShortString( num.toHex( shortStr)) }).join("");` | +| ByteArray | `func get_v() -> ByteArray` | string | `const res: string = myContract.call(...` | +| Tuple | `func get_v() -> (felt252, u8)` | Object {"0": bigint, "1": bigint} | `const res = myContract.call(...`
`const res0: bigint = res["0"];`
`const results: bigint[] = Object.values(res)` | +| Struct | ` func get_v() -> MyStruct` | MyStruct = { account: bigint, amount: bigint} | `const res: MyStruct = myContract.call(...` | +| complex array | `func get_v() -> Array` | MyStruct[] | `const res: MyStruct[] = myContract.call(...` | If you don't know if your Contract object is interacting with a Cairo 0 or a Cairo 1 contract, you have these methods: diff --git a/www/docs/guides/estimate_fees.md b/www/docs/guides/estimate_fees.md index 2c8d4f3b6..de24f8d3d 100644 --- a/www/docs/guides/estimate_fees.md +++ b/www/docs/guides/estimate_fees.md @@ -4,7 +4,7 @@ sidebar_position: 11 # Estimate fees -By default, all nonfree Starknet commands (declare, deploy, invoke) work without any limitation of cost. +By default, all non-free Starknet commands (declare, deploy, invoke) work without any limitation of cost. Nevertheless, you might want to inform the DAPP user of the cost of the incoming transaction before proceeding and requesting its validation. diff --git a/www/docs/guides/migrate.md b/www/docs/guides/migrate.md index 37483ef84..047cd3b1b 100644 --- a/www/docs/guides/migrate.md +++ b/www/docs/guides/migrate.md @@ -11,7 +11,7 @@ If you encounter any missing changes, please let us know and we will update this ## Transaction receipt When sending a transaction, the receipt type has changed. -In V5, it's an object that can have varied definitions, depending of the status and the type of transaction. +In V5, it's an object that can have varied definitions, depending on the status and the type of transaction. In V6, this object is in `TxR.value`, and several helpers are available (`.statusReceipt`, `isSuccess()`, `isRejected()`, `isReverted()`, `.isError()`, `match`, ...) ```typescript diff --git a/www/docs/guides/multiCall.md b/www/docs/guides/multiCall.md index 48a985263..381135aa2 100644 --- a/www/docs/guides/multiCall.md +++ b/www/docs/guides/multiCall.md @@ -18,7 +18,7 @@ const accountAddress = '0x7e00d496e324876bbc8531f2d9a82bf154d1a04a50218ee74cdd37 // Ether token contract address const contractAddress_1 = '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7'; -// contract address which require ether +// contract address which requires ether const contractAddress_2 = '0x078f36c1d59dd29e00a0bb60aa2a9409856f4f9841c47f165aba5bab4225aa6b'; const account = new Account(provider, accountAddress, privateKey); diff --git a/www/docs/guides/signature.md b/www/docs/guides/signature.md index 0c2044933..7728154f6 100644 --- a/www/docs/guides/signature.md +++ b/www/docs/guides/signature.md @@ -209,9 +209,9 @@ console.log('signature message =', sig0); ![](./pictures/LedgerTitle.png) Starknet.js has a support for Ledger Nano S+ or X, to sign your Starknet transactions. -You have to use a transporter to interact with the Ledger Nano. Depending if you use an USB or a Bluetooth connection, depending of your framework (Node, Web, Mobile), you have to use the appropriate library to create your transporter. +You have to use a transporter to interact with the Ledger Nano. Depending if you use an USB or a Bluetooth connection, depending on your framework (Node, Web, Mobile), you have to use the appropriate library to create your transporter. -The Ledger documentation is listing all the available cases : +The Ledger documentation lists all the available cases : ![](./pictures/LedgerConnectivity.png) The libs available are : @@ -228,7 +228,7 @@ import type Transport from '@ledgerhq/hw-transport'; // type for the transporter In a Web DAPP, take care that some browsers are not compatible (FireFox, ...), and that the Bluetooth is not working in all cases and in all operating systems. :::note -The last version of the Ledger Starknet APP (v1.1.1) only supports blind signing of the hash of your action. Sign only hashes from a code that you trust. +The last version of the Ledger Starknet APP (v2.2.1) supports explained V1 (ETH) & V3 (STRK) transactions & deploy accounts. For a class declaration or a message, you will have to blind sign a hash ; sign only hashes from a code that you trust. Do not forget to Enable `Blind signing` in the APP settings. ::: For example, for a Node script : @@ -236,7 +236,7 @@ For example, for a Node script : ```typescript import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'; const myLedgerTransport: Transport = await TransportNodeHid.create(); -const myLedgerSigner = new LedgerSigner(myLedgerTransport, 0); +const myLedgerSigner = new LedgerSigner221(myLedgerTransport, 0); const pubK = await myLedgerSigner.getPubKey(); const fullPubK = await myLedgerSigner.getFullPubKey(); // ... @@ -250,7 +250,7 @@ The Ledger shall be connected, unlocked, with the Starknet internal APP activate ::: Some complete examples : -A Node script : [here](https://github.com/PhilippeR26/starknet.js-workshop-typescript/blob/main/src/scripts/ledgerNano/5.testLedgerAccount.ts). +A Node script : [here](https://github.com/PhilippeR26/starknet.js-workshop-typescript/blob/main/src/scripts/ledgerNano/6.testLedgerAccount221.ts). A test Web DAPP, to use in devnet-rs network : [here](https://github.com/PhilippeR26/Starknet-Ledger-Wallet). If you want to read the version of the Ledger Starknet APP : @@ -260,3 +260,18 @@ const resp = await myLedgerTransport.send(Number('0x5a'), 0, 0, 0); const appVersion = resp[0] + '.' + resp[1] + '.' + resp[2]; console.log('version=', appVersion); ``` + +:::note +You also have in Starknet.js a signer for the old v1.1.1 Ledger Starknet APP. + +```typescript +const myLedgerSigner = new LedgerSigner111(myLedgerTransport, 0); +``` + +If you want to use the accounts created with the v1.1.1, using the v2.2.1 : + +```typescript +const myLedgerSigner = new LedgerSigner221(myLedgerTransport, 0, undefined, getLedgerPathBuffer111); +``` + +::: diff --git a/www/docs/guides/walletAccount.md b/www/docs/guides/walletAccount.md index 6497f4cd1..ceaf3dff6 100644 --- a/www/docs/guides/walletAccount.md +++ b/www/docs/guides/walletAccount.md @@ -4,46 +4,62 @@ sidebar_position: 9 # WalletAccount -**Use wallets (like Braavos & ArgentX) to sign your transactions in your DAPP.** +**Use wallets to sign transactions in your DAPP.** -The `WalletAccount` class is similar to the regular `Account` class, but is also able to ask a browser wallet to sign and send a transaction. Some other cool functionalities will be detailed hereunder. +The [`WalletAccount`](../API/classes/WalletAccount) class is similar to the regular [`Account`](../API/classes/Account) class, with the added ability to ask a browser wallet to sign and send transactions. Some other cool functionalities will be detailed hereunder. -The private key of a WalletAccount is held in a browser wallet (as ArgentX or Braavos), and any signature is managed by the wallet. You don't have to manage in your DAPP the security of any private key. +The private key of a `WalletAccount` is held in a browser wallet (such as ArgentX, Braavos, etc.), and any signature is managed by the wallet. With this approach DAPPs don't need to manage the security for any private key. :::caution -This class is working only in the scope of a DAPP. You can't use it in a node.js script. +This class functions only within the scope of a DAPP. It can't be used in a Node.js script. ::: ## Architecture ![](./pictures/WalletAccountArchitecture.png) -If you want to read Starknet, the WalletAccount will read directly the blockchain. That's why at the initialization of a WalletAccount, you need to put in the parameters a Provider instance. It will be used for all reading activities. +When retrieving information from Starknet, a `WalletAccount` instance will read directly from the blockchain. That is why at the initialization of a `WalletAccount` a [`Provider`](../API/classes/Provider) instance is a required parameter, it will be used for all reading activities. -If you want to write to Starknet, the WalletAccount will ask the browser Wallet to sign and send the transaction, using the Starknet Wallet API to communicate. -As several Wallets can be installed in your browser, the WalletAccount needs the ID of one of the available wallets. You can ask `get-starknet` to display a list of available wallets and to provide as a response the identifier of the selected wallet, called a `Starknet Windows Object` (named SWO from now). +If you want to write to Starknet the `WalletAccount` will ask the browser wallet to sign and send the transaction using the Starknet Wallet API to communicate. + +As several wallets can be installed in your browser, the `WalletAccount` needs the ID of one of the available wallets. You can ask `get-starknet` to display a list of available wallets and to provide as a response the identifier of the selected wallet, called a `Starknet Windows Object` (referred to as SWO in the rest of this guide). ## Select a Wallet -You can ask the `get-starknet` v4 library to display a list of wallets, then it will ask you to make a choice. It will return the SWO of the wallet the user selected. -Using the `get-starknet-core` v4 library, you can create your own UI and logic to select the wallet. An example of DAPP using a custom UI : [**here**](https://github.com/PhilippeR26/Starknet-WalletAccount/blob/main/src/app/components/client/WalletHandle/SelectWallet.tsx), where you can select only the wallets compatible with the Starknet wallet API. +You can ask the `get-starknet` v4 library to display a list of wallets, then it will ask you to make a choice. It will return the SWO of the wallet the user selected. + +Using the `get-starknet-core` v4 library you can create your own UI and logic to select the wallet. An example of DAPP using a custom UI: [**here**](https://github.com/PhilippeR26/Starknet-WalletAccount/blob/main/src/app/components/client/WalletHandle/SelectWallet.tsx), in the example you can select only the wallets compatible with the Starknet Wallet API. ![](./pictures/SelectWallet.png) -So, you instantiate a new WalletAccount with : +Instantiating a new `WalletAccount`: ```typescript -import { connect } from 'get-starknet'; // v4.0.0 min -import { WalletAccount } from 'starknet'; // v6.10.0 min +import { connect } from '@starknet-io/get-starknet'; // v4.0.3 min +import { WalletAccount, wallet } from 'starknet'; // v6.18.0 min const myFrontendProviderUrl = 'https://free-rpc.nethermind.io/sepolia-juno/v0_7'; -// standard UI to select a wallet : +// standard UI to select a wallet: const selectedWalletSWO = await connect({ modalMode: 'alwaysAsk', modalTheme: 'light' }); -const myWalletAccount = new WalletAccount({ nodeUrl: myFrontendProviderUrl }, selectedWalletSWO); +const myWalletAccount = await WalletAccount.connect( + { nodeUrl: myFrontendProviderUrl }, + selectedWalletSWO +); ``` -## Use as an account +The wallet is connected to this blockchain to write in Starknet: -Once the new WalletAccount is created, you can use all the power of Starknet.js, exactly as a with a normal Account instance. -You can use for example `myWalletAccount.execute(call)` or `myWalletAccount.signMessage(typedMessage)` : +```typescript +const writeChainId = await wallet.requestChainId(myWalletAccount.walletProvider); +``` + +and to this blockchain to read Starknet: + +```typescript +const readChainId = await myWalletAccount.getChainId(); +``` + +## Use as an Account + +Once a new `WalletAccount` is created, you can use all the power of Starknet.js, exactly as a with a normal `Account` instance, for example `myWalletAccount.execute(call)` or `myWalletAccount.signMessage(typedMessage)`: ```typescript const claimCall = airdropContract.populate('claim_airdrop', { @@ -57,34 +73,34 @@ const resp = await myWalletAccount.execute(claimCall); ## Use in a Contract instance -You can connect a WalletAccount with a Contract instance. All reading actions are performed by the provider of the WalletAccount, and all writing actions (that needs a signature) are performed by the browser wallet. +You can connect a `WalletAccount` with a [`Contract`](../API/classes/Contract) instance. All reading actions are performed by the provider of the `WalletAccount`, and all writing actions (that need a signature) are performed by the browser wallet. ```typescript const lendContract = new Contract(contract.abi, contractAddress, myWalletAccount); -const qty = await lendContract.get_available_asset(addr); // use of the WalletAccount provider. +const qty = await lendContract.get_available_asset(addr); // use of the WalletAccount provider const resp = await lendContract.process_lend_asset(addr); // use of the browser wallet ``` -## Use as a provider +## Use as a Provider -Your WalletAccount instance can be used as a provider : +Your `WalletAccount` instance can be used as a provider: ```typescript const bl = await myWalletAccount.getBlockNumber(); // bl = 2374543 ``` -You can use all the methods of the RpcProvider class. Under the hood, the WalletAccount will use the rpc node that you indicated at its instantiation. +You can use all the methods of the `Provider` class. Under the hood, the `WalletAccount` will use the RPC node that you indicated at its instantiation. ## Subscription to events -You can subscribe to 2 events : +You can subscribe to 2 events: -- `accountsChanged` : Triggered each time you change the current account in the wallet. -- `networkChanged` : Triggered each time you change the current network in the wallet. +- `accountsChanged`: Triggered each time you change the current account in the wallet. +- `networkChanged`: Triggered each time you change the current network in the wallet. -At each change of the network, both account and network events are occurring. -At each change of the account, only the account event is occurring. +At each change of the network, both account and network events are emitted. +At each change of the account, only the account event is emitted. ### Subscribe @@ -111,9 +127,9 @@ const handleNetwork: NetworkChangeEventHandler = (chainId?: string, accounts?: s selectedWalletSWO.on('networkChanged', handleNetwork); ``` -### Un-subscribe : +### Unsubscribe -Similar to subscription, using `.off` method. +Similar to subscription, by using the `.off` method. ```typescript selectedWalletSWO.off('accountsChanged', handleAccount); @@ -121,20 +137,21 @@ selectedWalletSWO.off('networkChanged', handleNetwork); ``` :::info -You can subscribe both with the SWO or with a WalletAccount instance. -The above examples are using the SWO, because it's the simpler way to process. +You can subscribe both with the SWO or with a `WalletAccount` instance. +The above examples are using the SWO, because it is the simpler way to process. ::: ## Direct access to the wallet API entry points -The WalletAccount class is able to interact with all the entrypoints of the Starknet wallet API, including some functionalities that do not exists in an Account class. -You have a full description of this API [**here**](https://github.com/PhilippeR26/Starknet-WalletAccount/blob/main/doc/walletAPIspec.md). +The `WalletAccount` class is able to interact with all the entrypoints of the Starknet Wallet API, including some functionalities that do not exists in the `Account` class. + +A full description of this API can be found [**here**](https://github.com/PhilippeR26/Starknet-WalletAccount/blob/main/doc/walletAPIspec.md). Some examples: -### Request a change of wallet network +### Request to change the wallet network -Using your WalletAccount, you can ask the wallet to change its current network: +Using your `WalletAccount`, you can ask the wallet to change its current network: ```typescript useEffect( @@ -154,7 +171,7 @@ useEffect( ### Request to display a token in the wallet -Using your WalletAccount, you can ask the wallet to display a new token: +Using your `WalletAccount`, you can ask the wallet to display a new token: ```typescript useEffect( @@ -177,9 +194,10 @@ useEffect( ![](./pictures/addToken.png) -## Change of network or account +## Changing the network or account + +When you change the network or the account address a `WalletAccount` instance is automatically updated, however, this can lead to unexpected behavior if one is not careful (reads and writes targeting different networks, problems with Cairo versions of the accounts, ...). -When you change the network or the account address, the WalletAccount is automatically updated, but it can lead to tricky behavior (read and write in different networks, problems of Cairo versions of the accounts, ....). :::warning RECOMMENDATION -It's strongly recommended to create a new WalletAccount instance each time the network or the account address is changed. +It is strongly recommended to create a new `WalletAccount` instance each time the network or the account address is changed. ::: diff --git a/www/docusaurus.config.js b/www/docusaurus.config.js index 5e08e6f25..4cf4a71d8 100644 --- a/www/docusaurus.config.js +++ b/www/docusaurus.config.js @@ -133,7 +133,7 @@ const config = { }, { label: 'Discord', - href: 'https://discord.com/channels/793094838509764618/927918707613786162', + href: 'https://discord.com/channels/793094838509764618/1270119831559078061', }, ], },