From a673fbcc17f17235243d6ee876c3339830106e71 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Sun, 2 Jun 2024 21:58:42 +0200 Subject: [PATCH 001/126] Update ERC-7540: Update ERC7540 Merged by EIP-Bot. --- ERCS/erc-7540.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ERCS/erc-7540.md b/ERCS/erc-7540.md index b59cbf6f33..eb995026bf 100644 --- a/ERCS/erc-7540.md +++ b/ERCS/erc-7540.md @@ -189,11 +189,11 @@ Assumes control of `shares` from `owner` and submits a Request for asynchronous The output `requestId` is used to partially discriminate the request along with the `controller`. See [Request Ids](#request-ids) section for more info. -MAY support either a locking or a burning mechanism for `shares` depending on the Vault implementation. +`shares` MAY be temporarily locked in the Vault until the Claimable or Claimed state for accounting purposes, or they MAY be burned immediately upon `requestRedeem`. -If a Vault uses a locking mechanism for `shares`, those `shares` MUST be burned from the Vault balance before or upon claiming the Request. +In either case, the `shares` MUST be removed from the custody of `owner` upon `requestRedeem` and burned by the time the request is Claimed. -MUST support a redeem Request flow where the control of `shares` is taken from `owner` directly where `msg.sender` has ERC-20 approval over the `shares` of `owner`, or the `owner` has approved the `msg.sender` as an operator. +Redeem Request approval of `shares` for a `msg.sender` NOT equal to `owner` may come either from ERC-20 approval over the `shares` of `owner` or if the `owner` has approved the `msg.sender` as an operator. When the Request is Claimable, `claimableRedeemRequest` will be increased for the `controller`. `redeem` or `withdraw` can subsequently be called by `controller` to receive `assets`. A Request MAY transition straight to Claimable state but MUST NOT skip the Claimable state. @@ -325,10 +325,10 @@ Implementations MUST support an additional overloaded `deposit` and `mint` metho - `deposit(uint256 assets, address receiver, address controller)` - `mint(uint256 shares, address receiver, address controller)` -The `controller` field is used to look up the Request for which the `assets` should be claimed. - Calls MUST revert unless `msg.sender` is either equal to `controller` or an operator approved by `controller`. +The `controller` field is used to discriminate the Request for which the `assets` should be claimed in the case where `msg.sender` is NOT `controller`. + When the `Deposit` event is emitted, the first parameter MUST be the `controller`, and the second parameter MUST be the `receiver`. ### Events @@ -556,6 +556,12 @@ In general, asynchronicity concerns make state transitions in the Vault much mor * The view methods for viewing Pending and Claimable request states (e.g. pendingDepositRequest) are estimates useful for display purposes but can be outdated. The inability to know the final exchange rate on any Request requires users to trust the implementation of the asynchronous Vault in the computation of the exchange rate and fulfillment of their Request. * Shares or assets locked for Requests can be stuck in the Pending state. Vaults may elect to allow for the fungibility of pending claims or implement some cancellation functionality to protect users. +### Operators + +An operator has the ability to transfer the `asset` of the vault from the approver to any address, and simultaneously grants control over the `share` of the vault. + +Any user approving an operator must trust that operator with both the `asset` and `share` of the Vault. + ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file From 1d67a408d8055055d205cd431d71c25eed56b3ec Mon Sep 17 00:00:00 2001 From: Vidor <1101164+V1d0r@users.noreply.github.com> Date: Mon, 3 Jun 2024 09:44:40 +0100 Subject: [PATCH 002/126] Update ERC-7578: Potential linearization issue Merged by EIP-Bot. --- ERCS/erc-7578.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7578.md b/ERCS/erc-7578.md index 68c8f83b2f..8c9ed1052e 100644 --- a/ERCS/erc-7578.md +++ b/ERCS/erc-7578.md @@ -153,7 +153,7 @@ An example of an [ERC-721](./eip-721.md) that includes this proposal using the O ```solidity pragma solidity ^0.8.21; -import { ERC721 } from "@openzeppelin/contracts-v5/token/ERC721/ERC721.sol"; +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { IERC7578, Properties, Amount } from "./interfaces/IERC7578.sol"; /** @@ -161,7 +161,7 @@ import { IERC7578, Properties, Amount } from "./interfaces/IERC7578.sol"; * @author DESAT * @notice Implementation of the ERC-7578: Physical Asset Redemption standard **/ -contract ERC7578 is IERC7578, ERC721 { +contract ERC7578 is ERC721, IERC7578 { /** * @notice Thrown when the properties of a token are not initialized */ From f5325120b1feb8cfcb53e12fa2e485987b5b35e5 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:38:04 +0200 Subject: [PATCH 003/126] Update ERC-7540: Update ERC7540 Merged by EIP-Bot. --- ERCS/erc-7540.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7540.md b/ERCS/erc-7540.md index eb995026bf..8f3c0ca8ea 100644 --- a/ERCS/erc-7540.md +++ b/ERCS/erc-7540.md @@ -187,7 +187,7 @@ MUST NOT revert unless due to integer overflow caused by an unreasonably large i Assumes control of `shares` from `owner` and submits a Request for asynchronous `redeem`. This places the Request in Pending state, with a corresponding increase in `pendingRedeemRequest` for the amount `shares`. -The output `requestId` is used to partially discriminate the request along with the `controller`. See [Request Ids](#request-ids) section for more info. +The output `requestId` is used to discriminate the request along with the `controller`. See [Request Ids](#request-ids) section for more info. `shares` MAY be temporarily locked in the Vault until the Claimable or Claimed state for accounting purposes, or they MAY be burned immediately upon `requestRedeem`. @@ -490,7 +490,7 @@ It reduces code and implementation complexity at little to no cost to simply man ### Mandated Support for [ERC-165](./eip-165.md) -Implementing support for [ERC-165](./eip-165.md) is mandated because of the [optionality of flows](#optionality-of-flows). Integrations can use the `supportsInterface` method to check whether a vault is fully asynchronous, partially asynchronous, or fully synchronous, and use a single contract to support all cases. +Implementing support for [ERC-165](./eip-165.md) is mandated because of the [optionality of flows](#optionality-of-flows). Integrations can use the `supportsInterface` method to check whether a vault is fully asynchronous, partially asynchronous, or fully synchronous (for which it is just following the [ERC-4626](./eip-4626)), and use a single contract to support all cases. ### Not Allowing Pending Claims to be Fungible The async pending claims represent a sort of semi-fungible intermediate share class. Vaults can elect to wrap these claims in any token standard they like, for example, ERC-20, [ERC-1155](./eip-1155.md), or ERC-721 depending on the use case. This is intentionally left out of the spec to provide flexibility to implementers. From cfe296ac26b91c4ef433159448c91e17fc5cbf6f Mon Sep 17 00:00:00 2001 From: William Morriss Date: Mon, 3 Jun 2024 15:20:53 -0500 Subject: [PATCH 004/126] Add ERC: Distinguishable base256emoji Addresses Merged by EIP-Bot. --- ERCS/erc-7673.md | 373 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 ERCS/erc-7673.md diff --git a/ERCS/erc-7673.md b/ERCS/erc-7673.md new file mode 100644 index 0000000000..ef1ab23168 --- /dev/null +++ b/ERCS/erc-7673.md @@ -0,0 +1,373 @@ +--- +eip: 7673 +title: Distinguishable base256emoji Addresses +description: Depict Account Addresses As A String of Emoji +author: William Morriss (@wjmelements) +discussions-to: https://ethereum-magicians.org/t/erc-7673-distinguishable-account-addresses/19461 +status: Draft +type: Standards Track +category: ERC +created: 2024-04-01 +--- + +## Abstract + +Introduce base256emoji for use as the primary input and display for account addresses in all user interfaces. + +## Motivation + +Human users often fail to distinguish between long strings of hexadecimal characters, especially when they match at the beginning and at the end. +This makes hexadecimal strings a poor format for human-readable account addresses. +The problem is being exploited by several spoofing strategies that mine similar addresses and spoof [ERC-20](./eip-20.md) Transfer events with the goal of tricking the end user into copying the wrong recipient address. +These address spoofing attacks have mislead tens of thousands of ether, and countless other tokens. +Spoofers flooding the network with fake Transfer events waste network resources and complicate blockchain accounting. +Improving the distinguishability of addresses will reduce the incentives for this behavior. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +User interfaces: +- SHALL depict account addresses as a base256emoji string instead of hexadecimal. +- SHALL accept base256emoji strings as input for user-supplied account address parameters. +- SHOULD recognize and interpret strings of exactly 20 consecutive emoji as addresses when all of them are valid base256emoji. + +### base256emoji encoding table + +| Emoji | Unicode codepoint | Byte Value | +|:-:|:-:|:-:| +| ๐Ÿš€ | U+1F680 | 0 | +| ๐Ÿช | U+1FA90 | 1 | +| โ˜„ | U+2604 | 2 | +| ๐Ÿ›ฐ | U+1F6F0 | 3 | +| ๐ŸŒŒ | U+1F30C | 4 | +| ๐ŸŒ‘ | U+1F311 | 5 | +| ๐ŸŒ’ | U+1F312 | 6 | +| ๐ŸŒ“ | U+1F313 | 7 | +| ๐ŸŒ” | U+1F314 | 8 | +| ๐ŸŒ• | U+1F315 | 9 | +| ๐ŸŒ– | U+1F316 | 10 | +| ๐ŸŒ— | U+1F317 | 11 | +| ๐ŸŒ˜ | U+1F318 | 12 | +| ๐ŸŒ | U+1F30D | 13 | +| ๐ŸŒ | U+1F30F | 14 | +| ๐ŸŒŽ | U+1F30E | 15 | +| ๐Ÿ‰ | U+1F409 | 16 | +| โ˜€ | U+2600 | 17 | +| ๐Ÿ’ป | U+1F4BB | 18 | +| ๐Ÿ–ฅ | U+1F5A5 | 19 | +| ๐Ÿ’พ | U+1F4BE | 20 | +| ๐Ÿ’ฟ | U+1F4BF | 21 | +| ๐Ÿ˜‚ | U+1F602 | 22 | +| โค | U+2764 | 23 | +| ๐Ÿ˜ | U+1F60D | 24 | +| ๐Ÿคฃ | U+1F923 | 25 | +| ๐Ÿ˜Š | U+1F60A | 26 | +| ๐Ÿ™ | U+1F64F | 27 | +| ๐Ÿ’• | U+1F495 | 28 | +| ๐Ÿ˜ญ | U+1F62D | 29 | +| ๐Ÿ˜˜ | U+1F618 | 30 | +| ๐Ÿ‘ | U+1F44D | 31 | +| ๐Ÿ˜… | U+1F605 | 32 | +| ๐Ÿ‘ | U+1F44F | 33 | +| ๐Ÿ˜ | U+1F601 | 34 | +| ๐Ÿ”ฅ | U+1F525 | 35 | +| ๐Ÿฅฐ | U+1F970 | 36 | +| ๐Ÿ’” | U+1F494 | 37 | +| ๐Ÿ’– | U+1F496 | 38 | +| ๐Ÿ’™ | U+1F499 | 39 | +| ๐Ÿ˜ข | U+1F622 | 40 | +| ๐Ÿค” | U+1F914 | 41 | +| ๐Ÿ˜† | U+1F606 | 42 | +| ๐Ÿ™„ | U+1F644 | 43 | +| ๐Ÿ’ช | U+1F4AA | 44 | +| ๐Ÿ˜‰ | U+1F609 | 45 | +| โ˜บ | U+263A | 46 | +| ๐Ÿ‘Œ | U+1F44C | 47 | +| ๐Ÿค— | U+1F917 | 48 | +| ๐Ÿ’œ | U+1F49C | 49 | +| ๐Ÿ˜” | U+1F614 | 50 | +| ๐Ÿ˜Ž | U+1F60E | 51 | +| ๐Ÿ˜‡ | U+1F607 | 52 | +| ๐ŸŒน | U+1F339 | 53 | +| ๐Ÿคฆ | U+1F926 | 54 | +| ๐ŸŽ‰ | U+1F389 | 55 | +| ๐Ÿ’ž | U+1F49E | 56 | +| โœŒ | U+270C | 57 | +| โœจ | U+2728 | 58 | +| ๐Ÿคท | U+1F937 | 59 | +| ๐Ÿ˜ฑ | U+1F631 | 60 | +| ๐Ÿ˜Œ | U+1F60C | 61 | +| ๐ŸŒธ | U+1F338 | 62 | +| ๐Ÿ™Œ | U+1F64C | 63 | +| ๐Ÿ˜‹ | U+1F60B | 64 | +| ๐Ÿ’— | U+1F497 | 65 | +| ๐Ÿ’š | U+1F49A | 66 | +| ๐Ÿ˜ | U+1F60F | 67 | +| ๐Ÿ’› | U+1F49B | 68 | +| ๐Ÿ™‚ | U+1F642 | 69 | +| ๐Ÿ’“ | U+1F493 | 70 | +| ๐Ÿคฉ | U+1F929 | 71 | +| ๐Ÿ˜„ | U+1F604 | 72 | +| ๐Ÿ˜€ | U+1F600 | 73 | +| ๐Ÿ–ค | U+1F5A4 | 74 | +| ๐Ÿ˜ƒ | U+1F603 | 75 | +| ๐Ÿ’ฏ | U+1F4AF | 76 | +| ๐Ÿ™ˆ | U+1F648 | 77 | +| ๐Ÿ‘‡ | U+1F447 | 78 | +| ๐ŸŽถ | U+1F3B6 | 79 | +| ๐Ÿ˜’ | U+1F612 | 80 | +| ๐Ÿคญ | U+1F92D | 81 | +| โฃ | U+2763 | 82 | +| ๐Ÿ˜œ | U+1F61C | 83 | +| ๐Ÿ’‹ | U+1F48B | 84 | +| ๐Ÿ‘€ | U+1F440 | 85 | +| ๐Ÿ˜ช | U+1F62A | 86 | +| ๐Ÿ˜‘ | U+1F611 | 87 | +| ๐Ÿ’ฅ | U+1F4A5 | 88 | +| ๐Ÿ™‹ | U+1F64B | 89 | +| ๐Ÿ˜ž | U+1F61E | 90 | +| ๐Ÿ˜ฉ | U+1F629 | 91 | +| ๐Ÿ˜ก | U+1F621 | 92 | +| ๐Ÿคช | U+1F92A | 93 | +| ๐Ÿ‘Š | U+1F44A | 94 | +| ๐Ÿฅณ | U+1F973 | 95 | +| ๐Ÿ˜ฅ | U+1F625 | 96 | +| ๐Ÿคค | U+1F924 | 97 | +| ๐Ÿ‘‰ | U+1F449 | 98 | +| ๐Ÿ’ƒ | U+1F483 | 99 | +| ๐Ÿ˜ณ | U+1F633 | 100 | +| โœ‹ | U+270B | 101 | +| ๐Ÿ˜š | U+1F61A | 102 | +| ๐Ÿ˜ | U+1F61D | 103 | +| ๐Ÿ˜ด | U+1F634 | 104 | +| ๐ŸŒŸ | U+1F31F | 105 | +| ๐Ÿ˜ฌ | U+1F62C | 106 | +| ๐Ÿ™ƒ | U+1F643 | 107 | +| ๐Ÿ€ | U+1F340 | 108 | +| ๐ŸŒท | U+1F337 | 109 | +| ๐Ÿ˜ป | U+1F63B | 110 | +| ๐Ÿ˜“ | U+1F613 | 111 | +| โญ | U+2B50 | 112 | +| โœ… | U+2705 | 113 | +| ๐Ÿฅบ | U+1F97A | 114 | +| ๐ŸŒˆ | U+1F308 | 115 | +| ๐Ÿ˜ˆ | U+1F608 | 116 | +| ๐Ÿค˜ | U+1F918 | 117 | +| ๐Ÿ’ฆ | U+1F4A6 | 118 | +| โœ” | U+2714 | 119 | +| ๐Ÿ˜ฃ | U+1F623 | 120 | +| ๐Ÿƒ | U+1F3C3 | 121 | +| ๐Ÿ’ | U+1F490 | 122 | +| โ˜น | U+2639 | 123 | +| ๐ŸŽŠ | U+1F38A | 124 | +| ๐Ÿ’˜ | U+1F498 | 125 | +| ๐Ÿ˜  | U+1F620 | 126 | +| โ˜ | U+261D | 127 | +| ๐Ÿ˜• | U+1F615 | 128 | +| ๐ŸŒบ | U+1F33A | 129 | +| ๐ŸŽ‚ | U+1F382 | 130 | +| ๐ŸŒป | U+1F33B | 131 | +| ๐Ÿ˜ | U+1F610 | 132 | +| ๐Ÿ–• | U+1F595 | 133 | +| ๐Ÿ’ | U+1F49D | 134 | +| ๐Ÿ™Š | U+1F64A | 135 | +| ๐Ÿ˜น | U+1F639 | 136 | +| ๐Ÿ—ฃ | U+1F5E3 | 137 | +| ๐Ÿ’ซ | U+1F4AB | 138 | +| ๐Ÿ’€ | U+1F480 | 139 | +| ๐Ÿ‘‘ | U+1F451 | 140 | +| ๐ŸŽต | U+1F3B5 | 141 | +| ๐Ÿคž | U+1F91E | 142 | +| ๐Ÿ˜› | U+1F61B | 143 | +| ๐Ÿ”ด | U+1F534 | 144 | +| ๐Ÿ˜ค | U+1F624 | 145 | +| ๐ŸŒผ | U+1F33C | 146 | +| ๐Ÿ˜ซ | U+1F62B | 147 | +| โšฝ | U+26BD | 148 | +| ๐Ÿค™ | U+1F919 | 149 | +| โ˜• | U+2615 | 150 | +| ๐Ÿ† | U+1F3C6 | 151 | +| ๐Ÿคซ | U+1F92B | 152 | +| ๐Ÿ‘ˆ | U+1F448 | 153 | +| ๐Ÿ˜ฎ | U+1F62E | 154 | +| ๐Ÿ™† | U+1F646 | 155 | +| ๐Ÿป | U+1F37B | 156 | +| ๐Ÿƒ | U+1F343 | 157 | +| ๐Ÿถ | U+1F436 | 158 | +| ๐Ÿ’ | U+1F481 | 159 | +| ๐Ÿ˜ฒ | U+1F632 | 160 | +| ๐ŸŒฟ | U+1F33F | 161 | +| ๐Ÿงก | U+1F9E1 | 162 | +| ๐ŸŽ | U+1F381 | 163 | +| โšก | U+26A1 | 164 | +| ๐ŸŒž | U+1F31E | 165 | +| ๐ŸŽˆ | U+1F388 | 166 | +| โŒ | U+274C | 167 | +| โœŠ | U+270A | 168 | +| ๐Ÿ‘‹ | U+1F44B | 169 | +| ๐Ÿ˜ฐ | U+1F630 | 170 | +| ๐Ÿคจ | U+1F928 | 171 | +| ๐Ÿ˜ถ | U+1F636 | 172 | +| ๐Ÿค | U+1F91D | 173 | +| ๐Ÿšถ | U+1F6B6 | 174 | +| ๐Ÿ’ฐ | U+1F4B0 | 175 | +| ๐Ÿ“ | U+1F353 | 176 | +| ๐Ÿ’ข | U+1F4A2 | 177 | +| ๐ŸคŸ | U+1F91F | 178 | +| ๐Ÿ™ | U+1F641 | 179 | +| ๐Ÿšจ | U+1F6A8 | 180 | +| ๐Ÿ’จ | U+1F4A8 | 181 | +| ๐Ÿคฌ | U+1F92C | 182 | +| โœˆ | U+2708 | 183 | +| ๐ŸŽ€ | U+1F380 | 184 | +| ๐Ÿบ | U+1F37A | 185 | +| ๐Ÿค“ | U+1F913 | 186 | +| ๐Ÿ˜™ | U+1F619 | 187 | +| ๐Ÿ’Ÿ | U+1F49F | 188 | +| ๐ŸŒฑ | U+1F331 | 189 | +| ๐Ÿ˜– | U+1F616 | 190 | +| ๐Ÿ‘ถ | U+1F476 | 191 | +| ๐Ÿฅด | U+1F974 | 192 | +| โ–ถ | U+25B6 | 193 | +| โžก | U+27A1 | 194 | +| โ“ | U+2753 | 195 | +| ๐Ÿ’Ž | U+1F48E | 196 | +| ๐Ÿ’ธ | U+1F4B8 | 197 | +| โฌ‡ | U+2B07 | 198 | +| ๐Ÿ˜จ | U+1F628 | 199 | +| ๐ŸŒš | U+1F31A | 200 | +| ๐Ÿฆ‹ | U+1F98B | 201 | +| ๐Ÿ˜ท | U+1F637 | 202 | +| ๐Ÿ•บ | U+1F57A | 203 | +| โš  | U+26A0 | 204 | +| ๐Ÿ™… | U+1F645 | 205 | +| ๐Ÿ˜Ÿ | U+1F61F | 206 | +| ๐Ÿ˜ต | U+1F635 | 207 | +| ๐Ÿ‘Ž | U+1F44E | 208 | +| ๐Ÿคฒ | U+1F932 | 209 | +| ๐Ÿค  | U+1F920 | 210 | +| ๐Ÿคง | U+1F927 | 211 | +| ๐Ÿ“Œ | U+1F4CC | 212 | +| ๐Ÿ”ต | U+1F535 | 213 | +| ๐Ÿ’… | U+1F485 | 214 | +| ๐Ÿง | U+1F9D0 | 215 | +| ๐Ÿพ | U+1F43E | 216 | +| ๐Ÿ’ | U+1F352 | 217 | +| ๐Ÿ˜— | U+1F617 | 218 | +| ๐Ÿค‘ | U+1F911 | 219 | +| ๐ŸŒŠ | U+1F30A | 220 | +| ๐Ÿคฏ | U+1F92F | 221 | +| ๐Ÿท | U+1F437 | 222 | +| โ˜Ž | U+260E | 223 | +| ๐Ÿ’ง | U+1F4A7 | 224 | +| ๐Ÿ˜ฏ | U+1F62F | 225 | +| ๐Ÿ’† | U+1F486 | 226 | +| ๐Ÿ‘† | U+1F446 | 227 | +| ๐ŸŽค | U+1F3A4 | 228 | +| ๐Ÿ™‡ | U+1F647 | 229 | +| ๐Ÿ‘ | U+1F351 | 230 | +| โ„ | U+2744 | 231 | +| ๐ŸŒด | U+1F334 | 232 | +| ๐Ÿ’ฃ | U+1F4A3 | 233 | +| ๐Ÿธ | U+1F438 | 234 | +| ๐Ÿ’Œ | U+1F48C | 235 | +| ๐Ÿ“ | U+1F4CD | 236 | +| ๐Ÿฅ€ | U+1F940 | 237 | +| ๐Ÿคข | U+1F922 | 238 | +| ๐Ÿ‘… | U+1F445 | 239 | +| ๐Ÿ’ก | U+1F4A1 | 240 | +| ๐Ÿ’ฉ | U+1F4A9 | 241 | +| ๐Ÿ‘ | U+1F450 | 242 | +| ๐Ÿ“ธ | U+1F4F8 | 243 | +| ๐Ÿ‘ป | U+1F47B | 244 | +| ๐Ÿค | U+1F910 | 245 | +| ๐Ÿคฎ | U+1F92E | 246 | +| ๐ŸŽผ | U+1F3BC | 247 | +| ๐Ÿฅต | U+1F975 | 248 | +| ๐Ÿšฉ | U+1F6A9 | 249 | +| ๐ŸŽ | U+1F34E | 250 | +| ๐ŸŠ | U+1F34A | 251 | +| ๐Ÿ‘ผ | U+1F47C | 252 | +| ๐Ÿ’ | U+1F48D | 253 | +| ๐Ÿ“ฃ | U+1F4E3 | 254 | +| ๐Ÿฅ‚ | U+1F942 | 255 | + +## Rationale + +Previous attempts to reduce spoofing and other copy errors such as [ERC-55](./eip-55.md) have not reduced the number of characters in an address. +Any base-256 standard would achieve this goal but emoji were chosen to maximize human-distinguishability. +Multiple base-256 emoji encodings have been proposed. +The base256emoji encoding was chosen due to its acceptance into the multibase repository. + +This standard does not also recommend base256emoji for use in depicting other bytestrings such as transaction hashes and calldata. +Transaction hashes are not yet being spoofed. +Calldata is best decoded via the appropriate ABI. +By only using base256emoji for addresses, addresses can be easily noticed among other information. + +## Backwards Compatibility + +Using the encoding table, the base256emoji encoding can be transcoded into hexadecimal and vice-versa. + +## Test Cases +| base256emoji | ERC-55 | +|:-:|:-:| +|๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿ˜€๐Ÿ’“๐Ÿฅด๐Ÿ’ฃ๐Ÿ‘ป๐Ÿ™Œ๐Ÿ™ˆ๐Ÿคข๐Ÿ˜ฅโ˜น๐ŸŒ๐Ÿ’ฉ๐ŸŽ๐Ÿ’•|`0x0000000000004946c0e9F43F4Dee607b0eF1fA1c`| +|๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿš€๐Ÿ’ธ๐ŸŽŠ๐Ÿ’ก๐ŸŒฟ๐Ÿšฉ๐Ÿ”ฅ๐Ÿ“Œ๐Ÿ™‚๐Ÿ’™โ„๐Ÿ›ฐ๐Ÿ’ฉ๐Ÿคโญ|`0x000000000000c57CF0A1f923d44527e703F1ad70`| +|โ˜€โ˜€โ˜€โ˜€โ˜€โค๐ŸŒŠ๐ŸŒ–โŒ๐Ÿ’€โœ”๐ŸŒŽ๐ŸŽˆโŒ๐Ÿ’ž๐Ÿ›ฐ๐Ÿ’—๐Ÿ˜…โ“โ˜„|`0x111111111117dC0aa78b770fA6A738034120C302`| +|๐Ÿ‘๐Ÿคซ๐Ÿ˜‹โœŠ๐Ÿคช๐Ÿ˜ž๐Ÿค๐Ÿ‘ถ๐Ÿ˜ญโค๐Ÿ‘‰๐Ÿšฉ๐Ÿ’”๐ŸŒฑ๐Ÿค๐ŸŒŠ๐Ÿ’š๐Ÿช๐Ÿšฉ๐Ÿ˜|`0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984`| +|๐Ÿ˜†๐ŸŒŽโœ…โœจ๐Ÿ‘‹๐Ÿ˜œ๐Ÿ’›โ˜บ๐Ÿ˜ถ๐Ÿ‘‹๐Ÿธ๐Ÿคฉ๐ŸŒ”๐Ÿ™Œโœ‹๐Ÿคคโญ๐Ÿ‘โ˜นโšก|`0x2a0f713aA953442EacA9EA47083f656170e67BA4`| +|๐Ÿ”ฅ๐Ÿคฌ๐ŸŒ”๐Ÿ˜๐Ÿ˜ž๐Ÿ™„๐Ÿ‘Œ๐Ÿ’ข๐Ÿ—ฃ๐ŸŒโœจ๐Ÿ˜™๐Ÿพ๐Ÿ˜ก๐Ÿ˜‘๐Ÿค˜๐Ÿ’ธ๐Ÿ˜‚๐Ÿ˜ค๐Ÿ”ต|`0x23B608675a2B2fB1890d3ABBd85c5775c51691d5`| +|๐Ÿ—ฃ๐Ÿ˜…๐Ÿ˜žโœจ๐Ÿคท๐Ÿ˜†๐ŸŒŸ๐Ÿท๐ŸŒท๐Ÿ‘ถโ˜๐Ÿช๐Ÿฅ€๐Ÿ–ฅ๐ŸคŸ๐Ÿ‰๐Ÿ’€๐Ÿ’ช๐Ÿ˜โ„|`0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7`| +|๐Ÿฅด๐Ÿ˜†๐Ÿ˜ฐโœŒ๐ŸคŸ๐Ÿ”ฅ๐Ÿ“ฃ๐ŸŽต๐ŸŒ–๐ŸŒ๐Ÿ˜ก๐ŸŽถ๐Ÿ’™๐Ÿธ๐Ÿ’๐ŸŒ”๐Ÿ˜ฑ๐Ÿค˜๐Ÿ€โžก|`0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2`| +|โ–ถ๐ŸŒป๐Ÿ˜ฅ๐Ÿ‘๐Ÿ’˜๐Ÿ˜›๐Ÿ’๐Ÿ’จโ„๐Ÿ’ธ๐Ÿ˜‚๐Ÿ˜ช๐Ÿ˜๐Ÿคค๐Ÿธ๐Ÿ’ป๐Ÿ˜Ÿโ˜๐Ÿƒ๐Ÿฅบ|`0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72`| + + +## Reference Implementation + +```python3 +to_emoji = [ + '๐Ÿš€', '๐Ÿช', 'โ˜„', '๐Ÿ›ฐ', '๐ŸŒŒ', '๐ŸŒ‘', '๐ŸŒ’', '๐ŸŒ“', '๐ŸŒ”', '๐ŸŒ•', '๐ŸŒ–', '๐ŸŒ—', '๐ŸŒ˜', '๐ŸŒ', '๐ŸŒ', '๐ŸŒŽ', + '๐Ÿ‰', 'โ˜€', '๐Ÿ’ป', '๐Ÿ–ฅ', '๐Ÿ’พ', '๐Ÿ’ฟ', '๐Ÿ˜‚', 'โค', '๐Ÿ˜', '๐Ÿคฃ', '๐Ÿ˜Š', '๐Ÿ™', '๐Ÿ’•', '๐Ÿ˜ญ', '๐Ÿ˜˜', '๐Ÿ‘', + '๐Ÿ˜…', '๐Ÿ‘', '๐Ÿ˜', '๐Ÿ”ฅ', '๐Ÿฅฐ', '๐Ÿ’”', '๐Ÿ’–', '๐Ÿ’™', '๐Ÿ˜ข', '๐Ÿค”', '๐Ÿ˜†', '๐Ÿ™„', '๐Ÿ’ช', '๐Ÿ˜‰', 'โ˜บ', '๐Ÿ‘Œ', + '๐Ÿค—', '๐Ÿ’œ', '๐Ÿ˜”', '๐Ÿ˜Ž', '๐Ÿ˜‡', '๐ŸŒน', '๐Ÿคฆ', '๐ŸŽ‰', '๐Ÿ’ž', 'โœŒ', 'โœจ', '๐Ÿคท', '๐Ÿ˜ฑ', '๐Ÿ˜Œ', '๐ŸŒธ', '๐Ÿ™Œ', + '๐Ÿ˜‹', '๐Ÿ’—', '๐Ÿ’š', '๐Ÿ˜', '๐Ÿ’›', '๐Ÿ™‚', '๐Ÿ’“', '๐Ÿคฉ', '๐Ÿ˜„', '๐Ÿ˜€', '๐Ÿ–ค', '๐Ÿ˜ƒ', '๐Ÿ’ฏ', '๐Ÿ™ˆ', '๐Ÿ‘‡', '๐ŸŽถ', + '๐Ÿ˜’', '๐Ÿคญ', 'โฃ', '๐Ÿ˜œ', '๐Ÿ’‹', '๐Ÿ‘€', '๐Ÿ˜ช', '๐Ÿ˜‘', '๐Ÿ’ฅ', '๐Ÿ™‹', '๐Ÿ˜ž', '๐Ÿ˜ฉ', '๐Ÿ˜ก', '๐Ÿคช', '๐Ÿ‘Š', '๐Ÿฅณ', + '๐Ÿ˜ฅ', '๐Ÿคค', '๐Ÿ‘‰', '๐Ÿ’ƒ', '๐Ÿ˜ณ', 'โœ‹', '๐Ÿ˜š', '๐Ÿ˜', '๐Ÿ˜ด', '๐ŸŒŸ', '๐Ÿ˜ฌ', '๐Ÿ™ƒ', '๐Ÿ€', '๐ŸŒท', '๐Ÿ˜ป', '๐Ÿ˜“', + 'โญ', 'โœ…', '๐Ÿฅบ', '๐ŸŒˆ', '๐Ÿ˜ˆ', '๐Ÿค˜', '๐Ÿ’ฆ', 'โœ”', '๐Ÿ˜ฃ', '๐Ÿƒ', '๐Ÿ’', 'โ˜น', '๐ŸŽŠ', '๐Ÿ’˜', '๐Ÿ˜ ', 'โ˜', + '๐Ÿ˜•', '๐ŸŒบ', '๐ŸŽ‚', '๐ŸŒป', '๐Ÿ˜', '๐Ÿ–•', '๐Ÿ’', '๐Ÿ™Š', '๐Ÿ˜น', '๐Ÿ—ฃ', '๐Ÿ’ซ', '๐Ÿ’€', '๐Ÿ‘‘', '๐ŸŽต', '๐Ÿคž', '๐Ÿ˜›', + '๐Ÿ”ด', '๐Ÿ˜ค', '๐ŸŒผ', '๐Ÿ˜ซ', 'โšฝ', '๐Ÿค™', 'โ˜•', '๐Ÿ†', '๐Ÿคซ', '๐Ÿ‘ˆ', '๐Ÿ˜ฎ', '๐Ÿ™†', '๐Ÿป', '๐Ÿƒ', '๐Ÿถ', '๐Ÿ’', + '๐Ÿ˜ฒ', '๐ŸŒฟ', '๐Ÿงก', '๐ŸŽ', 'โšก', '๐ŸŒž', '๐ŸŽˆ', 'โŒ', 'โœŠ', '๐Ÿ‘‹', '๐Ÿ˜ฐ', '๐Ÿคจ', '๐Ÿ˜ถ', '๐Ÿค', '๐Ÿšถ', '๐Ÿ’ฐ', + '๐Ÿ“', '๐Ÿ’ข', '๐ŸคŸ', '๐Ÿ™', '๐Ÿšจ', '๐Ÿ’จ', '๐Ÿคฌ', 'โœˆ', '๐ŸŽ€', '๐Ÿบ', '๐Ÿค“', '๐Ÿ˜™', '๐Ÿ’Ÿ', '๐ŸŒฑ', '๐Ÿ˜–', '๐Ÿ‘ถ', + '๐Ÿฅด', 'โ–ถ', 'โžก', 'โ“', '๐Ÿ’Ž', '๐Ÿ’ธ', 'โฌ‡', '๐Ÿ˜จ', '๐ŸŒš', '๐Ÿฆ‹', '๐Ÿ˜ท', '๐Ÿ•บ', 'โš ', '๐Ÿ™…', '๐Ÿ˜Ÿ', '๐Ÿ˜ต', + '๐Ÿ‘Ž', '๐Ÿคฒ', '๐Ÿค ', '๐Ÿคง', '๐Ÿ“Œ', '๐Ÿ”ต', '๐Ÿ’…', '๐Ÿง', '๐Ÿพ', '๐Ÿ’', '๐Ÿ˜—', '๐Ÿค‘', '๐ŸŒŠ', '๐Ÿคฏ', '๐Ÿท', 'โ˜Ž', + '๐Ÿ’ง', '๐Ÿ˜ฏ', '๐Ÿ’†', '๐Ÿ‘†', '๐ŸŽค', '๐Ÿ™‡', '๐Ÿ‘', 'โ„', '๐ŸŒด', '๐Ÿ’ฃ', '๐Ÿธ', '๐Ÿ’Œ', '๐Ÿ“', '๐Ÿฅ€', '๐Ÿคข', '๐Ÿ‘…', + '๐Ÿ’ก', '๐Ÿ’ฉ', '๐Ÿ‘', '๐Ÿ“ธ', '๐Ÿ‘ป', '๐Ÿค', '๐Ÿคฎ', '๐ŸŽผ', '๐Ÿฅต', '๐Ÿšฉ', '๐ŸŽ', '๐ŸŠ', '๐Ÿ‘ผ', '๐Ÿ’', '๐Ÿ“ฃ', '๐Ÿฅ‚' +] +from_emoji = {emoji: "{0:02x}".format(i) for i, emoji in enumerate(to_emoji)} + +def encode_address(hexadecimal_address): + if len(hexadecimal_address) != 42 or not hexadecimal_address.startswith('0x'): + return None + return ''.join([to_emoji[int(hexadecimal_address[i:i+2], 16)] for i in range(2, 42, 2)]) + + +def decode_address(emoji_address): + # In python, these unicode characters all have a len() of 1 + if len(emoji_address) != 20: + return None + try: + return '0x' + ''.join(from_emoji[emoji] for emoji in emoji_address) + except IndexError: + return None +``` + +## Security Considerations + +With the base256emoji encoding, addresses use half as many characters. +The characters used are more distinguishable. +This squares the difficulty of generating similar addresses, making address spoofing impractical. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 9d5cc78d111844af3c545c020b27697020f74217 Mon Sep 17 00:00:00 2001 From: OniReimu Date: Wed, 5 Jun 2024 00:14:53 +1000 Subject: [PATCH 005/126] Add ERC: Limited Transfer Count NFT Merged by EIP-Bot. --- ERCS/erc-7634.md | 223 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 ERCS/erc-7634.md diff --git a/ERCS/erc-7634.md b/ERCS/erc-7634.md new file mode 100644 index 0000000000..b0ab5046f8 --- /dev/null +++ b/ERCS/erc-7634.md @@ -0,0 +1,223 @@ +--- +eip: 7634 +title: Limited Transfer Count NFT +description: An ERC-721 extension to limit transferability based on counts among NFTs +author: Qin Wang (@qinwang-git), Saber Yu (@OniReimu), Shiping Chen +discussions-to: https://ethereum-magicians.org/t/erc-7634-limited-transferable-nft/18861 +status: Draft +type: Standards Track +category: ERC +created: 2024-02-22 +requires: 165, 721 +--- + +## Abstract + +This standard extends [ERC-721](./eip-721.md) to introduce a mechanism that allows minters to customize the transferability of NFTs through a parameter called `TransferCount`. `TransferCount` sets a limit on how many times an NFT can be transferred. The standard specifies an interface that includes functions for setting and retrieving transfer limits, tracking transfer counts, and defining pre- and post-transfer states. The standard enables finer control over NFT ownership and transfer rights, ensuring that NFTs can be programmed to have specific, enforceable transfer restrictions. + + +## Motivation + +Once NFTs are sold, they detach from their minters (creators) and can be perpetually transferred thereafter. Yet, many circumstances demand precise control over NFT issuance. We outline their advantages across three dimensions. + +Firstly, by imposing limitations on the frequency of NFT sales or trades, the worth of rare NFTs can be safeguarded. For example, in auctions, limiting the round of bids for a coveted item can uphold its premium price (especially in the Dutch Auction). Similarly, in the intellectual property sector, patents could be bounded by a finite number of transfers prior to becoming freely accessible (entering CC0). In the gaming sphere, items like weapons, apparel, and vehicles might possess a finite lifespan, with each usage or exchange contributing to wear and tear, culminating in automatic decommissioning (burn) upon reaching a predetermined threshold. + +Secondly, enforcing restrictions on trading frequency can enhance network security and stability by mitigating the risks associated with malicious NFT arbitrage, including high-frequency trading (HFT). While this presents a common vulnerability, the lack of easily deployable and effective methods to address it has been notable, making our approach particularly valuable. + +Additionally, limiting the round of transfers can mitigate the economic risks associated with (re)staking NFTs, thereby curbing potential bubbles. With the rapid evolution of restaking mechanisms, it's foreseeable that users may soon engage in multiple rounds of NFT staking (e.g., NFT โ†’ stNFT โ†’ st^2NFT), akin to staking liquidity tokens with third-party platforms like EigenLayer (Ethereum), Babylon (Bitcoin), and Picasso (Solana). Notably, the current setup of EigenLayer employs an NFT as the restaking position (a type of proof-of-restake) for participants. Should this NFT be restaked repeatedly into the market, it could amplify leverage and exacerbate bubble dynamics. By imposing limits on the number of stake iterations, we can proactively prevent the emergence of Ponzi-like dynamics within staking ecosystems. + +### Key Takeaways + +This standard provides several advantages: + +*Controlled Value Preservation*: By allowing minters to set customized transfer limits for NFTs, this standard facilitates the preservation of value for digital assets Just as physical collectibles often gain or maintain value due to scarcity, limiting the number of transfers for an NFT can help ensure its continued value over time. + +*Ensuring Intended Usage*: Setting transfer limits can ensure that NFTs are used in ways that align with their intended purpose. For example, if an NFT represents a limited-edition digital artwork, limiting transfers can prevent it from being excessively traded and potentially devalued. + +*Expanding Use Cases*: These enhancements broaden the potential applications of NFTs by offering more control and flexibility to creators and owners. For instance, NFTs could be used to represent memberships or licenses with limited transferability, opening up new possibilities for digital ownership models. + +*Easy Integration*: To ensure broad adoption and ease of integration, this standard extends the existing [ERC-721](./eip-721.md) interface. By defining a separate interface (`IERC7634`) that includes the new functions, the standard allows existing [ERC-721](./eip-721.md) contracts to adopt the new features with minimal changes. This approach promotes backward compatibility and encourages the seamless incorporation of transfer limits into current NFT projects. + +## Specification + +The key words โ€œMUSTโ€, โ€œMUST NOTโ€, โ€œREQUIREDโ€, โ€œSHALLโ€, โ€œSHALL NOTโ€, โ€œSHOULDโ€, โ€œSHOULD NOTโ€, โ€œRECOMMENDEDโ€, โ€œMAYโ€, and โ€œOPTIONALโ€ in this document are to be interpreted as described in RFC 2119. + +- `setTransferLimit`: a function establishes the transfer limit for a tokenId. +- `transferLimitOf`: a function retrieves the transfer limit for a tokenId. +- `transferCountOf`: a function returns the current transfer count for a tokenId. + +Implementers of this standard **MUST** have all of the following functions: + +```solidity + +pragma solidity ^0.8.4; + +/// @title IERC7634 Interface for Limited Transferable NFT +/// @dev Interface for ERC7634 Limited Transferable NFT extension for ERC721 +/// @author Saber Yu + +interface IERC7634 { + + /** + * @dev Emitted when transfer count is set or updated + */ + event TransferCount(uint256 indexed tokenId, address owner, uint256 counts); + + /** + * @dev Returns the current transfer count for a tokenId + */ + function transferCountOf(uint256 tokenId) external view returns (uint256); + + /** + * @dev Sets the transfer limit for a tokenId. Can only be called by the token owner or an approved address. + * @param tokenId The ID of the token for which to set the limit + * @param limit The maximum number of transfers allowed for the token + */ + function setTransferLimit(uint256 tokenId, uint256 limit) external; + + /** + * @dev Returns the transfer limit for a tokenId + */ + function transferLimitOf(uint256 tokenId) external view returns (uint256); +} + +``` + +## Rationale + +### Does tracking the internal transfer count matter? + +Yes and no. It is optional and quite depends on the actual requirements. The reference implementation given below is a recommended one if you opt for tracking. The `_incrementTransferCount` function and related retrieval functions (`transferLimitOf` and `transferCountOf`) are designed to keep track of the number of transfers an NFT has undergone. This internal tracking mechanism is crucial for enforcing the minter's transfer limits, ensuring that no further transfers can occur once the limit is reached. + +### If opting for tracking, is that all we may want to track? + +It is recommended to also track the before and after. The optional `_beforeTokenTransfer` and `_afterTokenTransfer` functions are overridden to define the state of the NFT before and after a transfer. These functions ensure that any necessary checks or updates are performed in line with the transfer limits and counts. By integrating these checks into the transfer process, the standard ensures that transfer limits are consistently enforced. + + +## Backwards Compatibility + +This standard can be fully [ERC-721](./eip-721.md) compatible by adding an extension function set. + +### Extensions + +This standard can be enhanced with additional advanced functionalities alongside existing NFT protocols. For example: + +- Incorporating a burn function (e.g., [ERC-5679](./eip-5679.md)) would enable NFTs to automatically expire after reaching their transfer limits, akin to the ephemeral nature of Snapchat messages that disappear after multiple views. + +- Incorporating a non-transferring function, as defined in the SBT standards, would enable NFTs to settle and bond with a single owner after a predetermined number of transactions. This functionality mirrors the scenario where a bidder ultimately secures a treasury after participating in multiple bidding rounds. + + +## Reference Implementation + +A recommended implementation is demonstrated as follows: + +- `_incrementTransferCount`: an internal function facilitates incrementing the transfer count. +- `_beforeTokenTransfer`: an overrided function defines the state before transfer. +- `_afterTokenTransfe`: an overrided function outlines the state after transfer. + +```solidity + +pragma solidity ^0.8.4; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "./IERC7634.sol"; + +/// @title Limited Transferable NFT Extension for ERC721 +/// @dev Implementation of the Limited Transferable NFT extension for ERC721 +/// @author Saber Yu + +contract ERC7634 is ERC721, IERC7634 { + + // Mapping from tokenId to the transfer count + mapping(uint256 => uint256) private _transferCounts; + + // Mapping from tokenId to its maximum transfer limit + mapping(uint256 => uint256) private _transferLimits; + + /** + * @dev See {IERC7634-transferCountOf}. + */ + function transferCountOf(uint256 tokenId) public view override returns (uint256) { + require(_exists(tokenId), "ERC7634: Nonexistent token"); + return _transferCounts[tokenId]; + } + + /** + * @dev See {IERC7634-setTransferLimit}. + */ + function setTransferLimit(uint256 tokenId, uint256 limit) public override { + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC7634: caller is not owner nor approved"); + _transferLimits[tokenId] = limit; + } + + /** + * @dev See {IERC7634-transferLimitOf}. + */ + function transferLimitOf(uint256 tokenId) public view override returns (uint256) { + require(_exists(tokenId), "ERC7634: Nonexistent token"); + return _transferLimits[tokenId]; + } + + /** + * @dev Internal function to increment transfer count. + */ + function _incrementTransferCount(uint256 tokenId) internal { + _transferCounts[tokenId] += 1; + emit TransferCount(tokenId, ownerOf(tokenId), _transferCounts[tokenId]); + } + + /** + * @dev Override {_beforeTokenTransfer} to enforce transfer limit. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal override { + require(_transferCounts[tokenId] < _transferLimits[tokenId], "ERC7634: Transfer limit reached"); + super._beforeTokenTransfer(from, to, tokenId); + } + + /** + * @dev Override {_afterTokenTransfer} to handle post-transfer logic. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 tokenId, + uint256 quantity + ) internal virtual override { + _incrementTransferCount(tokenId); + + if (_transferCounts[tokenId] == _transferLimits[tokenId]) { + // Optional post-transfer operations once the limit is reached + // Uncomment the following based on the desired behavior such as the `burn` opearation + // --------------------------------------- + // _burn(tokenId); // Burn the token + // --------------------------------------- + } + + super._afterTokenTransfer(from, to, tokenId, quantity); + } + + + /** + * @dev Override {supportsInterface} to declare support for IERC7634. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { + return interfaceId == type(IERC7634).interfaceId || super.supportsInterface(interfaceId); + } +} + +``` + +## Security Considerations + +- Ensure that each NFT minter can call this function to set transfer limits. +- Consider making transfer limits immutable once set to prevent tampering or unauthorized modifications. +- Avoid performing resource-intensive operations when integration with advanced functions that could exceed the gas limit during execution. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 626601072ee97b6265810ca244e0ccc4453bc960 Mon Sep 17 00:00:00 2001 From: Konrad Date: Tue, 4 Jun 2024 15:53:00 +0100 Subject: [PATCH 006/126] Update ERC-7484: require sorted attesters Merged by EIP-Bot. --- ERCS/erc-7484.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ERCS/erc-7484.md b/ERCS/erc-7484.md index f5ff17e70b..80c07471e7 100644 --- a/ERCS/erc-7484.md +++ b/ERCS/erc-7484.md @@ -107,6 +107,7 @@ The Registry SHOULD also implement the following additional functionality: - The Registry MUST revert if the number of `attesters` that have made an attestation on the `module` is smaller than the `threshold`. - The Registry MUST revert if any `attester` has revoked their attestation on the `module`. +- The `attesters` provided MUST be unique and sorted and the Registry MUST revert if they are not. #### `check` functions with moduleType @@ -120,6 +121,7 @@ The Registry SHOULD also implement the following additional functionality: #### `trustAttesters` - The Registry MUST store the `threshold` and `attesters` for the `msg.sender`. +- The `attesters` provided MUST be unique and sorted and the Registry MUST revert if they are not. ### Adapter behavior From 34e7e107786983267d190032b013292b91389b1b Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Thu, 6 Jun 2024 19:29:48 -0400 Subject: [PATCH 007/126] Add ERC: Paymaster Web Service Capability Merged by EIP-Bot. --- ERCS/erc-7677.md | 350 ++++++++++++++++++++++++++++++++++++++++++ assets/erc-7677/0.png | Bin 0 -> 153119 bytes assets/erc-7677/1.png | Bin 0 -> 149412 bytes 3 files changed, 350 insertions(+) create mode 100644 ERCS/erc-7677.md create mode 100644 assets/erc-7677/0.png create mode 100644 assets/erc-7677/1.png diff --git a/ERCS/erc-7677.md b/ERCS/erc-7677.md new file mode 100644 index 0000000000..ff3e0b818d --- /dev/null +++ b/ERCS/erc-7677.md @@ -0,0 +1,350 @@ +--- +eip: 7677 +title: Paymaster Web Service Capability +description: A way for apps to communicate with smart wallets about paymaster web services +author: Lukas Rosario (@lukasrosario), Dror Tirosh (@drortirosh), Wilson Cusack (@wilsoncusack), Kristof Gazso (@kristofgazso), Hazim Jumali (@hazim-j) +discussions-to: https://ethereum-magicians.org/t/erc-7677-paymaster-web-service-capability/19530 +status: Draft +type: Standards Track +category: ERC +created: 2024-04-03 +requires: 4337, 5792 +--- + +## Abstract + +With [EIP-5792](./eip-5792.md), apps can communicate with wallets about advanced features via capabilities. This proposal defines a capability that allows apps to request that [ERC-4337](./eip-4337.md) wallets communicate with a specified paymaster web service. To support this, we also define a standardized API for paymaster web services. + +## Motivation + +App developers want to start sponsoring their users' transactions using paymasters. Paymasters are commonly used via web services. However, there is currently no way for apps to tell wallets to communicate with a specific paymaster web service. Similarly, there is no standard for how wallets should communicate with these services. We need both a way for apps to tell wallets to communicate with a specific paymaster web service and a communication standard for wallets to do so. + +## Specification + +One new [EIP-5792](./eip-5792.md) wallet capability is defined. We also define a standard interface for paymaster web services as a prerequisite. + +### Paymaster Web Service Interface + +We define two JSON-RPC methods to be implemented by paymaster web services. + +#### `pm_getPaymasterStubData` + +Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation. Accepts an unsigned user operation, entrypoint address, chain id, and a context object. Paymaster service providers can define fields that app developers should use in the context object. + +This method MAY return paymaster-specific gas values if applicable to the provided EntryPoint version. For example, if provided with EntryPoint v0.7, this method MAY return `paymasterVerificationGasLimit` and `paymasterPostOpGasLimit` values. The wallet SHOULD use these provided gas values when submitting the `UserOperation` to a bundler for gas estimation. + +##### `pm_getPaymasterStubData` RPC Specification + +Note that the user operation parameter does not include a signature, as the user signs after all other fields are populated. + +```typescript +// [userOp, entryPoint, chainId, context] +type GetPaymasterStubDataParams = [ + // Below is specific to Entrypoint v0.6 but this API can be used with other entrypoint versions too + { + sender: `0x${string}`; + nonce: `0x${string}`; + initCode: `0x${string}`; + callData: `0x${string}`; + callGasLimit: `0x${string}`; + verificationGasLimit: `0x${string}`; + preVerificationGas: `0x${string}`; + maxFeePerGas: `0x${string}`; + maxPriorityFeePerGas: `0x${string}`; + }, // userOp + `0x${string}`, // EntryPoint + `0x${string}`, // Chain ID + Record // Context +]; + +type GetPaymasterStubDataResult = { + paymaster?: string // Paymaster address (entrypoint v0.7) + paymasterData?: string; // Paymaster data (entrypoint v0.7) + paymasterVerificationGasLimit?: `0x${string}`; // Paymaster validation gas (entrypoint v0.7) + paymasterPostOpGasLimit?: `0x${string}`; // Paymaster post-op gas (entrypoint v0.7) + paymasterAndData?: string; // Paymaster and data (entrypoint v0.6) +} +``` + +###### `pm_getPaymasterStubData` Example Parameters + +```json +[ + { + "sender": "0x...", + "nonce": "0x...", + "initCode": "0x", + "callData": "0x...", + "callGasLimit": "0x...", + "verificationGasLimit": "0x...", + "preVerificationGas": "0x...", + "maxFeePerGas": "0x...", + "maxPriorityFeePerGas": "0x...", + }, + "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "0x2105", + { + // Illustrative context field. These should be defined by service providers. + "policyId": "962b252c-a726-4a37-8d86-333ce0a07299" + } +] +``` + +###### `pm_getPaymasterStubData` Example Return Value + +Paymaster services MUST detect which entrypoint version the account is using and return the correct fields. + +For example, if using entrypoint v0.6: + +```json +{ + "paymasterAndData": "0x..." +} +``` + +If using entrypoint v0.7: + +```json +{ + "paymaster": "0x...", + "paymasterData": "0x..." +} +``` + +If using entrypoint v0.7, with paymaster gas estimates: + +```json +{ + "paymaster": "0x...", + "paymasterData": "0x...", + "paymasterVerificationGasLimit": "0x...", + "paymasterPostOpGasLimit": "0x..." +} +``` + +#### `pm_getPaymasterData` + +Returns values to be used in paymaster-related fields of a signed user operation. These are not stub values and will be used during user operation submission to a bundler. Similar to `pm_getPaymasterStubData`, accepts an unsigned user operation, entrypoint address, chain id, and a context object. + +This method also returns a `sponsor` object with `name` and `icon` fields. The `name` field is the name of the party sponsoring the transaction, and the `icon` field is a URI pointing to an image. Wallet developers MAY choose to display sponsor information to users. The `icon` string MUST be a data URI as defined in [RFC-2397]. The image SHOULD be a square with 96x96px minimum resolution. The image format is RECOMMENDED to be either lossless or vector based such as PNG, WebP or SVG to make the image easy to render on the wallet. Since SVG images can execute Javascript, wallets MUST render SVG images using the `` tag to ensure no untrusted Javascript execution can occur. + +##### `pm_getPaymasterData` RPC Specification + +Note that the user operation parameter does not include a signature, as the user signs after all other fields are populated. + +```typescript +// [userOp, entryPoint, chainId, context] +type GetPaymasterDataParams = [ + // Below is specific to Entrypoint v0.6 but this API can be used with other entrypoint versions too + { + sender: `0x${string}`; + nonce: `0x${string}`; + initCode: `0x${string}`; + callData: `0x${string}`; + callGasLimit: `0x${string}`; + verificationGasLimit: `0x${string}`; + preVerificationGas: `0x${string}`; + maxFeePerGas: `0x${string}`; + maxPriorityFeePerGas: `0x${string}`; + }, // userOp + `0x${string}`, // Entrypoint + `0x${string}`, // Chain ID + Record // Context +]; + +type GetPaymasterDataResult = { + sponsor?: { name: string; icon: string } // Sponsor info + paymaster?: string // Paymaster address (entrypoint v0.7) + paymasterData?: string; // Paymaster data (entrypoint v0.7) + paymasterAndData?: string; // Paymaster and data (entrypoint v0.6) +} +``` + +###### `pm_getPaymasterData` Example Parameters + +```json +[ + { + "sender": "0x...", + "nonce": "0x...", + "initCode": "0x", + "callData": "0x...", + "callGasLimit": "0x...", + "verificationGasLimit": "0x...", + "preVerificationGas": "0x...", + "maxFeePerGas": "0x...", + "maxPriorityFeePerGas": "0x...", + }, + "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "0x2105", + { + // Illustrative context field. These should be defined by service providers. + "policyId": "962b252c-a726-4a37-8d86-333ce0a07299" + } +] +``` + +###### `pm_getPaymasterData` Example Return Value + +Paymaster services MUST detect which entrypoint version the account is using and return the correct fields. + +For example, if using entrypoint v0.6: + +```json +{ + "sponsor": { + "name": "My App", + "icon": "https://..." + }, + "paymasterAndData": "0x..." +} +``` + +If using entrypoint v0.7: + +```json +{ + "sponsor": { + "name": "My App", + "icon": "https://..." + }, + "paymaster": "0x...", + "paymasterData": "0x..." +} +``` + +### `paymasterService` Capability + +The `paymasterService` capability is implemented by both apps and wallets. + +#### App Implementation + +Apps need to give wallets a paymaster service URL they can make the above RPC calls to. They can do this using the `paymasterService` capability as part of an [EIP-5792](./eip-5792.md) `wallet_sendCalls` call. + +##### `wallet_sendCalls` Paymaster Capability Specification + +```typescript +type PaymasterCapabilityParams = { + url: string; + context: Record; +} +``` + +###### `wallet_sendCalls` Example Parameters + +```json +[ + { + "version": "1.0", + "chainId": "0x01", + "from": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "calls": [ + { + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "value": "0x9184e72a", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }, + { + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "value": "0x182183", + "data": "0xfbadbaf01" + } + ], + "capabilities": { + "paymasterService": { + "url": "https://...", + "context": { + "policyId": "962b252c-a726-4a37-8d86-333ce0a07299" + } + } + } + } +] +``` + +The wallet will then make the above paymaster RPC calls to the URL specified in the `paymasterService` capability field. + +#### Wallet Implementation + +To conform to this specification, smart wallets that wish to leverage app-sponsored transactions: +1. MUST indicate to apps that they can communicate with paymaster web services via their response to an [EIP-5792](./eip-5792.md) `wallet_getCapabilities` call. +2. SHOULD make calls to and use the values returned by the paymaster service specified in the capabilities field of an [EIP-5792](./eip-5792.md) `wallet_sendCalls` call. An example of an exception is a wallet that allows users to select a paymaster provided by the wallet. Since there might be cases in which the provided paymaster is ultimately not usedโ€”either due to service failure or due to a user selecting a different, wallet-provided paymasterโ€”applications MUST NOT assume that the paymaster it provides to a wallet is the entity that pays for transaction fees. + +##### `wallet_getCapabilities` Response Specification + +```typescript +type PaymasterServiceCapability = { + supported: boolean; +} +``` + +###### `wallet_getCapabilities` Example Response + +```json +{ + "0x2105": { + "paymasterService": { + "supported": true + }, + }, + "0x14A34": { + "paymasterService": { + "supported": true + } + } +} +``` + +Below is a diagram illustrating the full `wallet_sendCalls` flow, including how a wallet might implement the interaction. + +![flow](../assets/eip-7677/0.png) + +## Rationale + +### Gas Estimation + +The current loose standard for paymaster services is to implement `pm_sponsorUserOperation`. This method returns values for paymaster-related user operation fields and updated gas values. The problem with this method is that paymaster service providers have different ways of estimating gas, which results in different estimated gas values. Sometimes these estimates can be insufficient. As a result we believe itโ€™s better to leave gas estimation up to the wallet, as the wallet has more context on how user operations will get submitted (e.g. which bundler they will get submitted to). Then wallets can ask paymaster services to sponsor given the estimates defined by the wallet. + +The above reason is also why we specify that the `pm_getPaymasterStubData` method MAY also return paymaster-specific gas estimates. I.e., bundlers are prone to insufficiently estimating the paymaster-specific gas values, and the paymaster servies themselves are ultimately better equipped to provide them. + +### Chain ID Parameter + +Currently, paymaster service providers typically provide developers with a URL per chain. That is, paymaster service URLs are not typically multichain. So why do we need a chain ID parameter? We recognize that we must specify some constraint so that wallets can communicate with paymaster services about which chain their requests are for. As we see it, there are two options: + +1. Formalize the current loose standard and require that paymaster service URLs are 1:1 with chains. +2. Require a chain ID parameter as part of paymaster service requests. + +We feel that option (2) is the better abstraction here. This allows service providers to offer multichain URLs if they wish at essentially no downside to providers who offer a URL per chain. Providers who offer a URL per chain would just need to accept an additional parameter that they can ignore. When an app developer who uses a URL-per-chain provider wants to submit a request to a different chain, they can just swap out the URL accordingly. + +### Challenges With Stub Data + +Enabling a workflow with greater flexibility in gas estimations will nonetheless come with some known challenges that paymaster services must be aware of in order to ensure reliable gas estimates are generated during the process. + +#### `preVerificationGas` + +The `preVerificationGas` value is largely influenced by the size of the user operation and it's ratio of zero to non-zero bytes. This can cause a scenario where `pm_getPaymasterStubData` returns values that results in upstream gas estimations to derive a lower `preVerificationGas` compared to what `pm_getPaymasterData` would require. If this occurs then bundlers will return an insufficient `preVerificationGas` error during `eth_sendUserOperation`. + +To avoid this scenario, a paymaster service MUST return stub data that: + +1. Is of the same length as the final data. +2. Has an amount of zero bytes (`0x00`) that is less than or equal to the final data. + +#### Consistent Code Paths + +In the naive case, a stub value of repeating non-zero bytes (e.g. `0x01`) that is of the same length as the final value will generate a usable `preVerificationGas`. Although this would immediately result in a gas estimation error given that the simulation will likely revert due to an invalid paymaster data. + +In a more realistic case, a valid stub can result in a successful simulation but still return insufficient gas limits. This can occur if the stub data causes `validatePaymasterUserOp` or `postOp` functions to simulate a different code path compared to the final value. For example, if the simulated code was to return early, the estimated gas limits would be less than expected which would cause upstream `out of gas` errors once a user operation is submitted to the bundler. + +Therefore, a paymaster service MUST also return a stub that can result in a simulation executing the same code path compared to what is expected of the final user operation. + +## Security Considerations + +The URLs paymaster service providers give to app developers commonly have API keys in them. App developers might not want to pass these API keys along to wallets. To remedy this, we recommend that app developers provide a URL to their app's backend, which can then proxy calls to paymaster services. Below is a modified diagram of what this flow might look like. + +![flowWithAPI](../assets/eip-7677/0.png) + +This flow would allow developers to keep their paymaster service API keys secret. Developers might also want to do additional simulation / validation in their backends to ensure they are sponsoring a transaction they want to sponsor. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/assets/erc-7677/0.png b/assets/erc-7677/0.png new file mode 100644 index 0000000000000000000000000000000000000000..84860abe5ca79cac764dca2686987850cc925fa4 GIT binary patch literal 153119 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

uk2JCSPS^Gt59R%C$A$Az#` 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¥`t5gCHnj%q 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 Zg)o_i)egkJTw{PgX>o-&MWP>k{|);@Oz!{y literal 0 HcmV?d00001 diff --git a/assets/erc-7677/1.png b/assets/erc-7677/1.png new file mode 100644 index 0000000000000000000000000000000000000000..860386e9c9aa46b990a606277c58d9f2cce867ff GIT binary patch literal 149412 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%0DAo;g^#kVIeDRj$6 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 T`4oJa1pKKeYCgm%SfT$1O`lCC literal 0 HcmV?d00001 From 90edb4298a87722c4bb6869b424176b51ebdb3fa Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Fri, 7 Jun 2024 10:58:35 -0400 Subject: [PATCH 008/126] Add ERC: Auxiliary Funds Capability Merged by EIP-Bot. --- ERCS/erc-7682.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 ERCS/erc-7682.md diff --git a/ERCS/erc-7682.md b/ERCS/erc-7682.md new file mode 100644 index 0000000000..a210c89dce --- /dev/null +++ b/ERCS/erc-7682.md @@ -0,0 +1,79 @@ +--- +eip: 7682 +title: Auxiliary Funds Capability +description: A way for wallets to indicate to apps that they have access to additional funds +author: Lukas Rosario (@lukasrosario), Wilson Cusack (@wilsoncusack) +discussions-to: https://ethereum-magicians.org/t/erc-7682-auxiliary-funds-capability/19599 +status: Draft +type: Standards Track +category: ERC +created: 2024-04-09 +requires: 5792 +--- + +## Abstract + +An [EIP-5792](./eip-5792.md) compliant capability that allows wallets to indicate to apps that they have access to funds beyond those that can be accounted for by looking up balances onchain given the wallet's address. + +## Motivation + +Many applications check users' balances before letting them complete some action. For example, if a user wants to swap some amount of tokens on a dex, the dex will commonly block the user from doing so if it sees that the user does not have that amount of tokens at their address. However, more advanced wallets have features that let users access funds from other sources. Wallets need a way to tell apps that they have access to additional funds so that users using these more advanced wallets are not blocked by balance checks. + +## Specification + +One new [EIP-5792](./eip-5792.md) wallet capability is defined. + +### Wallet Implementation + +To conform to this specification, wallets that wish to indicate that they have access to auxiliary funds MUST respond to `wallet_getCapabilities` calls with an `auxiliaryFunds` object with a `supported` field set to `true` for each chain they have access to auxiliary funds on. This specification does not put any constraints on the source of the auxiliary funds. + +#### `wallet_getCapabilities` Response Specification + +```typescript +type AuxiliaryFundsCapability = { + supported: boolean; +} +``` + +##### `wallet_getCapabilities` Example Response + +```json +{ + "0x2105": { + "auxiliaryFunds": { + "supported": true + }, + }, + "0x14A34": { + "auxiliaryFunds": { + "supported": true + } + } +} +``` + +### App Implementation + +When an app sees that a connected wallet has access to auxiliary funds via the `auxiliaryFunds` capability in a `wallet_getCapabilities` response, the app SHOULD NOT block users from taking actions on the basis of asset balance checks. + +## Rationale + +### Alternatives + +#### Advanced Balance Fetching + +An alternative we considered is defining a way for apps to fetch available auxiliary balances. This could be done, for example, by providing a URL as part of the `auxiliaryFunds` capability that apps could use to fetch auxiliary balance information. However, we ultimately decided that a boolean was enough to indicate to apps that they should not block user actions on the basis of balance checks, and it is minimally burdensome for apps to implement. + +The shape of this capability allows for a more advanced extension if apps feel more functionality is needed. + +#### Auxiliary Funds per Asset + +We could also specify auxiliary funds support per asset. We decided against this because this list could get quite large if a wallet has auxiliary funds supports for many assets, and a single boolean should be enough for apps to not block users from taking actions. + +## Security Considerations + +Apps MUST NOT make any assumptions about the source of auxiliary funds. Apps' smart contracts should still, as they would today, make appropriate balance checks onchain when processing a transaction. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From c0448e9af234d2b4fe760eede7998920f63bba41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cuesta=20Ca=C3=B1ada?= <38806121+alcueca@users.noreply.github.com> Date: Fri, 7 Jun 2024 22:37:54 +0100 Subject: [PATCH 009/126] Update ERC-7399: Don't revert on view functions. Merged by EIP-Bot. --- ERCS/erc-7399.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ERCS/erc-7399.md b/ERCS/erc-7399.md index e97b1a0a1c..94f0c841d2 100644 --- a/ERCS/erc-7399.md +++ b/ERCS/erc-7399.md @@ -1,8 +1,8 @@ --- eip: 7399 -title: Flash Loans +title: โšก Flash Loans โšก description: Interfaces and processes for flash loans -author: Alberto Cuesta Caรฑada (@alcueca), Ultrasecr.eth (@ultrasecreth), Devtooligan (@devtooligan), Michael Amadi (@AmadiMichaels) +author: Alberto Cuesta Caรฑada (@alcueca), Michael Amadi (@AmadiMichaels), Devtooligan (@devtooligan), Ultrasecr.eth (@ultrasecreth), Sam Bacha (@sambacha) discussions-to: https://ethereum-magicians.org/t/erc7400-flash-loans/15211 status: Draft type: Standards Track @@ -106,9 +106,9 @@ interface IERC7399 { ``` -The `maxFlashLoan` function MUST return the maximum available loan for `asset`. The `maxFlashLoan` function MUST NOT revert. If the asset is not supported `maxFlashLoan` the value returned MUST be zero. +The `maxFlashLoan` function MUST return the maximum available loan for `asset`. The `maxFlashLoan` function MUST NOT revert. If no flash loans for the specified `asset` are possible, the value returned MUST be zero. -The `flashFee` function MUST return the fee charged for a loan of `amount` `asset`. If the asset is not supported `flashFee` MUST revert. If the lender doesn't have enough liquidity to loan `amount` the fee returned MUST be `type(uint256).max`. If an `asset`is supported but the available liquidity for it is exactly zero, the fee returned still MUST be `type(uint256).max`. +The `flashFee` function MUST return the fee charged for a loan of `amount` `asset`. The `flashFee` function MUST NOT revert. If a flash loan for the specified `asset` and `amount` is not possible, the value returned MUST be `type(uint256).max`. The `flash` function MUST execute the callback passed on as an argument. @@ -148,9 +148,11 @@ function(address, address, IERC20, uint256, uint256, bytes memory) external retu The interfaces described in this ERC have been chosen as to cover the known flash lending use cases, while allowing for safe and gas efficient implementations. -`flashFee` reverts on unsupported assets, because returning a numerical value would be incorrect. +`maxFlashLoan` and `flashFee` return numerical values on impossible loans to allow sorting lenders without having to deal with reverts. -`flashFee` returns a value that is consistent with an impossible loan when the `lender` doesn't have enough liquidity to serve the loan. +`maxFlashLoan` returns a value that is consistent with an impossible loan when the `lender` is not able to serve the loan. + +`flashFee` returns a value that is consistent with an impossible loan when the `lender` is not able to serve the loan. `flash` has been chosen as a function name as a verb which is descriptive enough, unlikely to clash with other functions in the `lender`, and including both the use cases in which the assets lent are held or minted by the `lender`. From 3dd4f3e59faa52097daab4c1c9e5ae093a9e8245 Mon Sep 17 00:00:00 2001 From: Anderson Chen Date: Sat, 8 Jun 2024 06:18:52 +0800 Subject: [PATCH 010/126] Add ERC: Tokenized Vaults with Lock-in Period Merged by EIP-Bot. --- ERCS/erc-6229.md | 321 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 ERCS/erc-6229.md diff --git a/ERCS/erc-6229.md b/ERCS/erc-6229.md new file mode 100644 index 0000000000..c06fcd35f7 --- /dev/null +++ b/ERCS/erc-6229.md @@ -0,0 +1,321 @@ +--- +eip: 6229 +title: Tokenized Vaults with Lock-in Period +description: ERC-4626 Tokenized Vaults with Lock-in Period. +author: Anderson Chen (@Ankarrr), Martinet Lee , Anton Cheng +discussions-to: https://ethereum-magicians.org/t/eip-tokenized-vaults-with-lock-in-period/12298 +status: Draft +type: Standards Track +category: ERC +created: 2022-12-21 +requires: 4626 +--- + +## Abstract + +This standard extends [EIP-4626](./eip-4626.md) to support lock-in periods. + +## Motivation + +The [EIP-4626](./eip-4626.md) standard defines a tokenized vault allowing users (contracts or EOAs) to deposit and withdraw underlying tokens at any time. However, there exist cases where the vault needs to lock the underlying tokens (perhaps to execute certain strategies). During the lock-in period, neither withdrawals nor deposits should be allowed. This standard extends the EIP-4626 to support lock-in periods and handle scheduled deposits and withdrawals during them. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +All vaults that follow this EIP MUST implement [EIP-4626](./eip-4626.md) to provide basic vault functions and [EIP-20](./eip-20.md) to represent shares. + +### Definitions + +- asset: The underlying [EIP-20](./eip-20.md) token that the vault accepts and manages. +- share: The EIP-20 token that the vault issued. +- locked: A status of the vault. When the vault is locked, user canโ€™t withdraw or deposit assets from the vault. +- unlocked: A status of the vault. When the vault is unlocked, user can withdraw or deposit assets from the vault. +- round: The period that the vault is locked. + +### View Methods + +#### isLocked + +The current state of the vault. + +`true` represents a vault is in the locked state, and `false` represents a vault is in the unlocked state. + +```yaml +- name: isLocked + type: bool + stateMutability: view + + inputs: [] + + outputs: + - name: isLocked + type: bool + +``` + +#### vaultRound + +The current round of the vault. + +MUST start with `0`. + +MUST add `1` each time a new round starts, that is, when the `isLocked` becomes true. MUST NOT be modified in any other circumstances. + +```yaml +- name: vaultRound + type: uint256 + stateMutability: view + + inputs: [] + + outputs: + - name: vaultRound + type: uint256 +``` + +### Methods + +#### scheduleDeposit + +Schedule the intent to deposit `assets` when the `isLocked` is true. + +MUST only be callable when the `isLocked` is true. + +MUST transfer the `assets` from the caller to the vault. MUST not issue new shares. + +MUST revert if `assets`ย cannot be deposited. + +MUST revert if the `isLocked` is false. + +```yaml +- name: scheduleDeposit + type: function + stateMutability: nonpayable + + inputs: + - name: assets + type: uint256 +``` + +#### scheduleRedeem + +Schedule the intent to redeem `shares` from the vault when the `isLocked` is true. + +MUST only be callable when the `isLocked` is true. + +MUST transfer the `shares` from the caller to the vault. MUST not transfer assets to caller. + +MUST revert if `shares`ย cannot be redeemed. + +MUST revert if the `isLocked` is false. + +```yaml +- name: scheduleRedeem + type: function + stateMutability: nonpayable + + inputs: + - name: shares + type: uint256 +``` + +#### settleDeposits + +Process all scheduled deposits for `depositor` and minting `newShares`. + +MUST only be callable when the `isLocked` is false. + +MUST issue `newShares` according to the current share price for the scheduled `depositor`. + +MUSTย revert if there is no scheduled deposit for `depositor`. + +```yaml +- name: settleDeposits + type: function + stateMutability: nonpayable + + inputs: + - name: depositor + - type: address + + outputs: + - name: newShares + - type: uint256 +``` + +#### settleRedemptions + +Process all scheduled redemptions for `redeemer` by burning `burnShares` and transferring `redeemAssets` to the `redeemer`. + +MUST only be callable when the `isLocked` is false. + +MUST burn the `burnShares` and transfer `redeemAssets` back to the `redeemer` according to the current share price. + +MUSTย revert if no scheduled redemption for `redeemer`. + +```yaml +- name: settleRedemptions + type: function + stateMutability: nonpayable + + inputs: + - name: redeemer + - type: address + + outputs: + - name: burnShares + - type: uint256 + - name: redeemAssets + - type: uint256 +``` + +#### getScheduledDeposits + +Get the `totalAssets` of scheduled deposits for `depositor`. + +MUSTย NOTย revert. + +```yaml +- name: getScheduledDeposits + type: function + stateMutability: view + + inputs: + - name: depositor + - type: address + + outputs: + - name: totalAssets + - type: uint256 +``` + +#### getScheduledRedemptions + +Get the `totalShares` of scheduled redemptions for `redeemer`. + +MUSTย NOTย revert. + +```yaml +- name: getScheduledRedemptions + type: function + stateMutability: view + + inputs: + - name: redeemer + - type: address + + outputs: + - name: totalShares + - type: uint256 +``` + +### Events + +#### ScheduleDeposit + +`sender` schedules a deposit with `assets` in this `round`. + +MUST be emitted via `scheduleDeposit` method. + +```yaml +- name: ScheduleDeposit + type: event + + inputs: + - name: sender + indexed: true + type: address + - name: assets + indexed: false + type: uint256 + - name: round + indexed: false + type: uint256 +``` + +#### ScheduleRedeem + +`sender` schedules a redemption with `shares` in this `round`. + +MUST be emitted via `scheduleRedeem` method. + +```yaml +- name: ScheduleRedeem + type: event + + inputs: + - name: sender + indexed: true + type: address + - name: shares + indexed: false + type: uint256 + - name: round + indexed: false + type: uint2 +``` + +#### SettleDeposits + +Settle scheduled deposits for `depositor` in this `round`. Issue `newShares` and transfer them to the `depositor`. + +MUST be emitted via `settleDeposits` method. + +```yaml +- name: SettleDeposits + type: event + + inputs: + - name: depositor + indexed: true + type: address + - name: newShares + type: uint256 + - name: round + type: uint256 +``` + +#### SettleRedemptions + +Settle scheduled redemptions for `redeemer` in this `round`. Burn `burnShares` and transfer `redeemAssets` back to the `redeemer`. + +MUST be emitted via `settleRedemptions` method. + +```yaml +- name: SettleRedemptions + type: event + + inputs: + - name: redeemer + indexed: true + type: address + - name: burnShares + type: uint256 + - name: redeemAssets + type: uint256 + - name: round + type: uint256 +``` + +## Rationale + +The standard is designed to be a minimal interface. Details such as the start and end of a lock-in period, and how the underlying tokens are being used during the lock-in period are not specified. + +There is no function for scheduling a withdrawal, since during the lock-in period, the share price is undetermined, so it is impossible to determine how many underlying tokens can be withdrawn. + +## Backwards Compatibility + +The `deposit`, `mint`, `withdraw`, `redeem` methods for [EIP-4626](./eip-4626.md) should revert when the `isLocked` is true to prevent issuing or burning shares with an undefined share price. + +## Security Considerations + +Implementors need to be aware of unsettled scheduled deposits and redemptions. If a user has scheduled a deposit or redemption but does not settle when the `isLocked` is false, and then settles it after several rounds, the vault will process it with an incorrect share price. We didnโ€™t specify the solution in the standard since there are many possible ways to solve this issue and we think implementors should decide the solution according to their use cases. For example: + +- Not allow the `isLocked` to become true if there is any unsettled scheduled deposit or redemption +- Force settling the scheduled deposits or redemptions when the `isLocked` becomes true +- Memorize the ending share price for each round and let the users settle according to the share prices + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From cbfd4174d0e7ba59d5555dcb2f1fb9082e59c39a Mon Sep 17 00:00:00 2001 From: Liaoyuan Date: Mon, 10 Jun 2024 02:19:43 +0800 Subject: [PATCH 011/126] Update ERC-7627: Erc 7627 Merged by EIP-Bot. --- ERCS/erc-7627.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7627.md b/ERCS/erc-7627.md index 5188d1e9d9..09594a8ce5 100644 --- a/ERCS/erc-7627.md +++ b/ERCS/erc-7627.md @@ -39,7 +39,7 @@ pragma solidity ^0.8.0; interface IERC7627 { - enum PublicKeyAlgorithm { RSA, ECDSA, ED25519, DSA, DH, ECDH, X25519 } + enum PublicKeyAlgorithm { ECDSA, ED25519, X25519 } // Events @@ -98,7 +98,7 @@ pragma solidity ^0.8.0; contract ERC7627 { - enum PublicKeyAlgorithm { RSA, ECDSA, ED25519, DSA, DH, ECDH, X25519 } + enum PublicKeyAlgorithm { ECDSA, ED25519, X25519 } struct UserInfo { bytes publicKey; From 4e6c091b9556a7e3592d5eea423be480c1fb74a1 Mon Sep 17 00:00:00 2001 From: Liaoyuan Date: Mon, 10 Jun 2024 03:13:33 +0800 Subject: [PATCH 012/126] Update ERC-7644: Erc 7644 Merged by EIP-Bot. --- ERCS/erc-7644.md | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/ERCS/erc-7644.md b/ERCS/erc-7644.md index 474524d3c8..fd09d81159 100644 --- a/ERCS/erc-7644.md +++ b/ERCS/erc-7644.md @@ -44,13 +44,6 @@ pragma solidity ^0.8.0; * with associated expiry dates tied to specific tokens. */ interface IERC7644 /* is IERC721 */ { - /** - * @dev Emitted when a token is named. - * @param tokenId The token ID that is being named. - * @param newName The new name assigned to the token. - * @param expiryDate The expiry date of the name registration. - */ - event TokenNamed(uint256 indexed tokenId, bytes32 newName, uint256 expiryDate); /** * @dev Emitted when the name of a token is changed. @@ -121,7 +114,6 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract ERC7644 is ERC721 { - event TokenNamed(uint256 indexed tokenId, bytes32 newName, uint256 expiryDate); event NameChanged(uint256 indexed tokenId, bytes32 oldName, bytes32 newName, uint256 expiryDate); struct NameRegistration { @@ -136,41 +128,44 @@ contract ERC7644 is ERC721 { uint256 public constant MAX_DURATION = 10 * 365 days; uint256 public constant MIN_SET_NAME_INTERVAL = 1 days; - constructor() ERC721("My Token", "MTK") {} + constructor() ERC721("Asd Token", "ASDT") {} function nameOf(uint256 tokenId) public view returns (bytes32) { - require(_tokenNames[tokenId] != bytes32(0) && _nameRegistrations[_tokenNames[tokenId]].expiryDate > block.timestamp, "NameRegistry: Name expired or does not exist"); - return _tokenNames[tokenId]; + if(_tokenNames[tokenId] != bytes32(0) && _nameRegistrations[_tokenNames[tokenId]].expiryDate > block.timestamp) + { + return _tokenNames[tokenId]; + }else{ + return bytes32(0); + } } function tokenIdOf(bytes32 _name) public view returns (uint256) { require(_nameRegistrations[_name].expiryDate > block.timestamp, "NameRegistry: Name expired"); - return _nameRegistrations[_name].tokenId; + if(_nameRegistrations[_name].tokenId > 0) + { + return _nameRegistrations[_name].tokenId; + }else{ + return uint256(0); + } } function setName(uint256 tokenId, bytes32 _name, uint256 duration) public { require(ownerOf(tokenId) == msg.sender, "NameRegistry: Caller is not the token owner"); require(duration <= MAX_DURATION, "NameRegistry: Duration exceeds maximum limit"); require(block.timestamp - _lastSetNameTime[tokenId] >= MIN_SET_NAME_INTERVAL, "NameRegistry: Minimum interval not met"); - - // Check if name is either not registered or expired - require(_nameRegistrations[_name].expiryDate <= block.timestamp, "NameRegistry: Name already in use and not expired"); + require(tokenIdOf(_name) == uint256(0) || tokenIdOf(_name) == tokenId, "NameRegistry: Name already in use and not expired"); bytes32 oldName = _tokenNames[tokenId]; uint256 expiryDate = block.timestamp + duration; _setTokenName(tokenId, _name, expiryDate); - if (oldName != bytes32(0)) { - emit NameChanged(tokenId, oldName, _name, expiryDate); - } else { - emit TokenNamed(tokenId, _name, expiryDate); - } + emit NameChanged(tokenId, oldName, _name, expiryDate); _lastSetNameTime[tokenId] = block.timestamp; } function nameInfo(bytes32 _name) public view returns (uint256, uint256) { - require(_nameRegistrations[_name].expiryDate > block.timestamp, "NameRegistry: Name expired or does not exist"); + require(_nameRegistrations[_name].tokenId > 0 && _nameRegistrations[_name].expiryDate > block.timestamp, "NameRegistry: Name expired or does not exist"); NameRegistration memory registration = _nameRegistrations[_name]; return (registration.tokenId, registration.expiryDate); } From cc4a67a8e1838faa6bee5392a0007b797abb1010 Mon Sep 17 00:00:00 2001 From: Adam Egyed <5456061+adamegyed@users.noreply.github.com> Date: Mon, 10 Jun 2024 15:11:45 -0400 Subject: [PATCH 013/126] Update ERC-6900: clarify security considerations Merged by EIP-Bot. --- ERCS/erc-6900.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index a854fbbca8..cec5f898dc 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -550,9 +550,9 @@ See `https://github.com/erc6900/reference-implementation` ## Security Considerations -As much as the modular smart contract accounts themselves, installed plugins are trusted components, as a modular smart contract account can be almost arbitrarily configured through installed plugins. A malicious plugin could add always reverting hooks to native function selectors, bricking the account, as well as add function selectors that end up draining the funds of the account. Users should therefore be very careful in what plugins to add to their account. +The modular smart contract accounts themselves are trusted components. Installed plugins are trusted to varying degrees, as plugins can interact with an arbitrarily large or small set of resources on an account. For example, a wide-reaching malicious plugin could add reverting hooks to native function selectors, bricking the account, or add execution functions that may drain the funds of the account. However, it is also possible to install a plugin with a very narrow domain, and depend on the correctness of the account behavior to enforce its limited access. Users should therefore be careful in what plugins to add to their account. -Users should therefore perform careful due diligence before installing a plugin and should be mindful of the fact that plugins are potentially dangerous. The plugin's manifest can give users an understanding of the domain of the plugin, i.e., the requested permissions to install certain validation functions and/or hooks on certain execution selectors. Generally, plugins that include native function selectors in their domain, e.g., plugins that add a validation hook to the native `uninstallPlugin()` function, can potentially introduce significantly more harm than plugins that simply add validation hooks to function selectors that the plugin itself is adding to the account. +Users should perform careful due diligence before installing a plugin and should be mindful of the fact that plugins are potentially dangerous. The plugin's manifest can give users an understanding of the domain of the plugin, i.e., the requested permissions to install certain validation functions and/or hooks on certain execution selectors. Generally, plugins that include native function selectors in their domain, e.g., plugins that add a validation hook to the native `uninstallPlugin()` function, can introduce significantly more harm than plugins that simply add validation hooks to function selectors that the plugin itself is adding to the account. Plugins can also add validation hooks to function selectors installed by other plugins. While usually, such a plugin would, e.g., add additional pre-validation hooks, it can also cause the previously installed plugin to be executed in an unintended context. For example, if a plugin were to only be intended to operate in the user operation context, its plugin manifest might only define user operation validation functions. However, another plugin might add a passing runtime validation function to that function selector, causing, for example, a session key plugin to suddenly be executed in a runtime validation context, circumventing all the parameter-validation that would have happened during user operation validation and granting unrestricted access to all session keys. Therefore, it is strongly recommended to always add reverting validation hooks to the context the plugin is not intended to be executed in. This recommendation may change in the next iteration of the standard. From bb17ed70c740dded2fda53b33ee2d855a54c8810 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Tue, 11 Jun 2024 16:04:13 +0200 Subject: [PATCH 014/126] Update ERC-7575: Move to Final Merged by EIP-Bot. --- ERCS/erc-7575.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ERCS/erc-7575.md b/ERCS/erc-7575.md index efe9ec98ba..c3e279e88c 100644 --- a/ERCS/erc-7575.md +++ b/ERCS/erc-7575.md @@ -4,8 +4,7 @@ title: Multi-Asset ERC-4626 Vaults description: Extended ERC-4626 Interface enabling Multi-Asset Vaults author: Jeroen Offerijns (@hieronx), Alina Sinelnikova (@ilinzweilin), Vikram Arun (@vikramarun), Joey Santoro (@joeysantoro), Farhaan Ali (@0xfarhaan) discussions-to: https://ethereum-magicians.org/t/erc-7575-partial-and-extended-erc-4626-vaults/17274 -status: Last Call -last-call-deadline: 2024-06-11 +status: Final type: Standards Track category: ERC created: 2023-12-11 From 9de3314cac8355dac2df0d9b5abdd3a31d5c69c4 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Tue, 11 Jun 2024 16:06:42 +0200 Subject: [PATCH 015/126] Update ERC-7540: Move to Last Call Merged by EIP-Bot. --- ERCS/erc-7540.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ERCS/erc-7540.md b/ERCS/erc-7540.md index 8f3c0ca8ea..e5ef6fd5aa 100644 --- a/ERCS/erc-7540.md +++ b/ERCS/erc-7540.md @@ -4,7 +4,8 @@ title: Asynchronous ERC-4626 Tokenized Vaults description: Extension of ERC-4626 with asynchronous deposit and redemption support author: Jeroen Offerijns (@hieronx), Alina Sinelnikova (@ilinzweilin), Vikram Arun (@vikramarun), Joey Santoro (@joeysantoro), Farhaan Ali (@0xfarhaan), Joรฃo Martins (@0xTimepunk) discussions-to: https://ethereum-magicians.org/t/eip-7540-asynchronous-erc-4626-tokenized-vaults/16153 -status: Review +status: Last Call +last-call-deadline: 2024-06-25 type: Standards Track category: ERC created: 2023-10-18 From 3299361722608caa32300c4bc9a99b14002311c3 Mon Sep 17 00:00:00 2001 From: OniReimu Date: Wed, 12 Jun 2024 00:08:21 +1000 Subject: [PATCH 016/126] Update ERC-5521: Move to Final Merged by EIP-Bot. --- ERCS/erc-5521.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ERCS/erc-5521.md b/ERCS/erc-5521.md index 74cddf2fd7..6c08946e13 100644 --- a/ERCS/erc-5521.md +++ b/ERCS/erc-5521.md @@ -4,8 +4,7 @@ title: Referable NFT description: An ERC-721 extension to construct reference relationships among NFTs author: Saber Yu (@OniReimu), Qin Wang , Shange Fu , Yilin Sai , Shiping Chen , Sherry Xu , Jiangshan Yu discussions-to: https://ethereum-magicians.org/t/eip-x-erc-721-referable-nft/10310 -status: Last Call -last-call-deadline: 2024-06-07 +status: Final type: Standards Track category: ERC created: 2022-08-10 @@ -128,7 +127,7 @@ interface TargetContract is IERC165 { ### Is this event informative enough? `UpdateNode`: This event disseminates crucial information, including the rNFT ID, its owner, and lists of contract addresses/IDs with rNFTs referring to or referred by the subject rNFT. This data set enables stakeholders to efficiently manage and navigate the complex web of relationships inherent in the rNFT ecosystem. -Implementers are free to choose to use a struct (a **RECOMMENDED** struct is given in the Reference Implementation), or several separate mappings, or whatever other storage mechanism. Whichever mechanism chosen has no observable effect on the behaviour of the contract, as long as its output can fulfill the `UpdateNode` event. +Implementers are free to choose to use a struct (a recommended struct is given in the Reference Implementation), or several separate mappings, or whatever other storage mechanism. Whichever mechanism chosen has no observable effect on the behaviour of the contract, as long as its output can fulfill the `UpdateNode` event. ### Why `createdTimestampOf`? `createdTimestamp`: A key principle of this standard is that an rNFT should reference content already accepted by the community (a time-based sequence known by participants). Global timestamps for rNFTs are thus essential, serving to prevent conflicting states (akin to concurrency issues in transaction processing and block organization). We define a block-level timestamp where `createdTimestamp = block.timestamp` Note that, given that the granularity of references is tied to the block timestamp, it is impractical to discern the order of two rNFTs within the same block. @@ -149,14 +148,14 @@ Test cases are included in [ERC_5521.test.js](../assets/eip-5521/ERC_5521.test.j ## Reference Implementation -The **RECOMMENDED** implementation is demonstrated as follows: +The recommended implementation is demonstrated as follows: -- `Relationship`: a structure that contains `referring`, `referred`, `referringKeys`, `referredKeys`, `createdTimestamp`, and other customized and **OPTIONAL** attributes (i.e., not necessarily included in the standard) such as `privityOfAgreement` recording the ownerships of referred NFTs at the time the Referable NFTs (rNFTs) were being created or `profitSharing` recording the profit sharing of `referring`. +- `Relationship`: a structure that contains `referring`, `referred`, `referringKeys`, `referredKeys`, `createdTimestamp`, and other customized and optional attributes (i.e., not necessarily included in the standard) such as `privityOfAgreement` recording the ownerships of referred NFTs at the time the Referable NFTs (rNFTs) were being created or `profitSharing` recording the profit sharing of `referring`. - `referring`: an out-degree indicator, used to show the users this NFT refers to; - `referred`: an in-degree indicator, used to show the users who have refereed this NFT; - `referringKeys`: a helper for mapping conversion of out-degree indicators, used for events; - `referredKeys`: a helper for mapping conversion of in-degree indicators, used for events; -- `createdTimestamp`: a time-based indicator, used to compare the timestamp of mint, which **MUST NOT** be editable anyhow by callers. +- `createdTimestamp`: a time-based indicator, used to compare the timestamp of mint, which should not be editable anyhow by callers. - `referringOf` and `referredOf`: First, the current `referringOf` and `referredOf` allow cross-contract looking up, while this cannot be done by directly accessing `_relationship`. Secondly, only if privacy is not a concern, making `_relationship` public simplifies the contract by relying on Solidityโ€™s automatically generated getters. However, if you need to control the visibility of the data, keeping the state variable private and providing specific getter functions would be the best approach. For example, if `_relationship` includes details about specific usersโ€™ interactions or transactions or some private extensible parameters (in the updated version, we specifically highlight the `Relationship` can be extended to meet different requirements), always making this data public could reveal usersโ€™ behavior patterns or preferences, leading to potential privacy breaches. - `convertMap`: This function is essential for retrieving the full mapping contents within a struct. Even if `_relationship` is public, The getters only allow retrieval of individual values for specific keys. Since we need comprehensive access to all stored addresses, `convertMap` is necessary to fulfill our event emission requirements. From 6b99dc3ea1888cdbe04f063b7c254945a5bb7eb4 Mon Sep 17 00:00:00 2001 From: Liaoyuan Date: Tue, 11 Jun 2024 22:14:04 +0800 Subject: [PATCH 017/126] Add ERC: Deferred Token Transfer Merged by EIP-Bot. --- ERCS/erc-7720.md | 256 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 ERCS/erc-7720.md diff --git a/ERCS/erc-7720.md b/ERCS/erc-7720.md new file mode 100644 index 0000000000..d2e7ba66cb --- /dev/null +++ b/ERCS/erc-7720.md @@ -0,0 +1,256 @@ +--- +eip: 7720 +title: Deferred Token Transfer +description: Allows users to schedule ERC-20 token transfers for withdrawal at a specified future time, enabling deferred payments. +author: Chen Liaoyuan (@chenly) +discussions-to: https://ethereum-magicians.org/t/erc-7720-deferred-token-transfer/20245 +status: Draft +type: Standards Track +category: ERC +created: 2024-06-09 +requires: 20 +--- + +## Abstract + +This standard specifies that allows users to deposit [ERC-20](./eip-20.md) tokens for a beneficiary. The beneficiary can withdraw the tokens only after a specified future timestamp. Each deposit transaction is assigned a unique ID and includes details such as the token address, sender, recipient, amount, unlock time, and withdrawal status. + +### Motivation + +In various scenarios, such as vesting schedules, escrow services, or timed rewards, there is a need for deferred payments. This contract provides a secure and reliable mechanism for time-locked token transfers, ensuring that tokens can only be transferred after a specified timestamp is reached. By facilitating structured and delayed payments, it adds an extra layer of security and predictability to token transfers. This is particularly useful for scenarios where payments are contingent upon the passage of time. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +Implementers of this standard **MUST** have all of the following functions: + +```solidity +pragma solidity ^0.8.0; + +interface ITokenTransfer { + // Event emitted when a transfer is initiated. + event Transfer( + uint256 txnId, + address indexed token, + address indexed from, + address indexed to, + uint256 amount, + uint256 unlockTime, + bytes32 referenceNo + ); + + // Event emitted when tokens are withdrawn. + event Withdraw( + uint256 txnId, + address indexed token, + address indexed from, + address indexed to, + uint256 amount + ); + + // Function to initiate a token transfer. + // Parameters: + // - _token: Address of the ERC20 token contract. + // - _from: Address of the sender. + // - _to: Address of the recipient. + // - _amount: Amount of tokens to be transferred. + // - _unlockTime: Time after which the tokens can be withdrawn. + // - _reference: Reference ID for the transaction. + // Returns the transaction ID. + function transferFrom( + address _token, + address _from, + address _to, + uint256 _amount, + uint256 _unlockTime, + bytes32 _reference + ) external returns (uint256 txnId); + + // Function to withdraw tokens from a transaction. + // Parameters: + // - _txnId: ID of the transaction to withdraw from. + function withdraw(uint256 _txnId) external; + + // Function to get transaction details. + // Parameters: + // - _txnId: ID of the transaction. + // Returns the transaction details. + function getTransaction(uint256 _txnId) + external + view + returns ( + address token, + address from, + address to, + uint256 amount, + uint256 unlockTime, + bytes32 referenceNo, + bool withdrawn + ); +} + +``` + +## Rationale + +The design of the Deferred Token Transfer contract aims to provide a straightforward and secure method for handling time-locked token transfers. The following considerations were made during its development: + +**Simplicity and Usability**: The contract interface is designed to be simple and intuitive, making it easy for users to create deposits and for beneficiaries to withdraw tokens once the conditions are met. + +**Security**: The contract ensures secure token transfers, preventing common vulnerabilities associated with ERC-20 transfers. Additionally, the contract includes checks to prevent multiple withdrawals of the same deposit. + +**Flexibility**: The contract supports various ERC-20 tokens, allowing users to create deposits with any standard ERC-20 token. This flexibility makes it suitable for a wide range of use cases. + +**Event Logging**: Events are emitted for both deposit creation and token withdrawal. This provides transparency and allows easy tracking of contract activities, which is crucial for auditability and user confidence. + +**Conditional Payments**: By implementing a time-lock mechanism, the contract ensures that tokens are only transferred after a specific timestamp. This feature is essential for use cases like vesting schedules, escrow arrangements, and timed rewards, where payments need to be delayed until certain conditions are met. + +## Reference Implementation + +```solidity +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +contract TokenTransfer { + using SafeERC20 for IERC20; + + struct Transaction { + address token; // Address of the ERC20 token contract. + address from; // Address of the sender. + address to; // Address of the recipient. + uint256 amount; // Amount of tokens to be transferred. + uint256 unlockTime; // Time after which the tokens can be withdrawn. + bytes32 referenceNo; // Reference ID for the transaction. + bool withdrawn; // Flag indicating if the tokens have been withdrawn. + } + + // Mapping from transaction ID to Transaction structure. + mapping(uint256 => Transaction) public transactions; + + // Variable to keep track of the next transaction ID. + uint256 public lastTxnId = 0; + + // Event emitted when a transfer is initiated. + event Transfer( + uint256 txnId, + address indexed token, + address indexed from, + address indexed to, + uint256 amount, + uint256 unlockTime, + bytes32 referenceNo + ); + + // Event emitted when tokens are withdrawn. + event Withdraw( + uint256 txnId, + address indexed token, + address indexed from, + address indexed to, + uint256 amount + ); + + constructor() {} + + // Function to initiate a token transfer. + // Parameters: + // - _token: Address of the ERC20 token contract. + // - _from: Address of the sender. + // - _to: Address of the recipient. + // - _amount: Amount of tokens to be transferred. + // - _unlockTime: Time after which the tokens can be withdrawn. + // - _reference: Reference ID for the transaction. + // Returns the transaction ID. + function transferFrom( + address _token, + address _from, + address _to, + uint256 _amount, + uint256 _unlockTime, + bytes32 _reference + ) external returns (uint256 txnId) { + require(_amount > 0, "Invalid transfer amount"); + + // Transfer tokens from sender to this contract. + IERC20(_token).safeTransferFrom(_from, address(this), _amount); + + lastTxnId++; + + // Store the transaction details. + transactions[lastTxnId] = Transaction({ + token: _token, + from: _from, + to: _to, + amount: _amount, + unlockTime: _unlockTime, + referenceNo: _reference, + withdrawn: false + }); + + // Emit an event for the transaction creation. + emit Transfer(lastTxnId, _token, _from, _to, _amount, _unlockTime, _reference); + return lastTxnId; + } + + // Function to withdraw tokens from a transaction. + // Parameters: + // - _txnId: ID of the transaction to withdraw from. + function withdraw(uint256 _txnId) external { + Transaction storage transaction = transactions[_txnId]; + require(transaction.amount > 0, "Invalid transaction ID"); + require(block.timestamp >= transaction.unlockTime, "Current time is before unlock time"); + // require(transaction.to == msg.sender, "Only the recipient can withdraw the tokens"); + require(!transaction.withdrawn, "Tokens already withdrawn"); + + IERC20(transaction.token).safeTransfer(transaction.to, transaction.amount); + + transaction.withdrawn = true; + + // Emit an event for the token withdrawal. + emit Withdraw(_txnId, transaction.token, transaction.from, transaction.to, transaction.amount); + } + + // Function to get transaction details. + // Parameters: + // - _txnId: ID of the transaction. + // Returns the transaction details. + function getTransaction(uint256 _txnId) + external + view + returns ( + address token, + address from, + address to, + uint256 amount, + uint256 unlockTime, + bytes32 referenceNo, + bool withdrawn + ) + { + Transaction storage transaction = transactions[_txnId]; + require(transaction.amount > 0, "Invalid transaction ID"); + + return ( + transaction.token, + transaction.from, + transaction.to, + transaction.amount, + transaction.unlockTime, + transaction.referenceNo, + transaction.withdrawn + ); + } +} +``` + +## Security Considerations + +**Ownerless Contract Design**: To prevent the risk of token loss after deposit, the contract should not have an owner. This ensures that the contract's token balance cannot be transferred to any address other than the designated beneficiary. + +**Strict Beneficiary Control**: During withdrawal, the contract must strictly ensure that tokens are transferred only to the beneficiary specified at the time of deposit. This prevents unauthorized access and ensures that only the intended recipient can withdraw the tokens. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file From ccaf79a17707eb756496ce00f0feffb24ac631bc Mon Sep 17 00:00:00 2001 From: rickey <12867336+HelloRickey@users.noreply.github.com> Date: Tue, 11 Jun 2024 22:28:50 +0800 Subject: [PATCH 018/126] Add ERC: Request Method Types Merged by EIP-Bot. --- ERCS/erc-7654.md | 148 ++++++++++++++++++ assets/erc-7654/RequestMethodTypes.sol | 95 ++++++++++++ assets/erc-7654/Types.sol | 205 +++++++++++++++++++++++++ 3 files changed, 448 insertions(+) create mode 100644 ERCS/erc-7654.md create mode 100644 assets/erc-7654/RequestMethodTypes.sol create mode 100644 assets/erc-7654/Types.sol diff --git a/ERCS/erc-7654.md b/ERCS/erc-7654.md new file mode 100644 index 0000000000..edfd53de14 --- /dev/null +++ b/ERCS/erc-7654.md @@ -0,0 +1,148 @@ +--- +eip: 7654 +title: Request Method Types +description: Use a set of request methods to indicate the type of action to take on the contract. +author: Rickey (@HelloRickey) +discussions-to: https://ethereum-magicians.org/t/erc-7654-request-method-types/19183 +status: Draft +type: Standards Track +category: ERC +created: 2024-03-13 +--- + +## Abstract + +This proposal standardizes a set of request and response communication standards between clients and smart contracts, using POST, GET, and PUT requests to create, read, and update the states of smart contracts. You can customize different request method names, request parameters and response values, and each request method will be mapped to a specific operation. + +## Motivation + +Since each contract has different functions, the client cannot use a standard to call different functions of different contracts. Contract Request Methods redefines the request method of the contract, so that different functions of multiple different contracts can be called using a consistent set of rules and protocols. + +By dividing the function types into POST, GET, and PUT, different operations can be performed on the contract. This clear operation type can not only help all parties limit the access and operation of contract data, but also effectively simplify the interaction between the client and the contract, making it easier for all parties to understand the functions and hierarchical structure of the contract. The request and response parameter data types of each function of this standard can express the expected operation of the contract and have the ability to describe its own structure, which is conducive to the parties and contracts to create a unified and predictable way of exchanging data. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +It consists of four request method types: + +**GET**: Request the contract to retrieve records. + +**POST**: Request the contract to create a new record. + +**PUT**: Request the contract to update a record. + +**OPTIONS**: Supported request method types. + +Workflow: + +1. Call ```options``` to obtain supported request method types. +2. Call ```getMethods``` to obtain the request method name. +3. Call ```getMethodReqAndRes``` to obtain the request parameter data type and response value data type. +4. Encode request parameters and call ```get```, ```post```, and ```put```. +5. Decode response value. + +### Interfaces + +#### `IRequestMethodTypes.sol` + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity >=0.8.0; +import "./Types.sol"; +interface IRequestMethodTypes{ + + /** + * Requested method type. + * GET, POST, PUT, OPTIONS + */ + enum MethodTypes{ + GET, + POST, + PUT, + OPTIONS + } + + /** + * Response data event. + * @param _response is the response value of the post request or put request. + */ + event Response(bytes _response); + + /** + * Get method names based on request method type. + * @param _methodTypes is the request method type. + * @return Method names. + */ + function getMethods(MethodTypes _methodTypes)external view returns (string[] memory); + + /** + * Get the data types of request parameters and responses based on the requested method name. + * @param _methodName is the method name. + * @return Data types of request parameters and responses. + */ + function getMethodReqAndRes(string memory _methodName) external view returns(Types.Type[] memory ,Types.Type[] memory ); + + /** + * Request the contract to retrieve records. + * @param _methodName is the method name. + * @param _methodReq is the method type. + * @return The response to the get request. + */ + function get(string memory _methodName,bytes memory _methodReq)external view returns(bytes memory); + + /** + * Request the contract to create a new record. + * @param _methodName is the method name. + * @param _methodReq is the method type. + * @return The response to the post request. + */ + function post(string memory _methodName,bytes memory _methodReq)external returns(bytes memory); + + /** + * Request the contract to update a record. + * @param _methodName is the method name. + * @param _methodReq is the method type. + * @return The response to the put request. + */ + function put(string memory _methodName,bytes memory _methodReq)external returns(bytes memory); + + /** + * Supported request method types. + * @return Method types. + */ + function options()external returns(MethodTypes[] memory); +} + +``` + +### Library + +The library [`Types.sol`](../assets/eip-7654/Types.sol) contains an enumeration of Solidity types used in the above interfaces. + +## Rationale + +### Type of request method + +In order to enable the client to operate the contract in a standardized and predictable way, three request method types ```GET```, ```POST```, and ```PUT``` are set. The functions of each need to be defined in these three types to facilitate the contract caller to understand and process the information required for the request. However, there is no ```DELETE``` operation type because deleting data in the contract is an inefficient operation. Developers can add a ```PUT``` request method by themselves to set the data to be valid and invalid, and only return valid data in the ```GET``` method. + +### Request method parameter type + +Some functions are defined in each request method type. They all include request parameter data type and response parameter data type, which need to be set in the ```constructor``` and then obtained according to the method name through ```getMethodReqAndRes```. The data type of the parameter is defined by the enumeration of the data type. When processing the request parameter, ```abi.decode``` is used to decode according to the request parameter type and the request value. When returning the response, ```abi.encode``` is used to encode according to the response value and the response parameter type. + + +## Reference Implementation + +See [Request Method Types Example](../assets/eip-7654/RequestMethodTypes.sol) + +## Security Considerations + +Contract request methods are divided into safe methods and unsafe methods. If the method request is a read-only operation and will not change the state of the contract, then the method is safe. + +**Safe Methods:** GET, OPTIONS +**Unsafe Methods:** POST, PUT + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/assets/erc-7654/RequestMethodTypes.sol b/assets/erc-7654/RequestMethodTypes.sol new file mode 100644 index 0000000000..b3c3221402 --- /dev/null +++ b/assets/erc-7654/RequestMethodTypes.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity >=0.8.0; +import "./Types.sol"; +import "./IRequestMethodTypes.sol"; +contract RequestMethodTypes is IRequestMethodTypes{ + + //@dev define the data type of this component + struct Profiles{ + string name; + uint256 age; + } + + mapping (address=>Profiles) users; + + //@dev Types contains all data types in solidity + mapping (string=>Types.Type[]) methodRequests; + mapping (string=>Types.Type[]) methodResponses; + mapping (MethodTypes=>string[]) methods; + + constructor(){ + Types.Type[] memory getReqArray = new Types.Type[](1); + getReqArray[0] = Types.Type.ADDRESS; + Types.Type[] memory dataTypeArray = new Types.Type[](2); + dataTypeArray[0] = Types.Type.STRING; + dataTypeArray[1] = Types.Type.UINT256; + Types.Type[] memory putReqArray = new Types.Type[](2); + putReqArray[0] = Types.Type.ADDRESS; + putReqArray[1] = Types.Type.STRING; + // @dev initialize get, post, put request parameter data types and response data types + setMethod("getUser",MethodTypes.GET,getReqArray,dataTypeArray); + setMethod("createUser",MethodTypes.POST,dataTypeArray,new Types.Type[](0)); + setMethod("updateUserName",MethodTypes.PUT,putReqArray,new Types.Type[](0)); + } + + function setMethod(string memory _methodName,MethodTypes _methodType,Types.Type[] memory _methodReq,Types.Type[] memory _methodRes) private { + methods[_methodType].push(_methodName); + methodRequests[_methodName]=_methodReq; + methodResponses[_methodName]=_methodRes; + } + + function getMethodReqAndRes(string memory _methodName)public view returns(Types.Type[] memory ,Types.Type[] memory ){ + return( + methodRequests[_methodName], + methodResponses[_methodName] + ); + } + + function getMethods(MethodTypes _methodTypes)public view returns (string[] memory){ + return methods[_methodTypes]; + } + + function get(string memory _methodName,bytes memory _methodReq)public view returns(bytes memory){ + if(compareStrings(_methodName,"getUser")){ + address user=abi.decode(_methodReq, (address)); + bytes memory userData=abi.encode(users[user].name,users[user].age); + return userData; + }else{ + return abi.encode(""); + } + } + + function post(string memory _methodName,bytes memory _methodReq)public returns(bytes memory){ + if(compareStrings(_methodName,"createUser")){ + (string memory name,uint256 age)=abi.decode(_methodReq, (string,uint256)); + users[msg.sender]=Profiles(name,age); + + } + return abi.encode(""); + } + + function put(string memory _methodName,bytes memory _methodReq)public returns(bytes memory){ + if(compareStrings(_methodName,"updateUserName")){ + (address userAddress,string memory name)=abi.decode(_methodReq, (address,string)); + require(userAddress==msg.sender); + users[userAddress].name=name; + } + return abi.encode(""); + } + + function options()public pure returns(MethodTypes[] memory){ + MethodTypes[] memory methodTypes=new MethodTypes[](4); + methodTypes[0]=MethodTypes.GET; + methodTypes[1]=MethodTypes.POST; + methodTypes[2]=MethodTypes.PUT; + methodTypes[3]=MethodTypes.OPTIONS; + return methodTypes; + } + + //@dev compares two strings for equality + function compareStrings(string memory _a, string memory _b) private pure returns (bool) { + return keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b)); + } + + +} \ No newline at end of file diff --git a/assets/erc-7654/Types.sol b/assets/erc-7654/Types.sol new file mode 100644 index 0000000000..3c7b339899 --- /dev/null +++ b/assets/erc-7654/Types.sol @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity >=0.8.0; +library Types { + enum Type { + BOOL, + INT8, + INT16, + INT24, + INT32, + INT40, + INT48, + INT56, + INT64, + INT72, + INT80, + INT88, + INT96, + INT104, + INT112, + INT120, + INT128, + INT136, + INT144, + INT152, + INT160, + INT168, + INT176, + INT184, + INT192, + INT200, + INT208, + INT216, + INT224, + INT232, + INT240, + INT248, + INT256, + UINT8, + UINT16, + UINT24, + UINT32, + UINT40, + UINT48, + UINT56, + UINT64, + UINT72, + UINT80, + UINT88, + UINT96, + UINT104, + UINT112, + UINT120, + UINT128, + UINT136, + UINT144, + UINT152, + UINT160, + UINT168, + UINT176, + UINT184, + UINT192, + UINT200, + UINT208, + UINT216, + UINT224, + UINT232, + UINT240, + UINT248, + UINT256, + ADDRESS, + BYTES1, + BYTES2, + BYTES3, + BYTES4, + BYTES5, + BYTES6, + BYTES7, + BYTES8, + BYTES9, + BYTES10, + BYTES11, + BYTES12, + BYTES13, + BYTES14, + BYTES15, + BYTES16, + BYTES17, + BYTES18, + BYTES19, + BYTES20, + BYTES21, + BYTES22, + BYTES23, + BYTES24, + BYTES25, + BYTES26, + BYTES27, + BYTES28, + BYTES29, + BYTES30, + BYTES31, + BYTES32, + BYTES, + STRING, + INT8_ARRAY, + INT16_ARRAY, + INT24_ARRAY, + INT32_ARRAY, + INT40_ARRAY, + INT48_ARRAY, + INT56_ARRAY, + INT64_ARRAY, + INT72_ARRAY, + INT80_ARRAY, + INT88_ARRAY, + INT96_ARRAY, + INT104_ARRAY, + INT112_ARRAY, + INT120_ARRAY, + INT128_ARRAY, + INT136_ARRAY, + INT144_ARRAY, + INT152_ARRAY, + INT160_ARRAY, + INT168_ARRAY, + INT176_ARRAY, + INT184_ARRAY, + INT192_ARRAY, + INT200_ARRAY, + INT208_ARRAY, + INT216_ARRAY, + INT224_ARRAY, + INT232_ARRAY, + INT240_ARRAY, + INT248_ARRAY, + INT256_ARRAY, + UINT8_ARRAY, + UINT16_ARRAY, + UINT24_ARRAY, + UINT32_ARRAY, + UINT40_ARRAY, + UINT48_ARRAY, + UINT56_ARRAY, + UINT64_ARRAY, + UINT72_ARRAY, + UINT80_ARRAY, + UINT88_ARRAY, + UINT96_ARRAY, + UINT104_ARRAY, + UINT112_ARRAY, + UINT120_ARRAY, + UINT128_ARRAY, + UINT136_ARRAY, + UINT144_ARRAY, + UINT152_ARRAY, + UINT160_ARRAY, + UINT168_ARRAY, + UINT176_ARRAY, + UINT184_ARRAY, + UINT192_ARRAY, + UINT200_ARRAY, + UINT208_ARRAY, + UINT216_ARRAY, + UINT224_ARRAY, + UINT232_ARRAY, + UINT240_ARRAY, + UINT248_ARRAY, + UINT256_ARRAY, + ADDRESS_ARRAY, + BYTES1_ARRAY, + BYTES2_ARRAY, + BYTES3_ARRAY, + BYTES4_ARRAY, + BYTES5_ARRAY, + BYTES6_ARRAY, + BYTES7_ARRAY, + BYTES8_ARRAY, + BYTES9_ARRAY, + BYTES10_ARRAY, + BYTES11_ARRAY, + BYTES12_ARRAY, + BYTES13_ARRAY, + BYTES14_ARRAY, + BYTES15_ARRAY, + BYTES16_ARRAY, + BYTES17_ARRAY, + BYTES18_ARRAY, + BYTES19_ARRAY, + BYTES20_ARRAY, + BYTES21_ARRAY, + BYTES22_ARRAY, + BYTES23_ARRAY, + BYTES24_ARRAY, + BYTES25_ARRAY, + BYTES26_ARRAY, + BYTES27_ARRAY, + BYTES28_ARRAY, + BYTES29_ARRAY, + BYTES30_ARRAY, + BYTES31_ARRAY, + BYTES32_ARRAY, + BYTES_ARRAY, + STRING_ARRAY + } +} \ No newline at end of file From c20af1d1fa89658e848eccd80c6d8c76fdb35741 Mon Sep 17 00:00:00 2001 From: Liaoyuan Date: Tue, 11 Jun 2024 23:46:08 +0800 Subject: [PATCH 019/126] Update ERC-7720: Rationale Merged by EIP-Bot. --- ERCS/erc-7720.md | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/ERCS/erc-7720.md b/ERCS/erc-7720.md index d2e7ba66cb..5eeee4e248 100644 --- a/ERCS/erc-7720.md +++ b/ERCS/erc-7720.md @@ -36,7 +36,7 @@ interface ITokenTransfer { address indexed from, address indexed to, uint256 amount, - uint256 unlockTime, + uint40 unlockTime, bytes32 referenceNo ); @@ -63,7 +63,7 @@ interface ITokenTransfer { address _from, address _to, uint256 _amount, - uint256 _unlockTime, + uint40 _unlockTime, bytes32 _reference ) external returns (uint256 txnId); @@ -84,7 +84,7 @@ interface ITokenTransfer { address from, address to, uint256 amount, - uint256 unlockTime, + uint40 unlockTime, bytes32 referenceNo, bool withdrawn ); @@ -96,15 +96,11 @@ interface ITokenTransfer { The design of the Deferred Token Transfer contract aims to provide a straightforward and secure method for handling time-locked token transfers. The following considerations were made during its development: -**Simplicity and Usability**: The contract interface is designed to be simple and intuitive, making it easy for users to create deposits and for beneficiaries to withdraw tokens once the conditions are met. +**Unlock Time Precision with `uint40`**: We chose a full `uint40` for `_unlockTime` because it provides a sufficiently large range to cover all practical time-lock scenarios. This ensures that the contract can handle deferred payments that require precise timing over long periods, such as vesting schedules or long-term escrows. -**Security**: The contract ensures secure token transfers, preventing common vulnerabilities associated with ERC-20 transfers. Additionally, the contract includes checks to prevent multiple withdrawals of the same deposit. +**Returning `txnId` from `transferFrom`**: The `transferFrom` function returns a unique `txnId` for each transaction. This design choice was made to facilitate easy and independent tracking of each transaction. By having a unique ID, users can manage and reference specific transactions, ensuring clarity and preventing confusion. This approach allows each transaction's state to be managed independently, simplifying the withdrawal process. -**Flexibility**: The contract supports various ERC-20 tokens, allowing users to create deposits with any standard ERC-20 token. This flexibility makes it suitable for a wide range of use cases. - -**Event Logging**: Events are emitted for both deposit creation and token withdrawal. This provides transparency and allows easy tracking of contract activities, which is crucial for auditability and user confidence. - -**Conditional Payments**: By implementing a time-lock mechanism, the contract ensures that tokens are only transferred after a specific timestamp. This feature is essential for use cases like vesting schedules, escrow arrangements, and timed rewards, where payments need to be delayed until certain conditions are met. +**Compatibility with Existing ERC-20 Tokens**: The standard is designed as a separate interface rather than an extension of ERC-20 to ensure flexibility and broad compatibility. By not modifying the ERC-20 standard directly, this proposal can be used with any existing ERC-20 token without requiring changes to their contracts. This flexibility makes the standard applicable to a wide range of tokens already in circulation, enhancing its utility and adoption potential. ## Reference Implementation @@ -121,7 +117,7 @@ contract TokenTransfer { address from; // Address of the sender. address to; // Address of the recipient. uint256 amount; // Amount of tokens to be transferred. - uint256 unlockTime; // Time after which the tokens can be withdrawn. + uint40 unlockTime; // Time after which the tokens can be withdrawn. bytes32 referenceNo; // Reference ID for the transaction. bool withdrawn; // Flag indicating if the tokens have been withdrawn. } @@ -139,7 +135,7 @@ contract TokenTransfer { address indexed from, address indexed to, uint256 amount, - uint256 unlockTime, + uint40 unlockTime, bytes32 referenceNo ); @@ -168,7 +164,7 @@ contract TokenTransfer { address _from, address _to, uint256 _amount, - uint256 _unlockTime, + uint40 _unlockTime, bytes32 _reference ) external returns (uint256 txnId) { require(_amount > 0, "Invalid transfer amount"); @@ -224,7 +220,7 @@ contract TokenTransfer { address from, address to, uint256 amount, - uint256 unlockTime, + uint40 unlockTime, bytes32 referenceNo, bool withdrawn ) From a8e630a39d3b4c104e4665f2935fe08a761b9364 Mon Sep 17 00:00:00 2001 From: Derek Date: Tue, 11 Jun 2024 12:23:33 -0400 Subject: [PATCH 020/126] Add ERC: UserOperation Builder Merged by EIP-Bot. --- ERCS/erc-7679.md | 223 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 ERCS/erc-7679.md diff --git a/ERCS/erc-7679.md b/ERCS/erc-7679.md new file mode 100644 index 0000000000..b3d12b8684 --- /dev/null +++ b/ERCS/erc-7679.md @@ -0,0 +1,223 @@ +--- +eip: 7679 +title: UserOperation Builder +description: Construct UserOperations without being coupled with account-specific logic. +author: Derek Chiang (@derekchiang), Garvit Khatri (@plusminushalf), Fil Makarov (@filmakarov), Kristof Gazso (@kristofgazso), Derek Rein (@arein), Tomas Rocchi (@tomiir), bumblefudge (@bumblefudge) +discussions-to: https://ethereum-magicians.org/t/erc-7679-smart-account-interfaces/19547 +status: Draft +type: Standards Track +category: ERC +created: 2024-04-05 +requires: 4337 +--- + +## Abstract + +Different [ERC-4337](./eip-4337.md) smart account implementations encode their signature, nonce, and calldata differently. This makes it difficult for DApps, wallets, and smart account toolings to integrate with smart accounts without integrating with account-specific SDKs, which introduces vendor lock-in and hurts smart account adoption. + +We propose a standard way for smart account implementations to put their account-specific encoding logic on-chain. It can be achieved by implementing methods that accept the raw signature, nonce, or calldata (along with the context) as an input, and output them properly formatted, so the smart account can consume them while validating and executing the User Operation. + + +## Motivation + +At the moment, to build a [ERC-4337](./eip-4337.md) UserOperation (UserOp for short) for a smart account requires detailed knowledge of how the smart account implementation works, since each implementation is free to encode its nonce, calldata, and signature differently. + +As a simple example, one account might use an execution function called `executeFoo`, whereas another account might use an execution function called `executeBar`. This will result in the `calldata` being different between the two accounts, even if they are executing the same call. + +Therefore, someone who wants to send a UserOp for a given smart account needs to: + +* Figure out which smart account implementation the account is using. +* Correctly encode signature/nonce/calldata given the smart account implementation, or use an account-specific SDK that knows how to do that. + +In practice, this means that most DApps, wallets, and AA toolings today are tied to a specific smart account implementation, resulting in fragmentation and vendor lock-in. + +## Specification + +The key words โ€œMUSTโ€, โ€œMUST NOTโ€, โ€œREQUIREDโ€, โ€œSHALLโ€, โ€œSHALL NOTโ€, โ€œSHOULDโ€, โ€œSHOULD NOTโ€, โ€œRECOMMENDEDโ€, โ€œMAYโ€, and โ€œOPTIONALโ€ in this document are to be interpreted as described in RFC 2119. + +### UserOp builder + +To conform to this standard, a smart account implementation MUST provide a โ€œUserOp builderโ€ contract that implements the `IUserOperationBuilder` interface, as defined below: + + +```solidity +struct Execution { + address target; + uint256 value; + bytes callData; +} + +interface IUserOperationBuilder { + /** + * @dev Returns the ERC-4337 EntryPoint that the account implementation + * supports. + */ + function entryPoint() external view returns (address); + + /** + * @dev Returns the nonce to use for the UserOp, given the context. + * @param smartAccount is the address of the UserOp sender. + * @param context is the data required for the UserOp builder to + * properly compute the requested field for the UserOp. + */ + function getNonce( + address smartAccount, + bytes calldata context + ) external view returns (uint256); + + /** + * @dev Returns the calldata for the UserOp, given the context and + * the executions. + * @param smartAccount is the address of the UserOp sender. + * @param executions are (destination, value, callData) tuples that + * the UserOp wants to execute. It's an array so the UserOp can + * batch executions. + * @param context is the data required for the UserOp builder to + * properly compute the requested field for the UserOp. + */ + function getCallData( + address smartAccount, + Execution[] calldata executions, + bytes calldata context + ) external view returns (bytes memory); + + /** + * @dev Returns a correctly encoded signature, given a UserOp that + * has been correctly filled out except for the signature field. + * @param smartAccount is the address of the UserOp sender. + * @param userOperation is the UserOp. Every field of the UserOp should + * be valid except for the signature field. The "PackedUserOperation" + * struct is as defined in ERC-4337. + * @param context is the data required for the UserOp builder to + * properly compute the requested field for the UserOp. + */ + function formatSignature( + address smartAccount, + PackedUserOperation calldata userOperation, + bytes calldata context + ) external view returns (bytes memory signature); +} +``` + +### Using the UserOp builder + +To build a UserOp using the UserOp builder, the building party SHOULD proceed as follows: + +1. Obtain the address of `UserOpBuilder` and a `context` from the account owner. The `context` is an opaque bytes array from the perspective of the building party. The `UserOpBuilder` implementation may need the `context` in order to properly figure out the UserOp fields. See [Rationale](#rationale) for more info. +2. Execute a multicall (batched `eth_call`s) of `getNonce` and `getCallData` with the `context` and executions. The building party will now have obtained the nonce and calldata. +3. Fill out a UserOp with the data obtained previously. Gas values can be set randomly or very low. This userOp will be used to obtain a dummy signature for gas estimations. Sign the hash of userOp. (See [Rationale](#rationale) for what a dummy signature is. See [Security Considerations](#security-considerations) for the details on dummy signature security). +4. Call (via `eth_call`) `formatSignature` with the UserOp and `context` to obtain a UserOp with a properly formatted dummy signature. This userOp can now be used for gas estimation. +5. In the UserOp, change the existing gas values to those obtained from a proper gas estimation. This UserOp must be valid except for the `signature` field. Sign the hash of the UserOp and place the signature in the UserOp.signature field. +6. Call (via `eth_call`) `formatSignature` with the UserOp and `context` to obtain a completely valid UserOp. + 1. Note that a UserOp has a lot more fields than `nonce`, `callData`, and `signature`, but how the building party obtains the other fields is outside of the scope of this document, since only these three fields are heavily dependent on the smart account implementation. + +At this point, the building party has a completely valid UserOp that they can then submit to a bundler or do whatever it likes with it. + +### Using the UserOp builder when the account hasnโ€™t been deployed + +To provide the accurate data to the building party, the `UserOpBuilder` will in most cases have to call the account. +If the account has yet to be deployed, which means that the building party is looking to send the very first UserOp for this account, then the building party MAY modify the flow above as follows: + +- In addition to the `UserOpBuilder` address and the `context`, the building party also obtains the `factory` and `factoryData` as defined in ERC-4337. +- When calling one of the view functions on the UserOp builder, the building party may use `eth_call` to deploy the `CounterfactualCall` contract, which is going to deploy the account and call `UserOpBuilder` (see below). +- When filling out the UserOp, the building party includes `factory` and `factoryData`. + +The `CounterfactualCall` contract SHOULD: +- Deploy the account using `factory` and `factoryData` provided by the building party. +- Revert if the deployment has not succeeded. +- If the account has been deployed succesfully, call `UserOpBuilder` and return the data returned by `UserOpBuilder` to the building party. + +See Reference Implementation section for more details on the `CounterfactualCall` contract. + +## Rationale + +### Context + +The `context` is an array of bytes that encodes whatever data the UserOp builder needs in order to correctly determine the nonce, calldata, and signature. Presumably, the `context` is constructed by the account owner, with the help of a wallet software. + +Here we outline one possible use of `context`: delegation. Say the account owner wants to delegate a transaction to be executed by the building party. The account owner could encode a signature of the public key of the building party inside the `context`. Letโ€™s call this signature from the account owner the `authorization`. + +Then, when the building party fills out the UserOp, it would fill the `signature` field with a signature generated by its own private key. When it calls `getSignature` on the UserOp builder, the UserOp builder would extract the `authorization` from the `context` and concatenates it with the building partyโ€™s signature. The smart account would presumably be implemented such that it would recover the building partyโ€™s public key from the signature, and check that the public key was in fact signed off by the `authorization`. If the check succeeds, the smart account would execute the UserOp, thus allowing the building party to execute a UserOp on the userโ€™s behalf. + +### Dummy signature + +The โ€œdummy signatureโ€ refers to the signature used in a UserOp sent to a bundler for estimating gas (via `eth_estimateUserOperationGas`). A dummy signature is needed because, at the time the bundler estimates gas, a valid signature does not exist yet, since the valid signature itself depends on the gas values of the UserOp, creating a circular dependency. To break the circular dependency, a dummy signature is used. + +However, the dummy signature is not just a fixed value that any smart account can use. The dummy signature must be constructed such that it would cause the UserOp to use about as much gas as a real signature would. Therefore, the dummy signature varies based on the specific validation logic that the smart account uses to validate the UserOp, making it dependent on the smart account implementation. + +## Backwards Compatibility + +This ERC is intended to be backwards compatible with all ERC-4337 smart accounts as of EntryPoint 0.7. + +For smart accounts deployed against EntryPoint 0.6, the `IUserOperationBuilder` interface needs to be modified such that the `PackedUserOperation` struct is replaced with the corresponding struct in EntryPoint 0.6. + +## Reference Implementation + +### Counterfactual call contract + +The counterfactual call contract is inspired by [ERC-6492](./eip-6492.md), which devised a mechanism to execute `isValidSignature` (see [ERC-1271](./eip-1271.md)) against a pre-deployed (counterfactual) contract. + +```solidity +contract CounterfactualCall { + + error CounterfactualDeployFailed(bytes error); + + constructor( + address smartAccount, + address create2Factory, + bytes memory factoryData, + address userOpBuilder, + bytes memory userOpBuilderCalldata + ) { + if (address(smartAccount).code.length == 0) { + (bool success, bytes memory ret) = create2Factory.call(factoryData); + if (!success || address(smartAccount).code.length == 0) revert CounterfactualDeployFailed(ret); + } + + assembly { + let success := call(gas(), userOpBuilder, 0, add(userOpBuilderCalldata, 0x20), mload(userOpBuilderCalldata), 0, 0) + let ptr := mload(0x40) + returndatacopy(ptr, 0, returndatasize()) + if iszero(success) { + revert(ptr, returndatasize()) + } + return(ptr, returndatasize()) + } + } + +} +``` + +Hereโ€™s an example of calling this contract using the ethers and viem libraries: + +```javascript +// ethers +const nonce = await provider.call({ + data: ethers.utils.concat([ + counterfactualCallBytecode, + ( + new ethers.utils.AbiCoder()).encode(['address','address', 'bytes', 'address','bytes'], + [smartAccount, userOpBuilder, getNonceCallData, factory, factoryData] + ) + ]) +}) + +// viem +const nonce = await client.call({ + data: encodeDeployData({ + abi: parseAbi(['constructor(address, address, bytes, address, bytes)']), + args: [smartAccount, userOpBuilder, getNonceCalldata, factory, factoryData], + bytecode: counterfactualCallBytecode, + }) +}) +``` + +## Security Considerations + +### Dummy Signature security + +Since the properly formatted dummy signature is going to be publicly disclosed, in theory it can be intercepted and used by the man in the middle. Risks and potential harm of this is very low though as the dummy signature will be effectively unusable after the final UserOp is submitted (as both UserOps use the same nonce). However, to mitigate even this small issue, it is recommended that the UserOp which hash is going to be signed to obtain an un-foirmatted dummy signature (step 3 above) is filled with very low gas values. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 7e4222187c557efb31d728ded3ac4716342164fc Mon Sep 17 00:00:00 2001 From: Joshua Tan Date: Tue, 11 Jun 2024 13:34:06 -0500 Subject: [PATCH 021/126] Update ERC-4824: Move to Review Merged by EIP-Bot. --- ERCS/erc-4824.md | 316 +++++------------- assets/erc-4824/contracts/ERC4824Factory.sol | 84 +++++ .../contracts/ERC4824Registration.sol | 72 ++++ 3 files changed, 244 insertions(+), 228 deletions(-) create mode 100644 assets/erc-4824/contracts/ERC4824Factory.sol create mode 100644 assets/erc-4824/contracts/ERC4824Registration.sol diff --git a/ERCS/erc-4824.md b/ERCS/erc-4824.md index c9dd0459c4..d2021546fc 100644 --- a/ERCS/erc-4824.md +++ b/ERCS/erc-4824.md @@ -4,7 +4,7 @@ title: Common Interfaces for DAOs description: An API for decentralized autonomous organizations (DAOs). author: Joshua Tan (@thelastjosh), Isaac Patka (@ipatka), Ido Gershtein , Eyal Eithcowich , Michael Zargham (@mzargham), Sam Furter (@nivida) discussions-to: https://ethereum-magicians.org/t/eip-4824-decentralized-autonomous-organizations/8362 -status: Draft +status: Review type: Standards Track category: ERC created: 2022-02-17 @@ -22,17 +22,17 @@ DAOs, since being invoked in the Ethereum whitepaper, have been vaguely defined. The key words โ€œMUSTโ€, โ€œMUST NOTโ€, โ€œREQUIREDโ€, โ€œSHALLโ€, โ€œSHALL NOTโ€, โ€œSHOULDโ€, โ€œSHOULD NOTโ€, โ€œRECOMMENDEDโ€, โ€œMAYโ€, and โ€œOPTIONALโ€ in this document are to be interpreted as described in RFC 2119. -Every contract implementing this EIP MUST implement the [ERC-4824](./eip-4824) interface below: +Every contract implementing this EIP MUST implement the `IERC4824` interface below: ```solidity pragma solidity ^0.8.1; /// @title ERC-4824 DAOs /// @dev See -interface IERC-4824 { +interface IERC4824 { event DAOURIUpdate(address daoAddress, string daoURI); - /// @notice A distinct Uniform Resource Identifier (URI) pointing to a JSON object following the "ERC-4824 DAO JSON-LD Schema". This JSON file splits into four URIs: membersURI, proposalsURI, activityLogURI, and governanceURI. The membersURI should point to a JSON file that conforms to the "ERC-4824 Members JSON-LD Schema". The proposalsURI should point to a JSON file that conforms to the "ERC-4824 Proposals JSON-LD Schema". The activityLogURI should point to a JSON file that conforms to the "ERC-4824 Activity Log JSON-LD Schema". The governanceURI should point to a flatfile, normatively a .md file. Each of the JSON files named above can be statically-hosted or dynamically-generated. + /// @notice A distinct Uniform Resource Identifier (URI) pointing to a JSON object following the "ERC-4824 DAO JSON-LD Schema". This JSON file splits into four subsidiary URIs: membersURI, proposalsURI, activityLogURI, and governanceURI. The membersURI SHOULD point to a JSON file that conforms to the "ERC-4824 Members JSON-LD Schema". The proposalsURI SHOULD point to a JSON file that conforms to the "ERC-4824 Proposals JSON-LD Schema". The activityLogURI SHOULD point to a JSON file that conforms to the "ERC-4824 Activity Log JSON-LD Schema". The governanceURI SHOULD point to a flatfile, normatively a .md file. Each of the JSON files named above MAY be statically-hosted or dynamically-generated. The content of subsidiary JSON files MAY be directly embedded as a JSON object directly within the top-level DAO JSON, in which case the relevant field MUST be renamed to remove the "URI" suffix. For example, "membersURI" would be renamed to "members", "proposalsURI" would be renamed to "proposals", and so on. function daoURI() external view returns (string memory _daoURI); } ``` @@ -53,97 +53,20 @@ The DAO JSON-LD Schema mentioned above: } ``` -A DAO MAY inherit the above interface above or it MAY create an external registration contract that is compliant with this EIP. If a DAO creates an external registration contract, the registration contract MUST store the DAOโ€™s primary address. +A DAO MAY inherit the `IERC4824` interface above or it MAY create an external registration contract that is compliant with this EIP. Whether the DAO inherits the above interface or it uses an external registration contract, the DAO SHOULD define a method for and implement some access control logic to enable efficient updating for daoURI. If a DAO creates an external registration contract, the registration contract MUST store the DAOโ€™s primary address, typically the address of the primary governance contract. See the reference implementation of external registration contract in the attached assets folder to this EIP. -If the DAO inherits the above interface, it SHOULD define a method for updating daoURI. If the DAO uses an external registration contract, the registration contract SHOULD contain some access control logic to enable efficient updating for daoURI. - -```solidity -pragma solidity ^0.8.1; - -/// @title ERC-4824 Common Interfaces for DAOs -/// @dev See -/// @title ERC-4824: DAO Registration -contract ERC-4824Registration is IERC-4824, AccessControl { - bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); - - string private _daoURI; - - address daoAddress; - - constructor() { - daoAddress = address(0xdead); - } - - /// @notice Set the initial DAO URI and offer manager role to an address - /// @dev Throws if initialized already - /// @param _daoAddress The primary address for a DAO - /// @param _manager The address of the URI manager - /// @param daoURI_ The URI which will resolve to the governance docs - function initialize( - address _daoAddress, - address _manager, - string memory daoURI_, - address _ERC-4824Index - ) external { - initialize(_daoAddress, daoURI_, _ERC-4824Index); - _grantRole(MANAGER_ROLE, _manager); - } - - /// @notice Set the initial DAO URI - /// @dev Throws if initialized already - /// @param _daoAddress The primary address for a DAO - /// @param daoURI_ The URI which will resolve to the governance docs - function initialize( - address _daoAddress, - string memory daoURI_, - address _ERC-4824Index - ) public { - if (daoAddress != address(0)) revert AlreadyInitialized(); - daoAddress = _daoAddress; - _setURI(daoURI_); - - _grantRole(DEFAULT_ADMIN_ROLE, _daoAddress); - _grantRole(MANAGER_ROLE, _daoAddress); - - ERC-4824Index(_ERC-4824Index).logRegistration(address(this)); - } - - /// @notice Update the URI for a DAO - /// @dev Throws if not called by dao or manager - /// @param daoURI_ The URI which will resolve to the governance docs - function setURI(string memory daoURI_) public onlyRole(MANAGER_ROLE) { - _setURI(daoURI_); - } - - function _setURI(string memory daoURI_) internal { - _daoURI = daoURI_; - emit DAOURIUpdate(daoAddress, daoURI_); - } - - function daoURI() external view returns (string memory daoURI_) { - return _daoURI; - } - - function supportsInterface( - bytes4 interfaceId - ) public view virtual override returns (bool) { - return - interfaceId == type(IERC-4824).interfaceId || - super.supportsInterface(interfaceId); - } -} -``` +When reporting information in the DAO JSON-LD Schema, if a given field has no value (for example, `description`), it SHOULD be removed rather than left with an empty or `null` value. ### Indexing -If a DAO inherits the ERC-4824 interface from a 4824-compliant DAO factory, then the DAO factory SHOULD incorporate a call to an indexer contract as part of the DAO's initialization to enable efficient network indexing. If the DAO is [ERC-165](./eip-165)-compliant, the factory can do this without additional permissions. If the DAO is _not_ compliant with ERC-165, the factory SHOULD first obtain access control rights to the indexer contract and then call logRegistration directly with the address of the new DAO and the daoURI of the new DAO. Note that any user, including the DAO itself, MAY call logRegistration and submit a registration for a DAO which inherits the ERC-4824 interface and which is also ERC-165-compliant. +If a DAO inherits the `IERC4824` interface from a 4824-compliant DAO factory, then the DAO factory SHOULD incorporate a call to an indexer contract as part of the DAO's initialization to enable efficient network indexing. If the DAO is [ERC-165](./eip-165)-compliant, the factory can do this without additional permissions. If the DAO is _not_ compliant with ERC-165, the factory SHOULD first obtain access control rights to the indexer contract and then call `logRegistration` directly with the address of the new DAO and the daoURI of the new DAO. Note that any user, including the DAO itself, MAY call `logRegistration` and submit a registration for a DAO which inherits the `IERC4824` interface and which is also ERC-165-compliant. ```solidity pragma solidity ^0.8.1; -error ERC-4824InterfaceNotSupported(); +error ERC4824InterfaceNotSupported(); -contract ERC-4824Index is AccessControl { +contract ERC4824Index is AccessControl { using ERC165Checker for address; bytes32 public constant REGISTRATION_ROLE = keccak256("REGISTRATION_ROLE"); @@ -162,123 +85,39 @@ contract ERC-4824Index is AccessControl { } function logRegistration(address daoAddress) external { - if (!daoAddress.supportsInterface(type(IERC-4824).interfaceId)) - revert ERC-4824InterfaceNotSupported(); + if (!daoAddress.supportsInterface(type(IERC4824).interfaceId)) + revert ERC4824InterfaceNotSupported(); emit DAOURIRegistered(daoAddress); } } ``` -If a DAO uses an external registration contract, the DAO SHOULD use a common registration factory contract linked to a common indexer to enable efficient network indexing. +If a DAO uses an external registration contract, the DAO SHOULD use a common registration factory contract linked to a common indexer to enable efficient network indexing. See the reference implementation of the factory contract in the attached assets folder to this EIP. -```solidity -pragma solidity ^0.8.1; - -/// @title ERC-4824 Common Interfaces for DAOs -/// @dev See - -contract CloneFactory { - // implementation of eip-1167 - see https://eips.ethereum.org/EIPS/eip-1167 - function createClone(address target) internal returns (address result) { - bytes20 targetBytes = bytes20(target); - assembly { - let clone := mload(0x40) - mstore( - clone, - 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000 - ) - mstore(add(clone, 0x14), targetBytes) - mstore( - add(clone, 0x28), - 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 - ) - result := create(0, clone, 0x37) - } - } -} - -contract ERC-4824RegistrationSummoner { - event NewRegistration( - address indexed daoAddress, - string daoURI, - address registration - ); - - address public ERC-4824Index; - address public template; /*Template contract to clone*/ - - constructor(address _template, address _ERC-4824Index) { - template = _template; - ERC-4824Index = _ERC-4824Index; - } - - function registrationAddress( - address by, - bytes32 salt - ) external view returns (address addr, bool exists) { - addr = Clones.predictDeterministicAddress( - template, - _saltedSalt(by, salt), - address(this) - ); - exists = addr.code.length > 0; - } - - function summonRegistration( - bytes32 salt, - string calldata daoURI_, - address manager, - address[] calldata contracts, - bytes[] calldata data - ) external returns (address registration, bytes[] memory results) { - registration = Clones.cloneDeterministic( - template, - _saltedSalt(msg.sender, salt) - ); - - if (manager == address(0)) { - ERC-4824Registration(registration).initialize( - msg.sender, - daoURI_, - ERC-4824Index - ); - } else { - ERC-4824Registration(registration).initialize( - msg.sender, - manager, - daoURI_, - ERC-4824Index - ); - } - - results = _callContracts(contracts, data); - - emit NewRegistration(msg.sender, daoURI_, registration); - } -``` +#### Indexing priority +daoURIs may be published directly in the DAO's contract or through a call to a common registration factory contract. In cases where both occur, the daoURI (and all sub-URIs) published through a call to a registration factory contract SHOULD take precedence. If there are multiple registrations, the most recent registration SHOULD take precedence. ### Members -Members JSON-LD Schema. Every contract implementing this EIP SHOULD implement a membersuRI pointing to a JSON object satisfying this schema. +Members JSON-LD Schema. Every contract implementing this EIP SHOULD implement a membersURI pointing to a JSON object satisfying this schema. Below, DID refers to [Decentralized Identifiers](https://www.w3.org/TR/2022/REC-did-core-20220719/). ```json { - "@context": "", + "@context": "https://www.daostar.org/schemas", "type": "DAO", - "name": "", "members": [ { - "type": "EthereumAddress", - "id": "

" + "id": "" }, { - "type": "EthereumAddress", - "id": "
" + "id": "" } ] } ``` +For example, for an address on Ethereum Mainnet, the [CAIP-10](https://github.com/ChainAgnostic/CAIPs/blob/ad0cfebc45a4b8368628340bf22aefb2a5edcab7/CAIPs/caip-10.md) address would be of the form `eip155:1:0x1234abcd`, while the DID address would be of the form `did:ethr:0x1234abcd`. + ### Proposals Proposals JSON-LD Schema. Every contract implementing this EIP SHOULD implement a proposalsURI pointing to a JSON object satisfying this schema. @@ -287,15 +126,14 @@ In particular, any on-chain proposal MUST be associated to an id of the form CAI ```json { - "@context": "http://www.daostar.org/schemas", - "type": "DAO", - "name": "", + "@context": "https://www.daostar.org/schemas", "proposals": [ { "type": "proposal", "id": "", "name": "", - "contentURI": "", + "contentURI": "", + "discussionURI": "", "status": "", "calls": [ { @@ -312,29 +150,15 @@ In particular, any on-chain proposal MUST be associated to an id of the form CAI } ``` +When deferenced, contentURI should return the content (i.e. the text) of the proposal. Similarly, discussionURI should return a discussion link, whether a forum post, Discord channel, or Twitter thread. + ### Activity Log Activity Log JSON-LD Schema. Every contract implementing this EIP SHOULD implement a activityLogURI pointing to a JSON object satisfying this schema. ```json { - "@context": "", - "type": "DAO", - "name": "", - "activities": [ - { - "id": "", - "type": "activity", - "proposal": { - "type": "proposal" - "id": "", - }, - "member": { - "type": "EthereumAddress", - "id": "
" - } - }, - ], + "@context": "https://www.daostar.org/schemas", "activities": [ { "id": "", @@ -344,10 +168,9 @@ Activity Log JSON-LD Schema. Every contract implementing this EIP SHOULD impleme "id": "", }, "member": { - "type": "EthereumAddress", - "id": "
" + "id": "" } - } + } ] } ``` @@ -356,36 +179,68 @@ Activity Log JSON-LD Schema. Every contract implementing this EIP SHOULD impleme Contracts JSON-LD Schema. Every contract implementing this EIP SHOULD implement a contractsURI pointing to a JSON object satisfying this schema. -Further, every contractsURI SHOULD include at least the contract inheriting the ERC-4824 interface. +contractsURI is especially important for DAOs with distinct or decentralized governance occurring across multiple different contracts, possibly across several chains. Multiple addresses may report the same daoURI. -``` +To prevent spam or spoofing, all DAOs adopting this specification SHOULD publish through contractsURI the address of every contract associated to the DAO, including but not limited to those that inherit the `IERC4824` interface or those that interact with a registration factory contract. Note that this includes the contract address(es) of any actual registration contracts deployed through a registration factory. + +```json { - "@context": "", - "type": "DAO", - "name": "", + "@context": "https://www.daostar.org/schemas", "contracts": [ { - "type": "EthereumAddress", - "id": "
", + "id": "" "name": "", "description": "" }, { - "type": "EthereumAddress", - "id": "
", + "id": "" "name": "", "description": "" - } + }, { - "type": "EthereumAddress", - "id": "
", - "name": "", + "id": "" + "name": "", "description": "" } ] } ``` +### URI fields +The content of subsidiary JSON files MAY be directly embedded as a JSON object directly within the top-level DAO JSON, in which case the relevant field MUST be renamed to remove the "URI" suffix. For example, `membersURI` would be renamed to `members`, `proposalsURI` would be renamed to `proposals`, and so on. In all cases, the embedded JSON object MUST conform to the relevant schema. A given field and a URI-suffixed field (e.g. `membersURI` and `members`) SHOULD NOT appear in the same JSON-LD; if they do, the field without the URI suffix MUST take precedence. + +Fields which are not appended with URI MAY be appended with a URI, for example `name` and `description` may be renamed to `nameURI` and `descriptionURI`, in which case the dereferenced URI MUST return a JSON-LD object containing the `"@context": "https://www.daostar.org/schemas"` field and the original key-value pair. + +For example, descriptionURI should return: +```json +{ + "@context": "https://www.daostar.org/schemas", + "description": "" +} +``` + +### Entities which are not DAOs + +Entities which are not DAOs or which do not wish to identify as DAOs MAY still publish daoURIs. If so, they SHOULD use a different value for the `type` field than "DAO", for example "Organization", "Foundation", "Person", or, most broadly, "Entity". + +Entities which are not DAOs or which do not wish to identify as DAOs MAY also publish metadata information through an off-chain orgURI or entityURI, which are aliases of daoURI. If these entities are reporting their URI through an on-chain smart contract or registration, however, they MUST retain `IERC4824`'s daoURI in order to enable network indexing. + +The Entity JSON-LD Schema: + +```json +{ + "@context": "https://www.daostar.org/schemas", + "type": "", + "name": "", + "description": "", + "membersURI": "", + "proposalsURI": "", + "activityLogURI": "", + "governanceURI": "", + "contractsURI": "" +} +``` + ## Rationale In this standard, we assume that all DAOs possess at least two primitives: _membership_ and _behavior_. _Membership_ is defined by a set of addresses. _Behavior_ is defined by a set of possible contract actions, including calls to external contracts and calls to internal functions. _Proposals_ relate membership and behavior; they are objects that members can interact with and which, if and when executed, become behaviors of the DAO. @@ -396,19 +251,23 @@ DAOs themselves have a number of existing and emerging use-cases. But almost all While we considered standardizing on-chain aspects of DAOs in this standard, particularly on-chain proposal objects and proposal IDs, we felt that this level of standardization was premature given (1) the relative immaturity of use-cases, such as multi-DAO proposals or master-minion contracts, that would benefit from such standardization, (2) the close linkage between proposal systems and governance, which we did not want to standardize (see โ€œgovernanceURIโ€, below), and (3) the prevalence of off-chain and L2 voting and proposal systems in DAOs (see โ€œproposalsURIโ€, below). Further, a standard URI interface is relatively easy to adopt and has been actively demanded by frameworks (see โ€œCommunity Consensusโ€, below). +We added the ability to append or remove the URI suffix to make dereferenced daoURIs easier to parse, especially in certain applications that did not want to maintain several services or flatfiles. Where there is a conflict, we decided that fields without the URI suffix should take precedence since they are more directly connected to the initial publication of daoURI. + +In terms of indexing: we believe that the most trustworthy way of publishing a daoURI is through an on-chain registration contract, as it is the clearest reflection of the active will of a DAO. It is also the primary way a DAO may โ€œoverwriteโ€ any other daoURI that has previously been published, through any means. If a DAO inherits daoURI directly through its contract, then this information is also trustworthy, though slightly less so as it often reflects the decisions of a DAO framework rather than the DAO directly. + ### membersURI -Approaches to membership vary widely in DAOs. Some DAOs and DAO frameworks (e.g.ย Gnosis Safe, Tribute), maintain an explicit, on-chain set of members, sometimes called owners or stewards. But many DAOs are structured so that membership status is based on the ownership of a token or tokens (e.g.ย Moloch, Compound, DAOstack, 1Hive Gardens). In these DAOs, computing the list of current members typically requires some form of off-chain indexing of events. +Approaches to membership vary widely in DAOs. Some DAOs and DAO frameworks (e.g. Gnosis Safe, Tribute), maintain an explicit, on-chain set of members, sometimes called owners or stewards. But many DAOs are structured so that membership status is based on the ownership of a token or tokens (e.g. Moloch, Compound, DAOstack, 1Hive Gardens). In these DAOs, computing the list of current members typically requires some form of off-chain indexing of events. -In choosing to ask only for an (off-chain) JSON schema of members, we are trading off some on-chain functionality for more flexibility and efficiency. We expect different DAOs to use membersURI in different ways: to serve a static copy of on-chain membership data, to contextualize the on-chain data (e.g.ย many Gnosis Safe stewards would not say that they are the only members of the DAO), to serve consistent membership for a DAO composed of multiple contracts, or to point at an external service that computes the list, among many other possibilities. We also expect many DAO frameworks to offer a standard endpoint that computes this JSON file, and we provide a few examples of such endpoints in the implementation section. +In choosing to ask only for an (off-chain) JSON schema of members, we are trading off some on-chain functionality for more flexibility and efficiency. We expect different DAOs to use membersURI in different ways: to serve a static copy of on-chain membership data, to contextualize the on-chain data (e.g. many Gnosis Safe stewards would not say that they are the only members of the DAO), to serve consistent membership for a DAO composed of multiple contracts, or to point at an external service that computes the list, among many other possibilities. We also expect many DAO frameworks to offer a standard endpoint that computes this JSON file, and we provide a few examples of such endpoints in the implementation section. -We encourage extensions of the Membership JSON-LD Schema, e.g.ย for DAOs that wish to create a state variable that captures active/inactive status or different membership levels. +We encourage extensions of the Membership JSON-LD Schema, e.g. for DAOs that wish to create a state variable that captures active/inactive status or different membership levels. ### proposalsURI -Proposals have become a standard way for the members of a DAO to trigger on-chain actions, e.g.ย sending out tokens as part of grant or executing arbitrary code in an external contract. In practice, however, many DAOs are governed by off-chain decision-making systems on platforms such as Discourse, Discord, or Snapshot, where off-chain proposals may function as signaling mechanisms for an administrator or as a prerequisite for a later on-chain vote. (To be clear, on-chain votes may also serve as non-binding signaling mechanisms or as โ€œbindingโ€ signals leading to some sort of off-chain execution.) The schema we propose is intended to support both on-chain and off-chain proposals, though DAOs themselves may choose to report only on-chain, only off-chain, or some custom mix of proposal types. +Proposals have become a standard way for the members of a DAO to trigger on-chain actions, e.g. sending out tokens as part of a grant or executing arbitrary code in an external contract. In practice, however, many DAOs are governed by off-chain decision-making systems on platforms such as Discourse, Discord, or Snapshot, where off-chain proposals may function as signaling mechanisms for an administrator or as a prerequisite for a later on-chain vote. (To be clear, on-chain votes may also serve as non-binding signaling mechanisms or as โ€œbindingโ€ signals leading to some sort of off-chain execution.) The schema we propose is intended to support both on-chain and off-chain proposals, though DAOs themselves may choose to report only on-chain, only off-chain, or some custom mix of proposal types. -**Proposal ID**. Every unique on-chain proposal MUST be associated to a proposal ID of the form CAIP10_ADDRESS + โ€œ?proposalId=โ€ + PROPOSAL_COUNTER, where PROPOSAL_COUNTER is an arbitrary string which is unique per CAIP10_ADDRESS. Note that PROPOSAL_COUNTER may not be the same as the on-chain representation of the proposal; however, each PROPOSAL_COUNTER should be unique per CAIP10_ADDRESS, such that the proposal ID is a globally unique identifier. We endorse the CAIP-10 standard to support multi-chain / layer 2 proposals and the โ€œ?proposalId=โ€ query syntax to suggest off-chain usage. +**Proposal ID**. In the specification, we state that every unique on-chain proposal must be associated to a proposal ID of the form CAIP10_ADDRESS + โ€œ?proposalId=โ€ + PROPOSAL_COUNTER, where PROPOSAL_COUNTER is an arbitrary string which is unique per CAIP10_ADDRESS. Note that PROPOSAL_COUNTER may not be the same as the on-chain representation of the proposal; however, each PROPOSAL_COUNTER should be unique per CAIP10_ADDRESS, such that the proposal ID is a globally unique identifier. We endorse the CAIP-10 standard to support multi-chain / layer 2 proposals and the โ€œ?proposalId=โ€ query syntax to suggest off-chain usage. **ContentURI**. In many cases, a proposal will have some (off-chain) content such as a forum post or a description on a voting platform which predates or accompanies the actual proposal. @@ -433,7 +292,7 @@ _Alternatives we considered: history, interactions_ ### governanceURI -Membership, to be meaningful, usually implies rights and affordances of some sort, e.g.ย the right to vote on proposals, the right to ragequit, the right to veto proposals, and so on. But many rights and affordances of membership are realized off-chain (e.g.ย right to vote on a Snapshot, gated access to a Discord). Instead of trying to standardize these wide-ranging practices or forcing DAOs to locate descriptions of those rights on-chain, we believe that a flatfile represents the easiest and most widely-acceptable mechanism for communicating what membership means and how proposals work. These flatfiles can then be consumed by services such as Etherscan, supporting DAO discoverability and legibility. +Membership, to be meaningful, usually implies rights and affordances of some sort, e.g. the right to vote on proposals, the right to ragequit, the right to veto proposals, and so on. But many rights and affordances of membership are realized off-chain (e.g. right to vote on a Snapshot, gated access to a Discord). Instead of trying to standardize these wide-ranging practices or forcing DAOs to locate descriptions of those rights on-chain, we believe that a flatfile represents the easiest and most widely-acceptable mechanism for communicating what membership means and how proposals work. These flatfiles can then be consumed by services such as Etherscan, supporting DAO discoverability and legibility. We chose the word โ€œgovernanceโ€ as an appropriate word that reflects (1) the widespread use of the word in the DAO ecosystem and (2) the common practice of emitting a governance.md file in open-source software projects. @@ -441,7 +300,9 @@ _Alternative names considered: description, readme, constitution_ ### contractsURI -Over the course of community conversations, multiple parties raised the need to report on, audit, and index the different contracts belonging to a given DAO. Some of these contracts are deployed as part of the modular design of a single DAO framework, e.g. the core, voting, and timelock contracts within Open Zeppelin / Compound Governor. In other cases, a DAO might deploy multiple multsigs as treasuries and/or multiple subDAOs that are effectively controlled by the DAO. ContractsURI offers a generic way of declaring these many instruments. +Over the course of community conversations, multiple parties raised the need to report on, audit, and index the different contracts belonging to a given DAO. Some of these contracts are deployed as part of the modular design of a single DAO framework, e.g. the core, voting, and timelock contracts within Open Zeppelin / Compound Governor. In other cases, a DAO might deploy multiple multsigs as treasuries and/or multiple subDAOs that are effectively controlled by the DAO. contractsURI offers a generic way of declaring these many instruments so that they can be efficiently aggregated by an indexer. + +contractsURI is also important for spam prevention or spoofing. Some DAOs may spread governance power and control across multiple different governance contracts, possibly across several chains. To capture this reality, multiple addresses may wish to report the same daoURI, or different daoURIs with the same name. However, unauthorized addresses may try to report the same daoURI or name. Additional contract information can prevent attacks of this sort by allowing indexers to weed out spam information. _Alternative names considered: contractsRegistry, contractsList_ @@ -453,9 +314,9 @@ Further, given the emergence of patterns such as subDAOs and DAOs of DAOs in lar ### **Community Consensus** -The initial draft standard was developed as part of the DAOstar One roundtable series, which included representatives from all major EVM-based DAO frameworks (Aragon, Compound, DAOstack, Gnosis, Moloch, OpenZeppelin, and Tribute), a wide selection of DAO tooling developers, as well as several major DAOs. Thank you to all the participants of the roundtable. We would especially like to thank Auryn Macmillan, Fabien of Snapshot, Selim Imoberdorf, Lucia Korpas, and Mehdi Salehi for their contributions. +The initial draft standard was developed as part of the DAOstar roundtable series, which included representatives from all major EVM-based DAO frameworks (Aragon, Compound, DAOstack, Gnosis, Moloch, OpenZeppelin, and Tribute), a wide selection of DAO tooling developers, as well as several major DAOs. Thank you to all the participants of the roundtable. We would especially like to thank Fabien of Snapshot, Jake Hartnell, Auryn Macmillan, Selim Imoberdorf, Lucia Korpas, and Mehdi Salehi for their contributions. -In-person events will be held at Schelling Point 2022 and at ETHDenver 2022, where we hope to receive more comments from the community. We also plan to schedule a series of community calls through early 2022. +In-person events for community comment were held at Schelling Point 2022, ETHDenver 2022, ETHDenver 2023, DAO Harvard 2023, DAO Stanford 2023 (also known as the Science of Blockchains Conference DAO Workshop). The team also hosted over 50 biweekly community calls as part of the DAOstar project. ## Backwards Compatibility @@ -470,4 +331,3 @@ Indexers that rely on the data returned by the URI should take caution if DAOs r ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). - diff --git a/assets/erc-4824/contracts/ERC4824Factory.sol b/assets/erc-4824/contracts/ERC4824Factory.sol new file mode 100644 index 0000000000..93917c8a24 --- /dev/null +++ b/assets/erc-4824/contracts/ERC4824Factory.sol @@ -0,0 +1,84 @@ +pragma solidity ^0.8.1; + +/// @title ERC-4824 Common Interfaces for DAOs +/// @dev See + +contract CloneFactory { +ย  ย  // implementation of eip-1167 - see https://eips.ethereum.org/EIPS/eip-1167 +ย  ย  function createClone(address target) internal returns (address result) { +ย  ย  ย  ย  bytes20 targetBytes = bytes20(target); +ย  ย  ย  ย  assembly { +ย  ย  ย  ย  ย  ย  let clone := mload(0x40) +ย  ย  ย  ย  ย  ย  mstore( +ย  ย  ย  ย  ย  ย  ย  ย  clone, +ย  ย  ย  ย  ย  ย  ย  ย  0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000 +ย  ย  ย  ย  ย  ย  ) +ย  ย  ย  ย  ย  ย  mstore(add(clone, 0x14), targetBytes) +ย  ย  ย  ย  ย  ย  mstore( +ย  ย  ย  ย  ย  ย  ย  ย  add(clone, 0x28), +ย  ย  ย  ย  ย  ย  ย  ย  0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 +ย  ย  ย  ย  ย  ย  ) +ย  ย  ย  ย  ย  ย  result := create(0, clone, 0x37) +ย  ย  ย  ย  } +ย  ย  } +} + +contract ERC4824RegistrationSummoner { +ย  ย  event NewRegistration( +ย  ย  ย  ย  address indexed daoAddress, +ย  ย  ย  ย  string daoURI, +ย  ย  ย  ย  address registration +ย  ย  ); + +ย  ย  address public ERC4824Index; +ย  ย  address public template; /*Template contract to clone*/ + +ย  ย  constructor(address _template, address _ERC-4824Index) { +ย  ย  ย  ย  template = _template; +ย  ย  ย  ย  ERC-4824Index = _ERC-4824Index; +ย  ย  } + +ย  ย  function registrationAddress( +ย  ย  ย  ย  address by, +ย  ย  ย  ย  bytes32 salt +ย  ย  ) external view returns (address addr, bool exists) { +ย  ย  ย  ย  addr = Clones.predictDeterministicAddress( +ย  ย  ย  ย  ย  ย  template, +ย  ย  ย  ย  ย  ย  _saltedSalt(by, salt), +ย  ย  ย  ย  ย  ย  address(this) +ย  ย  ย  ย  ); +ย  ย  ย  ย  exists = addr.code.length > 0; +ย  ย  } + +ย  ย  function summonRegistration( +ย  ย  ย  ย  bytes32 salt, +ย  ย  ย  ย  string calldata daoURI_, +ย  ย  ย  ย  address manager, +ย  ย  ย  ย  address[] calldata contracts, +ย  ย  ย  ย  bytes[] calldata data +ย  ย  ) external returns (address registration, bytes[] memory results) { +ย  ย  ย  ย  registration = Clones.cloneDeterministic( +ย  ย  ย  ย  ย  ย  template, +ย  ย  ย  ย  ย  ย  _saltedSalt(msg.sender, salt) +ย  ย  ย  ย  ); + +ย  ย  ย  ย  if (manager == address(0)) { +ย  ย  ย  ย  ย  ย  ERC-4824Registration(registration).initialize( +ย  ย  ย  ย  ย  ย  ย  ย  msg.sender, +ย  ย  ย  ย  ย  ย  ย  ย  daoURI_, +ย  ย  ย  ย  ย  ย  ย  ย  ERC-4824Index +ย  ย  ย  ย  ย  ย  ); +ย  ย  ย  ย  } else { +ย  ย  ย  ย  ย  ย  ERC-4824Registration(registration).initialize( +ย  ย  ย  ย  ย  ย  ย  ย  msg.sender, +ย  ย  ย  ย  ย  ย  ย  ย  manager, +ย  ย  ย  ย  ย  ย  ย  ย  daoURI_, +ย  ย  ย  ย  ย  ย  ย  ย  ERC-4824Index +ย  ย  ย  ย  ย  ย  ); +ย  ย  ย  ย  } + +ย  ย  ย  ย  results = _callContracts(contracts, data); + +ย  ย  ย  ย  emit NewRegistration(msg.sender, daoURI_, registration); +ย  ย  } +} diff --git a/assets/erc-4824/contracts/ERC4824Registration.sol b/assets/erc-4824/contracts/ERC4824Registration.sol new file mode 100644 index 0000000000..0b5003fe8e --- /dev/null +++ b/assets/erc-4824/contracts/ERC4824Registration.sol @@ -0,0 +1,72 @@ +pragma solidity ^0.8.1; + +/// @title ERC-4824: DAO Registration +contract ERC-4824Registration is IERC-4824, AccessControl { + bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); + + string private _daoURI; + + address daoAddress; + + constructor() { + daoAddress = address(0xdead); + } + + /// @notice Set the initial DAO URI and offer manager role to an address + /// @dev Throws if initialized already + /// @param _daoAddress The primary address for a DAO + /// @param _manager The address of the URI manager + /// @param daoURI_ The URI which will resolve to the governance docs + function initialize( + address _daoAddress, + address _manager, + string memory daoURI_, + address _ERC-4824Index + ) external { + initialize(_daoAddress, daoURI_, _ERC-4824Index); + _grantRole(MANAGER_ROLE, _manager); + } + + /// @notice Set the initial DAO URI + /// @dev Throws if initialized already + /// @param _daoAddress The primary address for a DAO + /// @param daoURI_ The URI which will resolve to the governance docs + function initialize( + address _daoAddress, + string memory daoURI_, + address _ERC-4824Index + ) public { + if (daoAddress != address(0)) revert AlreadyInitialized(); + daoAddress = _daoAddress; + _setURI(daoURI_); + + _grantRole(DEFAULT_ADMIN_ROLE, _daoAddress); + _grantRole(MANAGER_ROLE, _daoAddress); + + ERC-4824Index(_ERC-4824Index).logRegistration(address(this)); + } + + /// @notice Update the URI for a DAO + /// @dev Throws if not called by dao or manager + /// @param daoURI_ The URI which will resolve to the governance docs + function setURI(string memory daoURI_) public onlyRole(MANAGER_ROLE) { + _setURI(daoURI_); + } + + function _setURI(string memory daoURI_) internal { + _daoURI = daoURI_; + emit DAOURIUpdate(daoAddress, daoURI_); + } + + function daoURI() external view returns (string memory daoURI_) { + return _daoURI; + } + + function supportsInterface( + bytes4 interfaceId + ) public view virtual override returns (bool) { + return + interfaceId == type(IERC-4824).interfaceId || + super.supportsInterface(interfaceId); + } +} From c23d4162a0e61662028d3828e79c68902840cf95 Mon Sep 17 00:00:00 2001 From: Pavel Fedotov <66903336+Pfed-prog@users.noreply.github.com> Date: Tue, 11 Jun 2024 20:34:29 +0200 Subject: [PATCH 022/126] fixing typos (#471) --- ERCS/erc-1046.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-1046.md b/ERCS/erc-1046.md index 82907d56cb..2aaaac64ca 100644 --- a/ERCS/erc-1046.md +++ b/ERCS/erc-1046.md @@ -97,7 +97,7 @@ The resolved JSON of the `tokenURI` described in the ERC-20 Interface Extension */ interface ERC20TokenMetadata { /** - * Interoperabiliy, to differentiate between different types of tokens and their corresponding URIs. + * Interoperability, to differentiate between different types of tokens and their corresponding URIs. **/ interop: InteroperabilityMetadata; @@ -157,7 +157,7 @@ Contracts that implement ERC-721 and use its token metadata URI SHOULD to use th ```typescript interface ERC721TokenMetadataInterop extends ERC721TokenMetadata { /** - * Interoperabiliy, to avoid confusion between different token URIs + * Interoperability, to avoid confusion between different token URIs **/ interop: InteroperabilityMetadata; } @@ -189,7 +189,7 @@ Contracts that implement ERC-1155 and use its token metadata URI are RECOMMENDED ```typescript interface ERC1155TokenMetadataInterop extends ERC1155TokenMetadata { /** - * Interoperabiliy, to avoid confusion between different token URIs + * Interoperability, to avoid confusion between different token URIs **/ interop: InteroperabilityMetadata; } From 16be9fc0248cb794db34442f446e0c6e248ee07d Mon Sep 17 00:00:00 2001 From: Liaoyuan Date: Wed, 12 Jun 2024 12:24:10 +0800 Subject: [PATCH 023/126] Update ERC-7627: change to external Merged by EIP-Bot. --- ERCS/erc-7627.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7627.md b/ERCS/erc-7627.md index 09594a8ce5..e035415ca6 100644 --- a/ERCS/erc-7627.md +++ b/ERCS/erc-7627.md @@ -111,19 +111,19 @@ contract ERC7627 { event PublicKeyUpdated(address indexed user, bytes newPublicKey, PublicKeyAlgorithm algorithm); // Function to register a user with their public key - function updatePublicKey(bytes calldata _publicKey, PublicKeyAlgorithm _algorithm) public { + function updatePublicKey(bytes calldata _publicKey, PublicKeyAlgorithm _algorithm) external { pk[msg.sender].publicKey = _publicKey; pk[msg.sender].algorithm = _algorithm; emit PublicKeyUpdated(msg.sender, _publicKey, _algorithm); } // Function to send an encrypted message from one user to another - function sendMessage(address _to, bytes32 _sessionId, bytes calldata _encryptedMessage) public { + function sendMessage(address _to, bytes32 _sessionId, bytes calldata _encryptedMessage) external { emit MessageSent(msg.sender, _to, _sessionId, _encryptedMessage); } // Function to retrieve a user's public key - function getUserPublicKey(address _user) public view returns (bytes memory, PublicKeyAlgorithm) { + function getUserPublicKey(address _user) external view returns (bytes memory, PublicKeyAlgorithm) { UserInfo memory userInfo = pk[_user]; return (userInfo.publicKey, userInfo.algorithm); } From a8198b0069665a79114c665596f11358693d5319 Mon Sep 17 00:00:00 2001 From: Liaoyuan Date: Wed, 12 Jun 2024 20:47:29 +0800 Subject: [PATCH 024/126] Update ERC-7644: Name Registry Merged by EIP-Bot. --- ERCS/erc-7644.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7644.md b/ERCS/erc-7644.md index fd09d81159..5eaaa8c987 100644 --- a/ERCS/erc-7644.md +++ b/ERCS/erc-7644.md @@ -15,7 +15,7 @@ requires: 721 This extension defines an interface that adds a naming mechanism to [ERC-721](./eip-721.md) tokens. It allows each token to have a unique name with a set expiration date, ensuring uniqueness within the current NFT contract. The interface includes functions for assigning, updating, and querying names and their associated tokens, ensuring that names remain unique until they expire. The entity responsible for setting names depends on the specific use case scenario when utilizing this extension. -### Motivation +## Motivation As decentralized domain registration methods evolve with the integration of NFTs, we see an opportunity to extend this paradigm to the realm of usernames. By associating token IDs with usernames, we enhance the intuitive identification of entities within decentralized ecosystems. From f74302fd25957c765246b837707c6b4c137048dd Mon Sep 17 00:00:00 2001 From: Liaoyuan Date: Wed, 12 Jun 2024 20:57:10 +0800 Subject: [PATCH 025/126] Update ERC-7720: Deferred token transfer Merged by EIP-Bot. --- ERCS/erc-7720.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7720.md b/ERCS/erc-7720.md index 5eeee4e248..83b830b697 100644 --- a/ERCS/erc-7720.md +++ b/ERCS/erc-7720.md @@ -15,7 +15,7 @@ requires: 20 This standard specifies that allows users to deposit [ERC-20](./eip-20.md) tokens for a beneficiary. The beneficiary can withdraw the tokens only after a specified future timestamp. Each deposit transaction is assigned a unique ID and includes details such as the token address, sender, recipient, amount, unlock time, and withdrawal status. -### Motivation +## Motivation In various scenarios, such as vesting schedules, escrow services, or timed rewards, there is a need for deferred payments. This contract provides a secure and reliable mechanism for time-locked token transfers, ensuring that tokens can only be transferred after a specified timestamp is reached. By facilitating structured and delayed payments, it adds an extra layer of security and predictability to token transfers. This is particularly useful for scenarios where payments are contingent upon the passage of time. From d9cbb65c34e9c8b95231cdadd1c5fb13f74aa4ef Mon Sep 17 00:00:00 2001 From: Lanyin Z <106770708+lanyinzly@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:24:18 -0400 Subject: [PATCH 026/126] Add ERC: Atomic Push-based Data Feed Among Contracts Merged by EIP-Bot. --- ERCS/erc-7615.md | 250 ++++++++++++++++++++++++++++++++++++ assets/erc-7615/ERC7615.svg | 109 ++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 ERCS/erc-7615.md create mode 100644 assets/erc-7615/ERC7615.svg diff --git a/ERCS/erc-7615.md b/ERCS/erc-7615.md new file mode 100644 index 0000000000..86f5317fde --- /dev/null +++ b/ERCS/erc-7615.md @@ -0,0 +1,250 @@ +--- +eip: 7615 +title: Atomic Push-based Data Feed Among Contracts +description: An Atomic Mechanism to Allow Publisher Contract Push Data to Subcriber Contracts +author: Elaine Zhang (@lanyinzly) , Jerry , Amandafanny , Shouhao Wong (@wangshouh) , Doris Che (@Cheyukj) , Henry Yuan (@onehumanbeing) +discussions-to: https://ethereum-magicians.org/t/erc-7615-smart-contract-data-push-mechanism/18466 +status: Draft +type: Standards Track +category: ERC +created: 2024-02-03 +--- +## Abstract +This ERC proposes a push-based mechanism for sending data, allowing publisher contract to automatically push certain data to subscriber contracts during a call. The specific implementation relies on two interfaces: one for publisher contract to push data, and another for the subscriber contract to receive data. When the publisher contract is called, it checks if the called function corresponds to subscriber addresses. If it does, the publisher contract push data to the subscriber contracts. + +## Motivation +Currently, there are many keepers rely on off-chain data or seperate data collection process to monitor the events on chain. This proposal aims to establish a system where the publisher contract can atomicly push data to inform subscriber contracts about the updates. The direct on-chain interaction bewteen the publisher and the subscriber allows the system to be more trustless and efficient. + +This proposal will offer significant advantages across a range of applications, such as enabling the boundless and permissionless expansion of DeFi, as well as enhancing DAO governance, among others. + +### Lending Protocol + +An example of publisher contract could be an oracle, which can automatically push the price update through initiating a call to the subscriber protocol. The lending protocol, as the subscriber, can automatically liquidate the lending positions based on the received price. + +### Automatic Payment + +A service provider can use a smart contract as a publisher contract, so that when a user call this contract, it can push the information to the subsriber contracts, such as, the users' wallets like NFT bound accounts that follows [ERC-6551](./eip-6551.md) or other smart contract wallets. The user's smart contract wallet can thus perform corresponding payment operations automatically. Compared to traditional `approve` needed approach, this solution allows more complex logic in implementation, such as limited payment, etc. + +### PoS Without Transferring Assets + +For some staking scenarios, especially NFT staking, the PoS contract can be set as the subscriber and the NFT contracts can be set as the publisher. Staking can thus achieved through contracts interation, allowing users to earn staking rewards without transferring assets. + +When operations like `transfer` of NFT occur, the NFT contract can push this information to the PoS contract, which can then perform unstaking or other functions. + +### DAO Voting + +The DAO governance contract as a publisher could automatically triggers the push mechanism after the vote is completed, calling relevant subscriber contracts to directly implement the voting results, such as injecting funds into a certain account or pool. + +## Specification + +The key words โ€œMUSTโ€, โ€œMUST NOTโ€, โ€œREQUIREDโ€, โ€œSHALLโ€, โ€œSHALL NOTโ€, โ€œSHOULDโ€, โ€œSHOULD NOTโ€, โ€œRECOMMENDEDโ€, โ€œMAYโ€, and โ€œOPTIONALโ€ in this document are to be interpreted as described in RFC 2119. + +### Overview + +The push mechanism can be divided into the following four steps: + +1. The publisher contract is called. +2. The publisher contract query the subscriber list from the `selector` of the function called. The subscriber contract can put the selected data into `inbox`. +3. The publisher contract push `selector` and data through calling `exec` function of the subscriber contract. +4. The subscriber contract executes based on pushed `selector` and data, or it may request information from the publisher contract's inbox function as needed. + +In the second step, the relationship between a called function and the corresponding subscriber can be configured in the publisher contract. Two configuration schemes are proposed: + +1. Unconditional Push: Any call to the configured `selector` triggers a push +2. Conditional Push: Only the conditioned calls to the configured `selector` trigger a push based on the configuration. + +It's allowed to configure multiple, different types of subscriber contracts for a single `selector`. The publisher contract will call the `exec` function of each subscriber contract to push the request. + +When unsubscribing a contract from a `selector`, publisher contract MUST check whether `isLocked` function of the subscriber contract returns `true`. + +It is OPTIONAL for a publisher contract to use the `inbox` mechanism to store data. + +In the fourth step, the subscriber contract SHOULD handle all possible `selector` requests and data in the implementation of `exec` function. In some cases, `exec` MAY call `inbox` function of publisher contract to obtain the pushed data in full. + + +![Workflow](../assets/eip-7615/ERC7615.svg) + +### Contract interface + +As mentioned above, there are Unconditional Push and Conditional Push two types of implementation. To implement Unconditional Push, the publisher contract SHOULD implement the following interface: + +```solidity +interface IPushForce { + function isForceApproved(bytes4 selector, address target) external returns (bool); + function forceApprove(bytes4 selector, address target) external; + function forceCancel(bytes4 selector, address target) external; +} +``` + +`isForceApproved` is to query whether `selector` has already unconditionally bound to the subscriber contract with the address `target`. +`forceApprove` is to bind `selector` to the subscriber contract `target`. `forceCancel` is to cancel the binding relationship between `selector` and `target`, where `isLocked` function of `target` returns `true` is REQUIRED. + +To implement Conditional Push, the publisher contract SHOULD implement the following interface: + +```solidity +interface IPushFree { + function inbox(bytes4 selector) external returns (bytes memory); + function isApproved(bytes4 selector, address target, bytes calldata data) external returns (bool); + function approve(bytes4 selector, address target, bytes calldata data) external; + function cancel(bytes4 selector, address target, bytes calldata data) external; +} +``` + +`isApproved`, `approve`, and `cancel` have functionalities similar to the corresponding functions in `IPushForce`. However, an additional `data` parameter is introduced here for checking whether a push is needed. +The `inbox` here is used to store data in case of being called from the subscriber contract. + +The publisher contract SHOULD implement `_push(bytes4 selector, bytes calldata data)` function, which acts as a hook. Any function within the publisher contract that needs to implement push mechanism must call this internal function. The function MUST include querying both unconditional and conditional subscription contracts based on `selector` and `data`, and then calling corresponding `exec` function of the subscribers. + +A subscriber need to implement the following interface: + +```solidity +interface IExec { + function isLocked(bytes4 selector, bytes calldata data) external returns (bool); + function exec(bytes4 selector, bytes calldata data) external; +} +``` + +`exec` is to receive requests from the publisher contracts and further proceed to execute. +`isLocked` is to check the status of whether the subscriber contract can unsubscribe the publisher contract based on `selector` and `data`. It is triggered when a request to unsubscribe is received. + +## Rationale + +### Unconditional and Conditional Configuration + +When the sending contract is called, it is possible to trigger a push, requiring the caller to pay the resulting gas fees. +In some cases, an Unconditional Push is necessary, such as pushing price changes to a lending protocol. While, Conditional Push will reduce the unwanted gas consumption. + +### Check `isLocked` Before Unsubscribing + +Before `forceCancel` or `cancel`, the publisher contract MUST call the `isLocked` function of the subscriber contract to avoid unilateral unsubscribing. The subscriber contract may have a significant logical dependence on the publisher contract, and thus unsubscription could lead to severe issues within the subscriber contract. Therefore, the subscriber contract should implement `isLocked` function with thorough consideration. + +### `inbox` Mechanism + +In certain scenarios, the publisher contract may only push essential data with `selector` to the subscriber contracts, while the full data might be stored within `inbox`. Upon receiving the push from the publisher contract, the subscriber contract is optional to call `inbox`. +`inbox` mechanism simplifies the push information while still ensuring the availability of complete data, thereby reducing gas consumption. + +### Using Function Selectors as Parameters + +Using function selectors to retrieve the addresses of subscriber contracts allows +more detailed configuration. +For the subscriber contract, having the specific function of the request source based on the push information enables more accurate handling of the push information. + +## Reference Implementation + +```solidity +pragma solidity ^0.8.24; + +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {IPushFree, IPushForce} from "./interfaces/IPush.sol"; +import {IPull} from "./interfaces/IPull.sol"; + +contract Foo is IPushFree, IPushForce { + using EnumerableSet for EnumerableSet.AddressSet; + + mapping(bytes4 selector => mapping(uint256 tokenId => EnumerableSet.AddressSet targets)) private _registry; + mapping(bytes4 selector => EnumerableSet.AddressSet targets) private _registryOfAll; + + modifier notLock(bytes4 selector, address target, bytes memory data) { + require(!IPull(target).isLocked(selector, data), "Foo: lock"); + _; + } + + function inbox(bytes4 selector) public view returns (bytes memory data) { + uint256 loadData; + assembly { + loadData := tload(selector) + } + + data = abi.encode(loadData); + } + + function isApproved(bytes4 selector, address target, bytes calldata data) external view override returns (bool) { + uint256 tokenId = abi.decode(data, (uint256)); + return _registry[selector][tokenId].contains(target); + } + + function isForceApproved(bytes4 selector, address target) external view override returns (bool) { + return _registryOfAll[selector].contains(target); + } + + function approve(bytes4 selector, address target, bytes calldata data) external override { + uint256 tokenId = abi.decode(data, (uint256)); + _registry[selector][tokenId].add(target); + } + + function cancel(bytes4 selector, address target, bytes calldata data) + external + override + notLock(selector, target, data) + { + uint256 tokenId = abi.decode(data, (uint256)); + _registry[selector][tokenId].remove(target); + } + + function forceApprove(bytes4 selector, address target) external override { + _registryOfAll[selector].add(target); + } + + function forceCancel(bytes4 selector, address target) external override notLock(selector, target, "") { + _registryOfAll[selector].remove(target); + } + + function send(uint256 message) external { + _push(this.send.selector, message); + } + + function _push(bytes4 selector, uint256 message) internal { + assembly { + tstore(selector, message) + } + + address[] memory targets = _registry[selector][message].values(); + for (uint256 i = 0; i < targets.length; i++) { + IPull(targets[i]).pull(selector, abi.encode(message)); + } + + targets = _registryOfAll[selector].values(); + for (uint256 i = 0; i < targets.length; i++) { + IPull(targets[i]).pull(selector, abi.encode(message)); + } + } +} + +contract Bar is IPull { + event Log(bytes4 indexed selector, bytes data, bytes inboxData); + + function isLocked(bytes4, bytes calldata) external pure override returns (bool) { + return true; + } + + function pull(bytes4 selector, bytes calldata data) external { + bytes memory inboxData = IPushFree(msg.sender).inbox(selector); + emit Log(selector, data, inboxData); + } +} +``` + +## Security Considerations + +### `exec` Attacks + +The `exec` function is `public`, therefore, it is vulnerable to malicious calls where arbitrary push information can be inserted. Implementations of `exec` should carefully consider the arbitrariness of calls and should not directly use data passed by the exec function without verification. + +### Reentrancy Attack + +The publisher contract's call to the subscriber contract's `exec` function could lead to reentrancy attacks. Malicious subscription contracts might construct reentrancy attacks to the publisher contract within `exec`. + +### Arbitrary Target Approve + +Implementation of `forceApprove` and `approve` should have reasonable access controls; otherwise, unnecessary gas losses could be imposed on callers. + +Check the gas usage of the `exec` function. + +### isLocked implementation + +Subscriber contracts should implement the `isLocked` function to avoid potential loss brought by unsubscription. This is particularly crucial for lending protocols implementing this proposal. Improper unsubscription can lead to abnormal clearing, causing considerable losses. + +Similarly, when subscribing, the publisher contract should consider whether `isLocked` is properly implemented to prevent irrevocable subscriptions. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/assets/erc-7615/ERC7615.svg b/assets/erc-7615/ERC7615.svg new file mode 100644 index 0000000000..a8754bac14 --- /dev/null +++ b/assets/erc-7615/ERC7615.svg @@ -0,0 +1,109 @@ +ClientPublisherSubscriber Call somefunc(...)Query SubscriberCall exec(bytes4 selector, bytes calldata data)ResultResult + + + + + + + + + + \ No newline at end of file From fd5afe4e5d41724d66b520375efb91350634a8ba Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Thu, 13 Jun 2024 21:02:49 +0200 Subject: [PATCH 027/126] Update ERC-7540: Update ERC7540 interface IDs Merged by EIP-Bot. --- ERCS/erc-7540.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ERCS/erc-7540.md b/ERCS/erc-7540.md index e5ef6fd5aa..f9786b9065 100644 --- a/ERCS/erc-7540.md +++ b/ERCS/erc-7540.md @@ -418,7 +418,9 @@ MAY be logged when the operator status is set to the same status it was before t Smart contracts implementing this Vault standard MUST implement the [ERC-165](./eip-165.md) `supportsInterface` function. -Asynchronous deposit Vaults MUST return the constant value `true` if `0x3a2f2433` is passed through the `interfaceID` argument. +All asynchronous Vaults MUST return the constant value `true` if either `0xe3bc4e65` (representing the operator methods that all ERC-7540 Vaults implement) or `0x2f0a18c5` (representing the [ERC-7575](./eip-7575.md) interface) is passed through the `interfaceID` argument. + +Asynchronous deposit Vaults MUST return the constant value `true` if `0xce3bbe50` is passed through the `interfaceID` argument. Asynchronous redemption Vaults MUST return the constant value `true` if `0x620ee8e4` is passed through the `interfaceID` argument. From b8556f7070f7e0b9cf43a915feedc3fa578c427b Mon Sep 17 00:00:00 2001 From: Konrad Date: Fri, 14 Jun 2024 03:42:56 +0100 Subject: [PATCH 028/126] Update ERC-7579: hook postCheck simplification Merged by EIP-Bot. --- ERCS/erc-7579.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ERCS/erc-7579.md b/ERCS/erc-7579.md index 5d41db4915..6ecc71f62a 100644 --- a/ERCS/erc-7579.md +++ b/ERCS/erc-7579.md @@ -320,12 +320,10 @@ interface IHook is IModule { /** * @dev Called by the smart account after execution * @param hookData the data that was returned by the `preCheck` function - * @param executionSuccess whether the execution(s) was (were) successful - * @param executionReturn the return/revert data of the execution(s) * * MAY validate the `hookData` to validate transaction context of the `preCheck` function */ - function postCheck(bytes calldata hookData, bool executionSuccess, bytes calldata executionReturn) external; + function postCheck(bytes calldata hookData) external; } ``` From dac2663a9582ab80f29ae8f18c37092983480a94 Mon Sep 17 00:00:00 2001 From: shiv Date: Fri, 14 Jun 2024 20:31:39 +0700 Subject: [PATCH 029/126] Add ERC: Modular Accounts with Delegated Validation Merged by EIP-Bot. --- ERCS/erc-7582.md | 163 ++++++++++++++++++++++++++++++++ assets/erc-7582/MADVAccount.sol | 26 +++++ assets/erc-7582/base-flow.svg | 29 ++++++ 3 files changed, 218 insertions(+) create mode 100644 ERCS/erc-7582.md create mode 100644 assets/erc-7582/MADVAccount.sol create mode 100644 assets/erc-7582/base-flow.svg diff --git a/ERCS/erc-7582.md b/ERCS/erc-7582.md new file mode 100644 index 0000000000..5b2746508a --- /dev/null +++ b/ERCS/erc-7582.md @@ -0,0 +1,163 @@ +--- +eip: 7582 +title: Modular Accounts with Delegated Validation +description: Extends ERC-4337 interface with nonce-based plugins +author: Shivanshi Tyagi (@nerderlyne), Ross Campbell (@z0r0z) +discussions-to: https://ethereum-magicians.org/t/erc-7582-modular-accounts-with-delegated-validation/17640 +status: Draft +type: Standards Track +category: ERC +created: 2023-12-25 +requires: 4337 +--- + +## Abstract + +This proposal standardizes a method for adding plugins and composable logic to smart contract accounts built on existing interfaces like [ERC-4337](eip-4337.md) (e.g., ERC-4337's `IAccount`). Specifically, by formalizing how applications can use the ERC-4337 Entry Point `NonceManager` and the emission of the `IEntryPoint` `UserOperationEvent` to account for plugin interactions, as well, as how to extract designated validators (in this case, by means of `IAccount`'s `validateUserOp`), accounts can specify how they call plugin contracts and grant special executory access for more advanced operations. Furthermore, this minimalist plugin approach is developer-friendly and complimentary to existing account abstraction standards by not requiring any additional functions for contracts that follow the `IAccount` interface (itself minimalist in only specifying one function, `validateUserOp`). + +## Motivation + +Smart contract accounts (contract accounts) are a powerful tool for managing digital assets and executing transactions by allowing users to program their interactions with blockchains. However, they are often limited in their functionality and flexibility without sufficient consensus around secure abstraction designs (albeit, the adoption of ERC-4337 is the preferred path of this proposal). For example, contract accounts are often unable to support social recovery, payment schedules, and other features that are common in traditional financial systems without efficient and predictable schemes to delegate execution and other access rights to approximate the UX of custodial and more specialized applications. + +Account abstraction standards like ERC-4337 have achieved simplification of many core contract account concerns such as transaction fee payments, but to fully leverage the expressive capability of these systems to accomplish user intents, minimalist methods to delegate contract account access and validation to other contracts would aid their UX and extend the benefits of centering operations around the Entry Point. + +While the `IAccount` interface from ERC-4337 does not specify a way to add custom validation logic to contract accounts to support plugins and similar extensions without upgrades or migrations, it nevertheless contains sufficient information to do so efficiently. This proposal therefore offers a method for adding plugins and other composable validation logic to smart contract accounts built on existing interfaces with singleton nonce-tracking like ERC-4337's `IAccount` and `NonceManager`. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +![diagram showing proposed flow](../assets/eip-7582/base-flow.svg) + +We leverage the key in ERC-4337 semi-abstracted nonce as the pointer to `validator` identifier. If a non-sequential key (`>type(uint64).max`) is used as an ERC-4337 Entry Point `UserOperation` (userOp) `nonce`, the `validateUserOp` function in the `sender` contract account MUST extract the validator identifier, this MAY be the address itself or a pointer to the validator address in storage. Once the validator contract address is extracted, the proposed contract account (henceforth, shall be referred to as MADV account) MUST forward the userOp calldata to the validator. This calldata SHOULD be the entire userOp. In response to this delegated validation, the validator contract MUST return the ERC-4337 `validationData`, and the MADV `sender` account MUST return this as the `validationData` to the Entry Point. + +In all of the above validation steps, the validator contract MUST respect the ERC-4337 Entry Point conventions. Note, that while validator key data might be included elsewhere in a `UserOperation` to achieve similar contract account modularity, for example, by packing this data into the `signature` field, this proposal opts to repurpose `nonce` for this pointer to minimize calldata costs and to benefit from the EntryPoint's `getNonce` accounting, as well as the discoverability of user plugin interactions in the `UserOperationEvent` which exposes `nonce` but not other userOp data. + +### ERC-4337 references: + +`PackedUserOperation` interface + +```solidity +/** + * User Operation struct + * @param sender - The sender account of this request. + * @param nonce - Unique value the sender uses to verify it is not a replay. In MADV, the validator identifier is encoded in the high 192 bit (`key`) of the nonce value + * @param initCode - If set, the account contract will be created by this constructor/ + * @param callData - The method call to execute on this account. + * @param accountGasLimits - Packed gas limits for validateUserOp and gas limit passed to the callData method call. + * @param preVerificationGas - Gas not calculated by the handleOps method, but added to the gas paid. + * Covers batch overhead. + * @param gasFees - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters. + * @param paymasterAndData - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data + * The paymaster will pay for the transaction instead of the sender. + * @param signature - Sender-verified signature over the entire request, the EntryPoint address and the chain ID. + */ +struct PackedUserOperation { + address sender; + uint256 nonce; + bytes initCode; + bytes callData; + bytes32 accountGasLimits; + uint256 preVerificationGas; + bytes32 gasFees; + bytes paymasterAndData; + bytes signature; +} +``` + +`IAccount` interface + +```solidity +interface IAccount { + /** + * Validate user's signature and nonce + * the entryPoint will make the call to the recipient only if this validation call returns successfully. + * signature failure should be reported by returning SIG_VALIDATION_FAILED (1). + * This allows making a "simulation call" without a valid signature + * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure. + * + * @dev Must validate caller is the entryPoint. + * Must validate the signature and nonce + * @param userOp - The operation that is about to be executed. + * @param userOpHash - Hash of the user's request data. can be used as the basis for signature. + * @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint. + * This is the minimum amount to transfer to the sender(entryPoint) to be + * able to make the call. The excess is left as a deposit in the entrypoint + * for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()". + * In case there is a paymaster in the request (or the current deposit is high + * enough), this value will be zero. + * @return validationData - Packaged ValidationData structure. use `_packValidationData` and + * `_unpackValidationData` to encode and decode. + * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, + * otherwise, an address of an "authorizer" contract. + * <6-byte> validUntil - Last timestamp this operation is valid. 0 for "indefinite" + * <6-byte> validAfter - First timestamp this operation is valid + * If an account doesn't use time-range, it is enough to + * return SIG_VALIDATION_FAILED value (1) for signature failure. + * Note that the validation code cannot use block.timestamp (or block.number) directly. + */ + function validateUserOp( + PackedUserOperation calldata userOp, + bytes32 userOpHash, + uint256 missingAccountFunds + ) external returns (uint256 validationData); +} +``` + +`NonceManager` interface + +```solidity + /** + * Return the next nonce for this sender. + * Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop) + * But UserOp with different keys can come with arbitrary order. + * + * @param sender the account address + * @param key the high 192 bit of the nonce, in MADV the validator identifier is encoded here + * @return nonce a full nonce to pass for next UserOp with this sender. + */ + function getNonce(address sender, uint192 key) + external view returns (uint256 nonce); +``` + +`UserOperationEvent` + +```solidity +/*** + * An event emitted after each successful request + * @param userOpHash - unique identifier for the request (hash its entire content, except signature). + * @param sender - the account that generates this request. + * @param paymaster - if non-null, the paymaster that pays for this request. + * @param nonce - the nonce value from the request. + * @param success - true if the sender transaction succeeded, false if reverted. + * @param actualGasCost - actual amount paid (by account or paymaster) for this UserOperation. + * @param actualGasUsed - total gas used by this UserOperation (including preVerification, creation, validation and execution). + */ + event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed); +``` + +## Rationale + +This proposal is designed to be a minimalist extension to ERC-4337 that allows for additional functionality without requiring changes to the existing interface. Keeping the proposal's footprint small. + +Further, by repurposing the nonce field for the validator identifier we minimize calldata costs and leverage existing `getNonce` accounting. The `UserOperationEvent` emits nonce which can be used for tracking validator invocations without additional events. Other options like packing the validator identifier into the `signature` field were considered but were rejected due to potential for conflict with other signatures schemes and increased opaqueness into validator invocation. + +This proposal allows for MADV accounts to specify their own method for extracting the validator address from the `nonce`. This provides flexibility to account developers and supports both "just in time" validators as well as a more predictable storage pattern for plugin reuse. + +The requirement is simply to use `nonce` for encoding an identifier and to return the `validationData` from the extracted validator contract to the `EntryPoint` in line with the requirements of the ERC-4337 `validateUserOp` function. + +## Backwards Compatibility + +No backward compatibility issues found. + +## Reference Implementation + +See the [MADV reference implementation](../assets/eip-7582/MADVAccount.sol) for a simple example of how to implement this proposal. + +## Security Considerations + +As this proposal introduces no new functions and leaves implementation of the validator extraction method and approval logic open to developers, the surface for security issues is intentionally kept small. Nevertheless, specific validator use cases require further discussion and consideration of the overall ERC-4337 verification flow and its underlying security. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/assets/erc-7582/MADVAccount.sol b/assets/erc-7582/MADVAccount.sol new file mode 100644 index 0000000000..bc899d580b --- /dev/null +++ b/assets/erc-7582/MADVAccount.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.8.19; + +import {SimpleAccount} from '@aa/samples/SimpleAccount.sol'; + +/// @notice MADVAccount extends ERC4337 accounts by specifying a plugin scheme for user operations. +contract MADVAccount is SimpleAccount { + function validateUserOp( + UserOperation calldata userOp, + bytes32 userOpHash, + uint256 missingAccountFunds + ) external virtual override(BaseAccount) returns (uint256 validationData) { + _requireFromEntryPoint(); + if (userOp.nonce > type(uint64).max) { + bytes32 validatorHash = bytes32(userOp.nonce >> 64); + validationData = _validateSignature(userOp, validatorHash); + if (validationData == SIGNATURE_VALIDATION_FAILED) { + return SIGNATURE_VALIDATION_FAILED; + } + MADVAccount validator = MADVAccount(address(uint160(uint256(validatorHash)))); + validationData = validator.validateUserOp(userOp, userOpHash, missingAccountFunds); + } else { + validationData = _validateSignature(userOp, userOpHash); + } + _payPrefund(missingAccountFunds); + } +} \ No newline at end of file diff --git a/assets/erc-7582/base-flow.svg b/assets/erc-7582/base-flow.svg new file mode 100644 index 0000000000..cfcd582e51 --- /dev/null +++ b/assets/erc-7582/base-flow.svg @@ -0,0 +1,29 @@ + + + + + + + + EntryPointcalls `validateUserOp`Check whether `nonce` uses non-sequential keysequential keycheck signaturenon-sequential keyforward calldata if a `validator`corresponding to key existsreturn `validationData` From 128b7de2581d0ac0d6049b14684926a08bf1f6d1 Mon Sep 17 00:00:00 2001 From: sshmatrix <19473027+sshmatrix@users.noreply.github.com> Date: Fri, 14 Jun 2024 21:23:13 +0530 Subject: [PATCH 030/126] Add ERC: Cross-chain Storage Router Protocol Merged by EIP-Bot. --- ERCS/erc-7700.md | 507 +++++++++ assets/erc-7700/images/Database.svg | 540 +++++++++ assets/erc-7700/images/Keygen.svg | 1229 ++++++++++++++++++++ assets/erc-7700/images/L1.svg | 527 +++++++++ assets/erc-7700/images/L2.svg | 527 +++++++++ assets/erc-7700/images/Schema.svg | 1638 +++++++++++++++++++++++++++ 6 files changed, 4968 insertions(+) create mode 100644 ERCS/erc-7700.md create mode 100644 assets/erc-7700/images/Database.svg create mode 100644 assets/erc-7700/images/Keygen.svg create mode 100644 assets/erc-7700/images/L1.svg create mode 100644 assets/erc-7700/images/L2.svg create mode 100644 assets/erc-7700/images/Schema.svg diff --git a/ERCS/erc-7700.md b/ERCS/erc-7700.md new file mode 100644 index 0000000000..2f34401934 --- /dev/null +++ b/ERCS/erc-7700.md @@ -0,0 +1,507 @@ +--- +eip: 7700 +title: Cross-chain Storage Router Protocol +description: The cross-chain storage router protocol provides a mechanism to replace L1 storage with L2 and databases through off-chain routers +author: Avneet Singh (@sshmatrix), 0xc0de4c0ffee (@0xc0de4c0ffee), Nick Johnson (@arachnid), Makoto Inoue (@makoto) +discussions-to: https://ethereum-magicians.org/t/erc-7700-cross-chain-storage-router-protocol/19853 +status: Draft +type: Standards Track +category: ERC +created: 2024-04-30 +requires: 155 +--- + +## Abstract +The following standard provides a mechanism by which smart contracts can route storage to external providers. In particular, protocols can reduce the gas fees associated with storing data on mainnet by routing the handling of storage operations to another system or network. These storage routers act as an extension to the core L1 contract. Methods in this document specifically target security and cost-effectiveness of storage routing to three router types: L1, L2 and databases. The cross-chain data written with these methods can be retrieved by generic [EIP-3668](./eip-3668)-compliant contracts, thus completing the cross-chain data life cycle. This document, nicknamed CCIP-Store, alongside [EIP-3668](./eip-3668), is a meaningful step toward a secure infrastructure for cross-chain storage routers and data retrievals. + +## Motivation +[EIP-3668](./eip-3668), aka 'CCIP-Read', has been key to retrieving cross-chain data for a variety of contracts on Ethereum blockchain, ranging from price feeds for DeFi contracts, to more recently records for ENS users. The latter case dedicatedly uses cross-chain storage to bypass the usually high gas fees associated with on-chain storage; this aspect has a plethora of use cases well beyond ENS records and a potential for significant impact on universal affordability and accessibility of Ethereum. + +Cross-chain data retrieval through [EIP-3668](./eip-3668) is a relatively simpler task since it assumes that all relevant data originating from cross-chain storages is translated by CCIP-Read-compliant HTTP gateways; this includes L2 chains and databases. On the flip side however, so far each service leveraging CCIP-Read must handle writing this data securely to these storage types on their own, while also incorporating reasonable security measures in their CCIP-Read-compatible contracts for verifying this data on L1. While these security measures are in-built into L2 architectures, database storage providers on the other hand must incorporate some form of explicit security measures during storage operations so that cross-chain data's integrity can be verified by CCIP-Read contracts during data retrieval stage. Examples of this include: + +- Services that allow the management of namespaces, e.g. ENS domains, stored externally on an L2 solution or off-chain database as if they were native L1 tokens, and, +- Services that allow the management of digital identities stored on external storages as if they were stored in the native L1 smart contract. + +In this context, a specification which allows storage routing to external routers will facilitate creation of services that are agnostic to the underlying storage solution. This in turn enables new applications to operate without knowledge of the underlying routers. This 'CCIP-Store' proposal outlines precisely this part of the process, i.e. how the bespoke storage routing can be made by smart contracts to L2s and databases. + +![Fig.1 CCIP-Store and CCIP-Read Workflows](../assets/eip-7700/images/Schema.svg) + +## Specification +### Overview +The following specification revolves around the structure and description of a cross-chain storage router tasked with the responsibility of writing to an L2 or database storage. This document introduces `StorageRoutedToL2()` and `StorageRoutedToDatabase()` storage routers, and proposes that new `StorageRoutedTo__()` reverts be allowed through new EIPs that sufficiently detail their interfaces and designs. Some foreseen examples of new storage routers include `StorageRoutedToSolana()` for Solana, `StorageRoutedToFilecoin()` for Filecoin, `StorageRoutedToIPFS()` for IPFS, `StorageRoutedToIPNS()` for IPNS, `StorageRoutedToArweave()` for Arweave, `StorageRoutedToArNS()` for ArNS, `StorageRoutedToSwarm()` for Swarm etc. + +### L1 Router: `StorageRoutedToL1()` +A minimal L1 router only requires the `contractL1` address to which routing must be made, while the clients must ensure that the calldata is invariant under routing to another contract. One example implementation of an L1 router is given below. + +```solidity +// Define revert event +error StorageRoutedToL1( + address contractL1 +); + +// Generic function in a contract +function setValue( + bytes32 node, + bytes32 key, + bytes32 value +) external { + // Get metadata from on-chain sources + ( + address contractL1, // Routed contract address on L1; may be globally constant + ) = getMetadata(node); // Arbitrary code + // contractL1 = 0x32f94e75cde5fa48b6469323742e6004d701409b + // Route storage call to L1 router + revert StorageRoutedToL1( + contractL1 + ); +}; +``` + +In this example, the routing must prompt the client to build the transaction with the exact same original calldata, and submit it to the `contractL1` by calling the exact same function. + +```solidity +// Function in router L1 contract +function setValue( + bytes32 node, + bytes32 key, + bytes32 value +) external { + // Some code storing data mapped by node & msg.sender + ... +} +``` + +![Fig.2 L1 Call Lifecycle](../assets/eip-7700/images/L1.svg) + +### L2 Router: `StorageRoutedToL2()` +A minimal L2 router only requires the list of `chainId` values and the corresponding `contract` addresses, while the clients must ensure that the calldata is invariant under routing to L2. One example implementation of an L2 router in an L1 contract is shown below. + +```solidity +// Define revert event +error StorageRoutedToL2( + address contractL2, + uint256 chainId +); + +// Generic function in a contract +function setValue( + bytes32 node, + bytes32 key, + bytes32 value +) external { + // Get metadata from on-chain sources + ( + address contractL2, // Contract address on L2; may be globally constant + uint256 chainId // L2 ChainID; may be globally constant + ) = getMetadata(node); // Arbitrary code + // contractL2 = 0x32f94e75cde5fa48b6469323742e6004d701409b + // chainId = 21 + // Route storage call to L2 router + revert StorageRoutedToL2( + contractL2, + chainId + ); +}; +``` + +In this example, the routing must prompt the client to build the transaction with the exact same original calldata, and submit it to the L2 by calling the exact same function on L2 as L1. + +```solidity +// Function in L2 contract +function setValue( + bytes32 node, + bytes32 key, + bytes32 value +) external { + // Some code storing data mapped by node & msg.sender + ... +} +``` + +![Fig.3 L2 Call Lifecycle](../assets/eip-7700/images/L2.svg) + +### Database Router: `StorageRoutedToDatabase()` +A minimal database router is similar to an L2 in the sense that: + + a) Similar to `chainId`, it requires the `gatewayUrl` that is tasked with handling off-chain storage operations, and + + b) Similar to `eth_call`, it requires `eth_sign` output to secure the data, and the client must prompt the users for these signatures. + +This specification does not require any other data to be stored on L1 other than the bespoke `gatewayUrl`; the storage router therefore should only return the `gatewayUrl` in revert. + +```solidity +error StorageRoutedToDatabase( + string gatewayUrl +); + +// Generic function in a contract +function setValue( + bytes32 node, + bytes32 key, + bytes32 value +) external { + ( + string gatewayUrl // Gateway URL; may be globally constant + ) = getMetadata(node); + // gatewayUrl = "https://api.namesys.xyz" + // Route storage call to database router + revert StorageRoutedToDatabase( + gatewayUrl + ); +}; +``` + +![Fig.4 Database Call Lifecycle](../assets/eip-7700/images/Database.svg) + +Following the revert, the client must take these steps: + +1. Request the user for a secret signature `sigKeygen` to generate a deterministic `dataSigner` keypair, + +2. Sign the calldata with generated data signer's private key and produce verifiable data signature `dataSig`, + +3. Request the user for an `approval` approving the generated data signer, and finally, + +4. Post the calldata to gateway along with signatures `dataSig` and `approval`, and the `dataSigner`. + +These steps are described in detail below. + +#### 1. Generate Data Signer +The data signer must be generated deterministically from ethereum wallet signatures; see figure below. + +![Fig.5 Data Signer Keygen Workflow](../assets/eip-7700/images/Keygen.svg) + +The deterministic key generation can be implemented concisely in a single unified `keygen()` function as follows. + +```js +/* Pseudo-code for key generation */ +function keygen( + username, // CAIP identifier for the blockchain account + sigKeygen, // Deterministic signature from wallet + spice // Stretched password +) { + // Calculate input key by hashing signature bytes using SHA256 algorithm + let inputKey = sha256(sigKeygen); + // Calculate salt for keygen by hashing concatenated username, stretched password (aka spice) and hex-encoded signature using SHA256 algorithm + let salt = sha256(`${username}:${spice}:${sigKeygen}`); + // Calculate hash key output by feeding input key, salt & username to the HMAC-based key derivation function (HKDF) with dLen = 42 + let hashKey = hkdf(sha256, inputKey, salt, username, 42); + // Calculate and return secp256k1 keypair + return secp256k1(hashKey); // Calculate secp256k1 keypair from hash key +} +``` + +This `keygen()` function requires three variables: `username`, `spice` and `sigKeygen`. Their definitions are given below. + +##### 1. `username` +[CAIP-10](https://github.com/ChainAgnostic/CAIPs/blob/ad0cfebc45a4b8368628340bf22aefb2a5edcab7/CAIPs/caip-10.md) identifier `username` is auto-derived from the connected wallet's checksummed address `wallet` and `chainId` using [EIP-155](./eip-155). + +```js +/* CAIP-10 identifier */ +const caip10 = `eip155:${chainId}:${wallet}`; +``` + +##### 2. `spice` +`spice` is calculated from the optional private field `password`, which must be prompted from the user by the client; this field allows users to change data signers for a given `username`. +```js +/* Secret derived key identifier */ +// Clients must prompt the user for this +const password = 'key1'; +``` + +Password must then be stretched before use with `PBKDF2` algorithm such that: + +```js +/* Calculate spice by stretching password */ +let spice = pbkdf2( + password, + pepper, + iterations + ); // Stretch password with PBKDF2 +``` + +where `pepper = keccak256(abi.encodePacked(username))` and the `iterations` count is fixed to `500,000` for brute-force vulnerability protection. + +```js +/* Definitions of pepper and iterations in PBKDF2 */ +let pepper = keccak256(abi.encodePacked(username)); +let iterations = 500000; // 500,000 iterations +``` + +##### 3. `sigKeygen` +The data signer must be derived from the owner or manager keys of a node. Message payload for the required `sigKeygen` must then be formatted as: + +```text +Requesting Signature To Generate Keypair(s)\n\nOrigin: ${username}\nProtocol: ${protocol}\nExtradata: ${extradata} +``` + +where the `extradata` is calculated as follows, + +```solidity +// Calculating extradata in keygen signatures +bytes32 extradata = keccak256( + abi.encodePacked( + spice + wallet + ) +) +``` + +The remaining `protocol` field is a protocol-specific identifier limiting the scope to a specific protocol represented by a unique contract address. This identifier cannot be global and must be uniquely defined for each implementating `contract` such that: + +```js +/* Protocol identifier in CAIP-10 format */ +const protocol = `eth:${chainId}:${contract}`; +``` + +With this deterministic format for signature message payload, the client must prompt the user for the ethereum signature. Once the user signs the messages, the `keygen()` function can derive the data signer keypair. + +#### 2. Sign Data +Since the derived signer is wallet-specific, it can + +- sign batch data for multiple keys for a given node, and +- sign batches of data for multiple nodes owned by a wallet + +simultaneously in the background without ever prompting the user. Signature(s) `dataSig` accompanying the off-chain calldata must implement the following format in their message payloads: + +```text +Requesting Signature To Update Off-Chain Data\n\nOrigin: ${username}\nData Type: ${dataType}\nData Value: ${dataValue} +``` + +where `dataType` parameters are protocol-specific and formatted as object keys delimited by `/`. For instance, if the off-chain data is nested in keys as `a > b > c > field > key`, then the equivalent `dataType` is `a/b/c/field/key`. For example, in order to update off-chain ENS record `text > avatar` and `address > 60`, `dataType` must be formatted as `text/avatar` and `address/60` respectively. + +#### 3. Approve Data Signer +The `dataSigner` is not stored on L1, and the clients must instead + +- request an `approval` signature for `dataSigner` signed by the owner or manager of a node, and +- post this `approval` and the `dataSigner` along with the signed calldata in encoded form. + +CCIP-Read-enabled contracts can then verify during resolution time that the `approval` attached with the signed calldata comes from the node's manager or owner, and that it approves the expected `dataSigner`. The `approval` signature must have the following message payload format: + +```text +Requesting Signature To Approve Data Signer\n\nOrigin: ${username}\nApproved Signer: ${dataSigner}\nApproved By: ${caip10} +``` + +where `dataSigner` must be checksummed. + +#### 4. Post CCIP-Read Compatible Payload +The final [EIP-3668](./eip-3668)-compatible `data` payload in the off-chain data file is identified by a fixed `callback.signedData.selector` equal to `0x2b45eb2b` and must follow the format + +```solidity +/* Compile CCIP-Read-compatible payload*/ +bytes encodedData = abi.encode(['bytes'], [dataValue]); // Encode data +bytes funcSelector = callback.signedData.selector; // Identify off-chain data with a fixed 'signedData' selector = '0x2b45eb2b' +bytes data = abi.encode( + ['bytes4', 'address', 'bytes32', 'bytes32', 'bytes'], + [funcSelector, dataSigner, dataSig, approval, encodedData] +); // Compile complete CCIP-Readable off-chain data +``` + +The client must construct this `data` and pass it to the gateway in the `POST` request along with the raw values for indexing. The CCIP-Read-enabled contracts after decoding the four parameters from this `data` must + +- verify that the `dataSigner` is approved by the owner or manager of the node through `approval`, and +- verify that the `dataSig` is produced by `dataSigner` + +before resolving the `encodedData` value in decoded form. + +##### `POST` Request +The `POST` request made by the client to the `gatewayUrl` must follow the format as described below. + +```ts +/* POST request format*/ +type Post = { + node: string + preimage: string + chainId: number + approval: string + payload: { + field1: { + value: string + signature: string + timestamp: number + data: string + } + field2: [ + { + index: number + value: string + signature: string + timestamp: number + data: string + } + ] + field3: [ + { + key: number + value: string + signature: string + timestamp: number + data: string + } + ] + } +} +``` + +Example of a complete `Post` typed object for updating multiple ENS records for a node is shown below. + +```ts +/* Example of a POST request */ +let post: Post = { + "node": "0xe4ad669b4f80715d286697885cfdf2552d3042e07717247924532662b1eda1da", + "preimage": "sub.domain.eth", + "chainId": 1, + "approval" : "0x1cc5e5efa312dc292560a26e3dba2584070b02ec203c51440a3e23d49ba56b342a4404d8b0d9dc26a94190691e47652343183bf1c64bf9c5081a2f1d887937f11b", + "payload" : { + "contenthash": { + "value" : "ipfs://QmRAQB6YaCyidP37UdDnjFY5vQuiBrcqdyoW1CuDgwxkD4", + "signature": "0x0679eaedb300308680a0e8c11725e891d1500fb98b65d6d09d538e2655567fdf06b989689a01db312ad6df0752cbcb1756b3405a7163f8b4b7c01e70b1a9c5c31c", + "timestamp": 1708322868, + "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec800000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000414abb7b2b9fc395910b4387ff69897ee639fe1cf9b79c31bf2d3743134e77a9b222ec175e563d13d60bc722c8829ce91d9af51bcd949816f95979abef4378d84e1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000026e3010170122022380b0884d9e85ef3ff5f71ea7a25874738da71f38b999dc8ffec2f6389a3670000000000000000000000000000000000000000000000000000" + }, + "address": [ + { + "coinType": 0, + "value": "1FfmbHfnpaZjKFvyi1okTjJJusN455paPH", + "signature": "0x60ecd4979ae2c39399ffc7ad361066d46fc3d20f2b2902c52e01549a1f6912643c21d23d1ad817507413dc8b73b59548840cada57481eb55332c4327a5086a501b", + "timestamp": 1708322877, + "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec800000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000419c7c185335898d7ec57cffb842e88116a82f367237815f35e16d5f8b28dc3e7b0f0b40edd9f9fc48f771f921986c45973f4c2a82e8c2ebe1732a9f552f8b033a1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001111000000000000000000000000000000000001" + }, + { + "coinType": 60, + "value": "0x839B3B540A9572448FD1B2335e0EB09Ac1A02885", + "signature": "0xaad74ddef8c031131b6b83b3bf46749701ed11aeb585b63b72246c8dab4fff4f79ef23aea5f62b227092719f72f7cfe04f3c97bfad0229c19413f5cb491e966c1b", + "timestamp": 1708322917, + "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec800000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000419bb4494a9ac6b37d5d979cbb6c43cccbbd8790ebbd8f898d8427e1ebfd8bb8bd29a2fbc2b20b0a53c3fdde9dd8ce3df648112754742156d3a5ac6fd1b80d8bd01b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c68747470733a2f2f6e616d657379732e78797a2f6c6f676f2e706e6700000000" + } + ], + "text": [ + { + "key": "avatar", + "value": "https://domain.com/avatar", + "signature": "0xbc3c7f1b511de151bffe8df033859295d83d400413996789e706e222055a2353404ce17027760c927af99e0bf621bfb24d3bfc52abb36bcfbe6e20cf43db7c561b", + "timestamp": 1708329377, + "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000041dc6ca55c1d1c75eec223a7eb01eb5942a2bdb79708c25ff2827cfc0343f97fb76faefd9fbc40de5103956bbdc841f2cc2d53630cd2836a6b76d8d2c107ccadd21b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046e6e6e6e00000000000000000000000000000000000000000000000000000000" + }, + { + "key": "com.github", + "value": "namesys-eth", + "signature": "0xc9c33ff219e90510f79b6c9bb489917ee6e00ab123c55abe1117e71ea0d171356cf316420c71cfcf4bd63a791aaf37388ef1832e582f54a8c2df173917240fff1b", + "timestamp": 1708322898, + "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000041bfd0ab74712b98bc472ef0e5bbb031acba077fc98a54cdfcb3f11e64b02d7fe21477ba5ea9d508a0265616d74a8df99b9c8f3c04e6bfd41f2df554fe11e1fe141c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046b6b6b6b00000000000000000000000000000000000000000000000000000000" + } + ] + } +} +``` + +### New Revert Events +1. Each new storage router must submit their `StorageRoutedTo__()` identifier through an ERC track proposal referencing the current document. + +2. Each `StorageRoutedTo__()` provider must be supported with detailed documentation of its structure and the necessary metadata that its implementers must return. + +3. Each `StorageRoutedTo__()` proposal must define the precise formatting of any message payloads that require signatures and complete descriptions of custom cryptographic techniques implemented for additional security, accessibility or privacy. + +### Implementation featuring ENS on L2 & Database +ENS off-chain resolvers capable of reading from and writing to databases are perhaps the most common use-case for CCIP-Read and CCIP-Write. One example of such a (minimal) resolver is given below along with the client-side code for handling the storage router revert. + +#### L1 Contract +```solidity +/* ENS resolver implementing StorageRoutedToDatabase() */ +interface iResolver { + // Defined in EIP-7700 + error StorageRoutedToL2( + uint chainId, + address contractL2 + ); + error StorageRoutedToDatabase( + string gatewayUrl + ); + // Defined in EIP-137 + function setAddr(bytes32 node, address addr) external; +} + +// Defined in EIP-7700 +string public gatewayUrl = "https://post.namesys.xyz"; // RESTful API endpoint +uint256 public chainId = uint(21); // ChainID of L2 +address public contractL2 = "0x839B3B540A9572448FD1B2335e0EB09Ac1A02885"; // Contract on L2 + +/** +* Sets the ethereum address associated with an ENS node +* [!] May only be called by the owner or manager of that node in ENS registry +* @param node Namehash of ENS domain to update +* @param addr Ethereum address to set +*/ +function setAddr( + bytes32 node, + address addr +) authorised(node) { + // Route to database storage + revert StorageRoutedToDatabase( + gatewayUrl + ); +} + +/** +* Sets the avatar text record associated with an ENS node +* [!] May only be called by the owner or manager of that node in ENS registry +* @param node Namehash of ENS domain to update +* @param key Key for ENS text record +* @param value URL to avatar +*/ +function setText( + bytes32 node, + string key, + string value +) external { + // Verify owner or manager permissions + require(authorised(node), "NOT_ALLOWED"); + // Route to L2 storage + revert StorageRoutedToL2( + chainId, + contractL2 + ); +} +``` + +#### L2 Contract +```solidity +// Function in L2 contract +function setText( + bytes32 node, + bytes32 key, + bytes32 value +) external { + // Store record mapped by node & sender + records[keccak256(abi.encodePacked(node, msg.sender))]["text"][key] = value; +} +``` + +#### Client-side Code +```ts +/* Client-side pseudo-code in ENS App */ +// Deterministically generate signer keypair +let signer = keygen(username, sigKeygen, spice); +// Construct POST body by signing calldata with derived private key +let post: Post = signData(node, addr, signer.priv); +// POST to gateway +await fetch(gatewayUrl, { + method: "POST", + body: JSON.stringify(post) +}); +``` + +## Rationale +Technically, the cases of L2s and databases are similar; routing to an L2 involves routing the `eth_call` to another EVM, while routing to a database can be made by extracting `eth_sign` from `eth_call` and posting the resulting signature explicitly along with the data for later verification. Methods in this document perform these precise tasks when routing storage operations to external routers. In addition, methods such as signing data with a derived signer (for databases) allow for significant UX improvement by fixing the number of signature prompts in wallets to 2, irrespective of the number of data instances to sign per node or the total number of nodes to update. This improvement comes at no additional cost to the user and allows services to perform batch updates. + +## Backwards Compatibility +None + +## Security Considerations +1. Clients must purge the derived signer private keys from local storage immediately after signing the off-chain data. + +2. Signature message payload and the resulting deterministic signature `sigKeygen` must be treated as a secret by the clients and immediately purged from local storage after usage in the `keygen()` function. + +3. Clients must immediately purge the `password` and `spice` from local storage after usage in the `keygen()` function. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/assets/erc-7700/images/Database.svg b/assets/erc-7700/images/Database.svg new file mode 100644 index 0000000000..f00d57d15e --- /dev/null +++ b/assets/erc-7700/images/Database.svg @@ -0,0 +1,540 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HTTP POST [signer] [signature] [approval] + [callData] + + + setValue(calldata) + + + + + + L1 + + + + DB + + + CONTRACT 1 + + + + + + + + + + + + + + + + revert StorageRoutedToDatabase(gatewayURL) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + response + + + + CLIENT + + DATABASE + + diff --git a/assets/erc-7700/images/Keygen.svg b/assets/erc-7700/images/Keygen.svg new file mode 100644 index 0000000000..3de8e8237c --- /dev/null +++ b/assets/erc-7700/images/Keygen.svg @@ -0,0 +1,1229 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + keypairdecode + + + + + + + + + + + CAIP-10 + pubkey[address] + + + + + + + MESSAGE + + + + + + + + + + + + + + + + + + + + signature + + + + salt + + + + + + + + + + + + wallet keypair + + + + + + input key + + + + + + + hashkey + + + + + + + + + USERNAME + + + + + + + password + + + + + + + + + signerkeys + + + CAIP-02 + + + + + + + + + + + + + + otherkeys + + + + + + + + + + + + + + + + + + + + + + + hkdf + + + + + + + + + + + + sha- + 256 + + sign + + + + + + + + + + + + + + + + + + + + + + + + + + PROTOCOL + + + + + + + + PBKDf + 2 + + + + diff --git a/assets/erc-7700/images/L1.svg b/assets/erc-7700/images/L1.svg new file mode 100644 index 0000000000..4e6b2a9495 --- /dev/null +++ b/assets/erc-7700/images/L1.svg @@ -0,0 +1,527 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Execute on Mainnet [contract] [callData] + + + setValue(calldata) + + + + + + L1 + + + + L1 + + + CONTRACT 1 + CONTRACT 2 + + + + + + + + + + + + + + + + revert StorageRoutedToL1(contract) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + response + + + + CLIENT + + + diff --git a/assets/erc-7700/images/L2.svg b/assets/erc-7700/images/L2.svg new file mode 100644 index 0000000000..6906ecc807 --- /dev/null +++ b/assets/erc-7700/images/L2.svg @@ -0,0 +1,527 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Execute on L2 [contract] [callData] + + + setValue(calldata) + + + + + + L1 + + + + L2 + + + CONTRACT 1 + CONTRACT 2 + + + + + + + + + + + + + + + + revert StorageRoutedToL2(chainId, contract) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + response + + + + CLIENT + + + diff --git a/assets/erc-7700/images/Schema.svg b/assets/erc-7700/images/Schema.svg new file mode 100644 index 0000000000..c201698411 --- /dev/null +++ b/assets/erc-7700/images/Schema.svg @@ -0,0 +1,1638 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ns1 + ns2 + ns3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OffchainLookup(sender urls[] calldata callback() extradata ) + callback() + + + + extradata + + + + + + + + + Read + return + gateway + result + + + + EIP-3668 + EIP-7700 + + + + + L2 + database + IPNS + ar + SOLANA + IPFS + IPNS + storage + + + + + + ns1 + ns2 + ns3 + + + + + + + + + + + + + + + + + + + + L2 + IPFS + DATABASE + ARWEAVE + SOLANA + + + + + + + + + STORE + gateway + json-rpc + http + ipfs2 + ar-io + arns + arns + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + requires CID/keygen + + requires signature + + + + + + + + + + requires $ payment + + + + + API + metadata + + + + + From 878ef7694cac391d6c06d22fb5f6c4fc59aa21c4 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Fri, 14 Jun 2024 21:59:21 +0200 Subject: [PATCH 031/126] Update ERC-7540: Update ERC7540 Merged by EIP-Bot. --- ERCS/erc-7540.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ERCS/erc-7540.md b/ERCS/erc-7540.md index f9786b9065..3a8a6d4bea 100644 --- a/ERCS/erc-7540.md +++ b/ERCS/erc-7540.md @@ -85,9 +85,7 @@ The request ID (`requestId`) of a request is returned by the corresponding `requ Multiple requests may have the same `requestId`, so a given Request is discriminated by both the `requestId` and the `owner`. -Requests of the same `requestId` MUST be fungible with each other (except in the special case `requestId == 0` described below). I.e. all Requests with the same `requestId` MUST transition from Pending to Claimable at the same time and receive the same exchange rate between `assets` and `shares`. - -If a Request becomes partially claimable, all requests of the same `requestId` MUST become claimable at the same pro-rata rate. +Requests of the same `requestId` MUST be fungible with each other (except in the special case `requestId == 0` described below). I.e. all Requests with the same `requestId` MUST transition from Pending to Claimable at the same time and receive the same exchange rate between `assets` and `shares`. If a Request with `requestId != 0` becomes partially claimable, all requests of the same `requestId` MUST become claimable at the same pro-rata rate. There are no assumptions or requirements of requests with different `requestId`. I.e. they MAY transition to Claimable at different times and exchange rates with no ordering or correlation enforced in any way. From 626727ffa0f151e38d3cc6c2bad70f673957fb73 Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Mon, 17 Jun 2024 12:13:55 -0400 Subject: [PATCH 032/126] Update ERC-7677: add isFinal flag, move sponsor, and specify validation expectations Merged by EIP-Bot. --- ERCS/erc-7677.md | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/ERCS/erc-7677.md b/ERCS/erc-7677.md index ff3e0b818d..e888183b4b 100644 --- a/ERCS/erc-7677.md +++ b/ERCS/erc-7677.md @@ -33,6 +33,12 @@ Returns stub values to be used in paymaster-related fields of an unsigned user o This method MAY return paymaster-specific gas values if applicable to the provided EntryPoint version. For example, if provided with EntryPoint v0.7, this method MAY return `paymasterVerificationGasLimit` and `paymasterPostOpGasLimit` values. The wallet SHOULD use these provided gas values when submitting the `UserOperation` to a bundler for gas estimation. +This method MAY also return a `sponsor` object with a `name` field and an optional `icon` field. The `name` field is the name of the party sponsoring the transaction, and the `icon` field is a URI pointing to an image. Wallet developers MAY choose to display sponsor information to users. The `icon` string MUST be a data URI as defined in [RFC-2397]. The image SHOULD be a square with 96x96px minimum resolution. The image format is RECOMMENDED to be either lossless or vector based such as PNG, WebP or SVG to make the image easy to render on the wallet. Since SVG images can execute Javascript, wallets MUST render SVG images using the `` tag to ensure no untrusted Javascript execution can occur. + +There are cases where a call to just `pm_getPaymasterStubData` is sufficient to provide valid paymaster-related user operation fields, e.g. when the `paymasterData` does not contain a signature. In such cases, the second RPC call to `pm_getPaymasterData` (defined below) MAY be skipped, by returning `isFinal: true` by this RPC call. + +Paymaster web services SHOULD do validations on incoming user operations during `pm_getPaymasterStubData` execution and reject the request at this stage if it would not sponsor the operation. + ##### `pm_getPaymasterStubData` RPC Specification Note that the user operation parameter does not include a signature, as the user signs after all other fields are populated. @@ -58,11 +64,13 @@ type GetPaymasterStubDataParams = [ ]; type GetPaymasterStubDataResult = { + sponsor?: { name: string; icon?: string } // Sponsor info paymaster?: string // Paymaster address (entrypoint v0.7) paymasterData?: string; // Paymaster data (entrypoint v0.7) paymasterVerificationGasLimit?: `0x${string}`; // Paymaster validation gas (entrypoint v0.7) paymasterPostOpGasLimit?: `0x${string}`; // Paymaster post-op gas (entrypoint v0.7) paymasterAndData?: string; // Paymaster and data (entrypoint v0.6) + isFinal?: boolean; // Indicates that the caller does not need to call pm_getPaymasterData } ``` @@ -98,6 +106,10 @@ For example, if using entrypoint v0.6: ```json { + "sponsor": { + "name": "My App", + "icon": "https://..." + }, "paymasterAndData": "0x..." } ``` @@ -106,6 +118,10 @@ If using entrypoint v0.7: ```json { + "sponsor": { + "name": "My App", + "icon": "https://..." + }, "paymaster": "0x...", "paymasterData": "0x..." } @@ -115,6 +131,10 @@ If using entrypoint v0.7, with paymaster gas estimates: ```json { + "sponsor": { + "name": "My App", + "icon": "https://..." + }, "paymaster": "0x...", "paymasterData": "0x...", "paymasterVerificationGasLimit": "0x...", @@ -122,12 +142,24 @@ If using entrypoint v0.7, with paymaster gas estimates: } ``` +Indicating that the caller does not need to make a `pm_getPaymasterData` RPC call: + +```json +{ + "sponsor": { + "name": "My App", + "icon": "https://..." + }, + "paymaster": "0x...", + "paymasterData": "0x...", + "isFinal": true +} +``` + #### `pm_getPaymasterData` Returns values to be used in paymaster-related fields of a signed user operation. These are not stub values and will be used during user operation submission to a bundler. Similar to `pm_getPaymasterStubData`, accepts an unsigned user operation, entrypoint address, chain id, and a context object. -This method also returns a `sponsor` object with `name` and `icon` fields. The `name` field is the name of the party sponsoring the transaction, and the `icon` field is a URI pointing to an image. Wallet developers MAY choose to display sponsor information to users. The `icon` string MUST be a data URI as defined in [RFC-2397]. The image SHOULD be a square with 96x96px minimum resolution. The image format is RECOMMENDED to be either lossless or vector based such as PNG, WebP or SVG to make the image easy to render on the wallet. Since SVG images can execute Javascript, wallets MUST render SVG images using the `` tag to ensure no untrusted Javascript execution can occur. - ##### `pm_getPaymasterData` RPC Specification Note that the user operation parameter does not include a signature, as the user signs after all other fields are populated. @@ -153,7 +185,6 @@ type GetPaymasterDataParams = [ ]; type GetPaymasterDataResult = { - sponsor?: { name: string; icon: string } // Sponsor info paymaster?: string // Paymaster address (entrypoint v0.7) paymasterData?: string; // Paymaster data (entrypoint v0.7) paymasterAndData?: string; // Paymaster and data (entrypoint v0.6) @@ -192,10 +223,6 @@ For example, if using entrypoint v0.6: ```json { - "sponsor": { - "name": "My App", - "icon": "https://..." - }, "paymasterAndData": "0x..." } ``` @@ -204,10 +231,6 @@ If using entrypoint v0.7: ```json { - "sponsor": { - "name": "My App", - "icon": "https://..." - }, "paymaster": "0x...", "paymasterData": "0x..." } From af421525559cca748fdc5a852f6de552264cb131 Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Mon, 17 Jun 2024 14:05:43 -0400 Subject: [PATCH 033/126] Update ERC-7677: Change pngs to svgs Merged by EIP-Bot. --- ERCS/erc-7677.md | 4 ++-- assets/erc-7677/0.png | Bin 153119 -> 0 bytes assets/erc-7677/0.svg | 1 + assets/erc-7677/1.png | Bin 149412 -> 0 bytes assets/erc-7677/1.svg | 1 + 5 files changed, 4 insertions(+), 2 deletions(-) delete mode 100644 assets/erc-7677/0.png create mode 100644 assets/erc-7677/0.svg delete mode 100644 assets/erc-7677/1.png create mode 100644 assets/erc-7677/1.svg diff --git a/ERCS/erc-7677.md b/ERCS/erc-7677.md index e888183b4b..8c155c41f4 100644 --- a/ERCS/erc-7677.md +++ b/ERCS/erc-7677.md @@ -320,7 +320,7 @@ type PaymasterServiceCapability = { Below is a diagram illustrating the full `wallet_sendCalls` flow, including how a wallet might implement the interaction. -![flow](../assets/eip-7677/0.png) +![flow](../assets/eip-7677/0.svg) ## Rationale @@ -364,7 +364,7 @@ Therefore, a paymaster service MUST also return a stub that can result in a simu The URLs paymaster service providers give to app developers commonly have API keys in them. App developers might not want to pass these API keys along to wallets. To remedy this, we recommend that app developers provide a URL to their app's backend, which can then proxy calls to paymaster services. Below is a modified diagram of what this flow might look like. -![flowWithAPI](../assets/eip-7677/0.png) +![flowWithAPI](../assets/eip-7677/1.svg) This flow would allow developers to keep their paymaster service API keys secret. Developers might also want to do additional simulation / validation in their backends to ensure they are sponsoring a transaction they want to sponsor. diff --git a/assets/erc-7677/0.png b/assets/erc-7677/0.png deleted file mode 100644 index 84860abe5ca79cac764dca2686987850cc925fa4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153119 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

uk2JCSPS^Gt59R%C$A$Az#` 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¥`t5gCHnj%q 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 Zg)o_i)egkJTw{PgX>o-&MWP>k{|);@Oz!{y diff --git a/assets/erc-7677/0.svg b/assets/erc-7677/0.svg new file mode 100644 index 0000000000..21ec9af5bd --- /dev/null +++ b/assets/erc-7677/0.svg @@ -0,0 +1 @@ +BundlerPaymaster ServiceWalletAppBundlerPaymaster ServiceWalletAppwallet_sendCalls({...url: "https://..."})pm_getPaymasterStubData(userOp)Stub paymaster valueseth_estimateUserOperationGas(userOpWithStubPaymasterValues)Gas estimatespm_getPaymasterData(userOp)Paymaster valueseth_sendUserOperation(userOp)userOp hashCalls identifier \ No newline at end of file diff --git a/assets/erc-7677/1.png b/assets/erc-7677/1.png deleted file mode 100644 index 860386e9c9aa46b990a606277c58d9f2cce867ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149412 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%0DAo;g^#kVIeDRj$6 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 T`4oJa1pKKeYCgm%SfT$1O`lCC diff --git a/assets/erc-7677/1.svg b/assets/erc-7677/1.svg new file mode 100644 index 0000000000..db61175b88 --- /dev/null +++ b/assets/erc-7677/1.svg @@ -0,0 +1 @@ +BundlerPaymaster Serviceapi.app.comWalletapp.comBundlerPaymaster Serviceapi.app.comWalletapp.comwallet_sendCalls({...url: "api.app.com"})pm_getPaymasterStubData(userOp)pm_getPaymasterStubData(userOp)Stub paymaster valuesStub paymaster valueseth_estimateUserOperationGas(userOpWithStubPaymasterValues)Gas estimatespm_getPaymasterData(userOp)pm_getPaymasterData(userOp)Paymaster valuesPaymaster valueseth_sendUserOperation(userOp)userOp hashCalls identifier \ No newline at end of file From ec746f23128bef69e307fde8ff9566485ff3d0e3 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Mon, 17 Jun 2024 22:07:38 +0200 Subject: [PATCH 034/126] Update ERC-7540: Fix ERC7540 typo Merged by EIP-Bot. --- ERCS/erc-7540.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7540.md b/ERCS/erc-7540.md index 3a8a6d4bea..c91f0c19da 100644 --- a/ERCS/erc-7540.md +++ b/ERCS/erc-7540.md @@ -83,7 +83,7 @@ For asynchronous Vaults, the exchange rate between `shares` and `assets` includi ### Request Ids The request ID (`requestId`) of a request is returned by the corresponding `requestDeposit` and `requestRedeem` functions. -Multiple requests may have the same `requestId`, so a given Request is discriminated by both the `requestId` and the `owner`. +Multiple requests may have the same `requestId`, so a given Request is discriminated by both the `requestId` and the `controller`. Requests of the same `requestId` MUST be fungible with each other (except in the special case `requestId == 0` described below). I.e. all Requests with the same `requestId` MUST transition from Pending to Claimable at the same time and receive the same exchange rate between `assets` and `shares`. If a Request with `requestId != 0` becomes partially claimable, all requests of the same `requestId` MUST become claimable at the same pro-rata rate. From 614b17881d3c9bacbb3fa879bd81a21089b9eb38 Mon Sep 17 00:00:00 2001 From: sshmatrix <19473027+sshmatrix@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:31:49 +0530 Subject: [PATCH 035/126] Update ERC-7700: Cross-chain Storage Router Protocol Merged by EIP-Bot. --- ERCS/erc-7700.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7700.md b/ERCS/erc-7700.md index 2f34401934..17af1dc8eb 100644 --- a/ERCS/erc-7700.md +++ b/ERCS/erc-7700.md @@ -1,7 +1,7 @@ --- eip: 7700 title: Cross-chain Storage Router Protocol -description: The cross-chain storage router protocol provides a mechanism to replace L1 storage with L2 and databases through off-chain routers +description: Provides a mechanism to replace L1 storage with L2 and databases through cross-chain routers author: Avneet Singh (@sshmatrix), 0xc0de4c0ffee (@0xc0de4c0ffee), Nick Johnson (@arachnid), Makoto Inoue (@makoto) discussions-to: https://ethereum-magicians.org/t/erc-7700-cross-chain-storage-router-protocol/19853 status: Draft From 67bc8cc5b8fb1fcd6afcac729d8fc8f006e61326 Mon Sep 17 00:00:00 2001 From: Riley Date: Tue, 18 Jun 2024 12:50:42 -0700 Subject: [PATCH 036/126] Update ERC-6909: Nit, adds case to `transferFrom` where `sender` is `msg.sender`. Merged by EIP-Bot. --- ERCS/erc-6909.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-6909.md b/ERCS/erc-6909.md index ee0b569418..29a5011354 100644 --- a/ERCS/erc-6909.md +++ b/ERCS/erc-6909.md @@ -134,7 +134,7 @@ MUST return True. Transfers an `amount` of a token `id` from a `sender` to a `receiver` by the caller. -MUST revert when the caller is not an operator for the `sender` and the caller's allowance for the token `id` for the `sender` is insufficient. +MUST revert when the caller is neither the `sender` nor an operator for the `sender` and the caller's allowance for the token `id` for the `sender` is insufficient. MUST revert when the `sender`'s balance for the token id is insufficient. @@ -144,7 +144,7 @@ MUST decrease the caller's `allowance` by the same `amount` of the `sender`'s ba SHOULD NOT decrease the caller's `allowance` for the token `id` for the `sender` if the `allowance` is infinite. -SHOULD NOT decrease the caller's `allowance` for the token `id` for the `sender` if the caller is an operator. +SHOULD NOT decrease the caller's `allowance` for the token `id` for the `sender` if the caller is an operator or the `sender`. MUST return True. From a23c5eb82e2f20b75ced133323945efada78650a Mon Sep 17 00:00:00 2001 From: Rajat Kumar Date: Tue, 18 Jun 2024 17:11:53 -0400 Subject: [PATCH 037/126] Add ERC: Dynamic Compliant Interop Security Token Merged by EIP-Bot. --- ERCS/erc-7518.md | 573 ++++++++++++++++++++++++++ assets/erc-7518/exampleUsecase.png | Bin 0 -> 151294 bytes assets/erc-7518/sequentialDiagram.png | Bin 0 -> 94192 bytes 3 files changed, 573 insertions(+) create mode 100644 ERCS/erc-7518.md create mode 100644 assets/erc-7518/exampleUsecase.png create mode 100644 assets/erc-7518/sequentialDiagram.png diff --git a/ERCS/erc-7518.md b/ERCS/erc-7518.md new file mode 100644 index 0000000000..707647c32f --- /dev/null +++ b/ERCS/erc-7518.md @@ -0,0 +1,573 @@ +--- +eip: 7518 +title: Dynamic Compliant Interop Security Token +description: Security token framework with semi-fungible partitions for dynamic regulatory compliance management and cross-chain interoperability +author: Abhinav (@abhinav-d3v) , Prithvish Baidya (@d4mr) , Rajat Kumar (@rajatwasan) , Prasanth Kalangi +discussions-to: https://ethereum-magicians.org/t/eip-7518-dynamic-compliant-interop-security-token-dycist/15822 +status: Draft +type: Standards Track +category: ERC +created: 2023-09-14 +requires: 165, 1155 +--- +## Abstract + +This proposal is a security token standard that extends [ERC-1155](./eip-1155.md) to provide a flexible framework for managing compliant real-asset security tokens. It introduces the concept of partitions, where each `tokenId` represents a distinct partition with its own set of rights and privileges. This makes it suitable for various use cases, particularly semi-fungible asset management. The standard also includes features like token locking, forced transfers for recovery, address freezing, payouts, and dynamic compliance management using off-chain vouchers. + +## Motivation + +The growing demand for tokenized real-world assets necessitates a token standard that can accommodate the unique requirements of security tokens. Existing standards, while powerful, do not fully address the need for flexible partitioning and comprehensive compliance management. + +Build upon of [ERC-1155](./eip-1155.md) to introduce partitions, allowing for the creation of semi-fungible tokens representing fractional ownership, different share classes, or other distinct units within a single token contract. This flexibility is crucial for tokenizing complex real-world assets like real estate or funds. + +Furthermore, it includes features essential for security tokens, such as token locking for vesting or holding periods, forced transfers for recovery in case of lost keys, address freezing for regulatory compliance, efficient payout mechanisms, and dynamic compliance management using off-chain vouchers. + +By providing a standardized interface for these features, this proposal aims to facilitate the development of interoperable and compliant security token ecosystems. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Interface + +```solidity +pragma solidity ^0.8.0; + +interface IERC7518 is IERC1155, IERC165{ + event TokensLocked(address indexed account, uint indexed id, uint256 amount, uint256 releaseTime); + + event TokenUnlocked(address indexed account, uint indexed id); + + event TokensForceTransferred(address indexed from, address indexed to, uint indexed id, uint256 amount); + + event AddressFrozen(address indexed account, bytes data); + + event AddressUnfrozen(address indexed account, bytes data); + + // Emitted when the transferability of tokens with a specific ID is restricted. + event TransferRestricted(uint indexed id); + + // Emitted when the transferability restriction of tokens with a specific ID is removed. + event TransferRestrictionRemoved(uint indexed id); + + event PayoutDelivered(address indexed from, address indexed to, uint256 amount); + + /** + * @dev Retrieves the transferable balance of tokens for the specified account and ID. + * @param account The address of the account. + * @param id The token ID. + * @return The transferable balance of tokens. + */ + function transferableBalance(address account, uint id) external view returns (uint); + + /** + * @dev Retrieves the locked balance of tokens for the specified account and ID. + * @param account The address of the account. + * @param id The token ID. + * @return The locked balance of tokens. + */ + function lockedBalanceOf(address account, uint256 id) external view returns (uint256); + + /** + * @dev Restricts the transferability of tokens with the specified ID. + * @param id The token ID. + * @return A boolean value indicating whether the operation was successful. + */ + function restrictTransfer(uint id) external returns (bool); + + /** + * @dev Removes the transferability restriction of tokens with the specified ID. + * @param id The token ID. + * @return A boolean value indicating whether the operation was successful. + */ + function removeRestriction(uint id) external returns (bool); + + /** + * @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call). + * @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard). + + * After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). + * @param _from Source address + * @param _to Target address + * @param _id ID of the token type + * @param _value Transfer amount + * @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to` + */ + function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) override external; + + /** + * @dev Checks if a transfer is allowed. + * @param from The address to transfer tokens from. + * @param to The address to transfer tokens to. + * @param id The token ID. + * @param amount The amount of tokens to transfer. + * @param data Additional data related to the transfer. + * @return status A boolean value indicating whether the transfer is allowed. + */ + function canTransfer(address from, address to, uint id, uint amount, bytes calldata data) external view returns (bool status); + + /** + * @dev lock token till a particular block time. + * @param account The address of the account for which tokens will be locked. + * @param id The token ID. + * @param amount The amount of tokens to be locked for the account. + * @param releaseTime The timestamp indicating when the locked tokens can be released. + * @return bool Returns true if the tokens are successfully locked, otherwise false. + */ + function lockTokens(address account, uint id, uint256 amount, uint256 releaseTime) external returns (bool); + + /** + * @dev Unlocks tokens that have crossed the release time for a specific account and id. + * @param account The address of the account to unlock tokens for. + * @param id The token ID. + */ + function unlockToken(address account, uint256 id) external; + + /** + * @dev Force transfer in cases like recovery of tokens. + * @param from The address to transfer tokens from. + * @param to The address to transfer tokens to. + * @param id The token ID. + * @param amount The amount of tokens to transfer. + * @param data Additional data related to the transfer. + * @return A boolean value indicating whether the operation was successful. + */ + function forceTransfer(address from, address to, uint256 id, uint256 amount, bytes memory data) external returns (bool); + + /** + * @dev Freezes specified address. + * @param account The address to be frozen. + * @param data Additional data related to the freeze operation. + * @return A boolean value indicating whether the operation was successful. + */ + function freezeAddress(address account, bytes calldata data) external returns (bool); + + /** + * @dev Unfreezes specified address. + * @param account The address to be unfrozen. + * @param data Additional data related to the unfreeze operation. + * @return A boolean value indicating whether the operation was successful. + */ + function unFreeze(address account, bytes memory data) external returns (bool); + + /** + * @dev Sends payout to single address with corresponding amounts. + * @param to address to send the payouts to. + * @param amount amount representing the payouts to be sent. + * @return A boolean indicating whether the batch payouts were successful. + */* + function payout(address calldata to, uint256 calldata amount) public returns (bool); + + /** + * @dev Sends batch payouts to multiple addresses with corresponding amounts. + * @param to An array of addresses to send the payouts to. + * @param amount An array of amounts representing the payouts to be sent. + * @return A boolean indicating whether the batch payouts were successful. + */ + function batchPayout(address[] calldata to, uint256[] calldata amount) public returns (bool); +} +``` + +### Methods for token + +### `transferableBalance` + +Retrieves the transferable balance of tokens for the specified account and ID. + +```solidity +function transferableBalance(address account,uint id) external view returns (uint) +``` + +- MUST calculate and return the transferable balance of tokens for the specified account and ID ie current `balanceOf(account, id) - lockedBalanceOf(account, id)`. + +### `lockedBalanceOf` + +Retrieves the locked balance of tokens for the specified account and ID. + +```solidity +function lockedBalanceOf(address account,uint256 id) external view returns (uint256) +``` + +- MUST retrieve and return the locked balance of tokens for the specified `account` and `id`. + +### `restrictTransfer` + +Restricts the transferability of tokens with the specified ID. + +```solidity +function restrictTransfer(uint id) external returns (bool) +``` + +- MUST restrict the transferability of tokens with the specified `id`. +- SHOULD emit `TransferRestricted`. + +### `removeRestriction` + +Removes the transferability restriction of tokens with the specified ID. + +```solidity +function removeRestriction(uint id) external returns (bool) +``` + +- MUST remove the transferability restriction of tokens with the specified `id`. MUST check if `id` is previously restricted. +- SHOULD emit `TransferRestrictionRemoved`. + +### `safeTransferFrom` + +```solidi +function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) override external; +``` + +- MUST revert if `_to` is the zero address. +- MUST revert if balance of holder for token `_id` is lower than the `_value` sent. +- MUST revert on any other error. +- MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard). +- MUST call `canTransfer` function to check if the transfer can proceed + +### `canTransfer` + +Determine transferring a specified amount of a token from one address to another. + +```solidity +function canTransfer(address from,address to,uint id,uint amount,bytes calldata data) external view returns (bool status); +``` + +- Accurately determine whether the transfer of tokens is allowed. +- MUST validate `to` and `from` are not frozen address. +- MUST validate `id` of the transfer should not be restricted +- MUST check if `amount` is a transferable balance. +- MAY call external contract to verify the transfer. +- SHOULD NOT modify any state or perform any side effects. + +### `lockTokens` + +Locks a specified amount of tokens from an account for a specified duration. + +```solidity +function lockTokens(address account,uint id,uint256 amount,uint256 releaseTime) external returns (bool); +``` + +- MUST enforce time-based restrictions on the transfer or use of tokens. +- MUST revert if balance of holder is less than amount. +- SHOULD use proper access control measures to ensure that only authorized entities can lock tokens. +- MUST perform input validation prevent potential vulnerabilities and unauthorized locking of tokens. +- SHOULD record release time securely and ensure that locked tokens are only released after the designated time has passed. +- SHOULD emit `TokensLocked`. + +### `unlockToken` + +Unlocks tokens that have crossed the release time for a specific account and id. + +```solidity +function unlockToken(address account,uint256 id) external; +``` + +- MUST unlock the tokens for the specified `account` address and `id`. +- MUST unlock all the token which has release time > `block.time` +- SHOULD revert if no token are unlocked to save gas. +- SHOULD emit `TokenUnlocked`. + +### `forceTransfer` + +Force transfer in cases like recovery of tokens + +```solidity +function forceTransfer(address from,address to,uint256 id,uint256 amount,bytes memory data) external returns (bool); +``` + +- MUST bypass normal transfer restrictions and authorization checks. +- MUST revert if the `from` address is not Frozen. +- MUST revert if `to` address is Frozen. +- MUST ensure that only authorized entities have the capability to call this function. +- Additional data related to the freeze operation. +- SHOULD emit `TokensForceTransferred`. + +### `freeze` + +Freezes specified address. The Freeze function takes in the `account address` to be frozen and additional data, and returns a `boolean` value indicating whether the operation was successful. + +```solidity +function freezeAddress(address account,bytes data) external returns (bool); +``` + +- MUST prevent `account` to transfer and payout. +- SHOULD implement appropriate access control measures to ensure that only authorized addresses can be unfrozen. +- SHOULD emit `AddressFrozen`. + +### `unFreeze` + +The Unfreeze function takes in the `account address` to be unfrozen and additional data, and returns a `boolean` value indicating whether the operation was successful. + +```solidity +function unFreeze(address account,bytes memory data) external returns (bool); +``` + +- MUST consider implications of unfreezing an address, as it grants unrestricted transfer and operation capabilities. +- MUST unfreeze the specified `account` +- SHOULD implement appropriate access control measures to ensure that only authorized addresses can be unfrozen. +- SHOULD emit `AddressUnfrozen`. + +### `payout` + +Send payouts to single address, receiver will be receiving a specific amount of tokens. + +```solidity +function payout(address calldata to,uint256 calldata amount) public returns (bool) +``` + +- MUST revert if `to` address is frozen address. +- SHOULD have sufficient balance to transfer token from issuer address. +- SHOULD emit `PayoutDelivered`. + +### `batchPayout` + +Send payouts to multiple addresses at once, with each address receiving a specific amount of tokens. It can be used for various purposes such as distributing rewards, dividends, or interest payment. + +```solidity +function batchPayout(address[] calldata to,uint256[] calldata amount) public returns (bool) +``` + +- MUST revert if `to` address is frozen address. +- SHOULD have sufficient balance to transfer token from issuer address. +- SHOULD emit `PayoutDelivered`. + +### Interoperability + +This proposal facilitates interoperability with [ERC-3643](./eip-3643.md) tokens through a token wrapping method. The process involves two key components: the [ERC-3643](./eip-3643.md) token contracts representing the original and the proposed token contract for the wrapped version. Users seeking to wrap their tokens interact with the wrapping contract, which securely locks their original tokens and mints an equivalent amount of the proposed tokens to their address. Conversely, unwrapping is achieved by calling the contract's withdraw function, resulting in the burning of the proposed tokens and the release of the corresponding original tokens. Events are emitted for transparency, and robust security measures are implemented to safeguard user assets and address any potential vulnerabilities in the contract code. With this design, this proposal ensures the seamless conversion and compatibility with [ERC-3643](./eip-3643.md) tokens, promoting greater utility and usability across the Ethereum ecosystem. + +### Interface for Interoperability + +```solidity +interface IERC1155Wrapper is IERC7518 { + +/** +@dev Emitted when a new wrapped token address is added to the set. +@param wrappedTokenAddress The address of the wrapped token that was added. +*/ +event WrappedTokenAddressSet(address wrappedTokenAddress); + +/** +@dev Emitted when tokens are wrapped. +@param The ERC1155 token ID of the wrapped tokens. +@param amount The amount of tokens that were wrapped. +*/ +event TokensWrapped(uint indexed id, uint256 amount); + +/** +@dev Emitted when tokens are unwrapped. +@param wrappedTokenId Is the ERC1155 token ID of the wrapped tokens. +@param amount The amount of tokens that were unwrapped. +*/ +event TokensUnwrapped(uint indexed wrappedTokenId, uint256 amount); + +/** +* @dev Sets the wrapped token address and logic for deciding partitions. +* @param wrappedTokenAddress The address of the wrapped token contract. +* @return A boolean value indicating whether the operation was successful. +*/ +function setWrappedToken(address token) external returns (bool); + +/** +* @dev Wraps the specified amount of tokens by depositing the original tokens and receiving new standard tokens. +* @param amount The amount of tokens to wrap. +* @param data Additional data for partition. +* @return A boolean value indicating whether the operation was successful. +*/ +function wrapToken(uint256 amount, bytes calldata data) external returns (bool); + +/** +* @notice Wraps a specified amount of tokens from a given partition into the main balance. +* @dev This function allows users to convert tokens from a specific partition back to the main balance,making them fungible with tokens from other partitions. +* @param partitionId The unique identifier of the partition from which tokens will be wrapped. +* @param id The unique identifier of the token. +* @param amount The amount of tokens to be wrapped from the specified partition. +* @param data Additional data that may be used to handle the wrap process (optional). +* @return success A boolean indicating whether the wrapping operation was successful or not. +*/ + +function wrapTokenFromPartition(bytes32 partitionId, uint256 id, uint256 amount, bytes calldata data) external returns (bool); +/** +* @dev Unwraps the specified amount of wrapped tokens by depositing the current tokens and receiving the original tokens. +* @param wrappedTokenId internal partition id. +* @param amount The amount of wrapped tokens to unwrap. +* @param data Additional data for partition. +* @return A boolean value indicating whether the operation was successful. +*/ +function unwrapToken(uint256 wrappedTokenId, uint256 amount, bytes calldata data) external returns (bool); + +/** +* @dev Retrieves the balance of wrapped tokens for the specified account and ID. +* @param account The address of the account. +* @param id The token ID. +* @param data Additional data for partition. +* @return The balance of wrapped tokens. +*/ +function wrappedBalanceOf(address account, uint256 id, bytes calldata data) external view returns (uint256); + +/** +* @dev Retrieves the balance of original tokens for the specified account and ID. +* @param account The address of the account. +* @param id The token ID. +* @param data Additional data for partition. +* @return The balance of original tokens. +*/ +function originalBalanceOf(address account, uint256 id, bytes calldata data) external view returns (uint256); +} +``` + +### Methods for Interoperability + +### `setWrappedTokenAddress` + +```solidity +function setWrappedTokenAddress(address token) external returns (bool); +``` + +- `token` address could be any security token standard i.e [ERC-3643](./eip-3643.md). + +### `wrapToken` + +```solidity +function wrapToken(uint256 amount, bytes calldata data) external returns (bool); +``` + +- MUST lock token in an on-chain vault type smart contract. +- MUST mint an equivalent amount of the proposed token. +- MUST verify mapping of [ERC-1155](./eip-1155.md) `id` with the corresponding [ERC-20](./eip-20.md) compatible security token. + +### `wrapTokenFromPartition` + +```solidity +function wrapTokenFromPartition(bytes32 partitionId, uint256 id, uint256 amount, bytes calldata data) external returns (bool); +``` + +- MUST lock the token amount from source standard and mint an equivalent amount of the proposed token. +- SHOULD lock token in smart contract to achieve one to one mapping with the investor. +- MUST verify mapping of `id` with the corresponding partially fungible security token `partitionId`. + +### `unwrapToken` + +```solidity +function unwrapToken(uint256 wrappedTokenId, uint256 amount, bytes calldata data) external returns (bool); +``` + +- MUST burn the proposed token and release the original token. +- MUST verify that the token is not subject to any of the proposal's locking functionality. + +### Partition Management + +The proposal leverages the `tokenId` feature of [ERC-1155](./eip-1155.md) to represent distinct partitions within a token contract. Each `tokenId` corresponds to a unique partition with its own set of rights, privileges, and compliance rules. This enables the creation of semi-fungible tokens representing fractional ownership, different share classes, or other granular units. + +The partition paradigm offers significant flexibility and power in managing security tokens: + +1. Dynamic Allocation : Partitions allow for dynamic allocation of tokens between different classes or categories. For example, in a real estate tokenization scenario, an issuer can initially allocate tokens to a Reg D partition for accredited U.S. investors and a "Reg S" partition for non-U.S. investors. As the offering progresses and demand shifts, the issuer can dynamically mint tokens into the appropriate partition based on the investor's eligibility, ensuring optimal distribution and compliance. +2. Temporary Non-Fungibility : Partitions enable temporary non-fungibility of tokens. In some cases, securities may need to be treated as non-fungible for a certain period, such as tokens of the same underlying asset sold at different offerings. By assigning tokens to specific partitions, issuers can enforce these restrictions and maintain the necessary segregation between them, but merge them at a later point to prevent liquidity fragmentation. Merger occurs by creating a new joint partition, a deploying a merger contract where users can deposit old partitioned tokens to receive new joint partition token. +3. Granular Compliance : Each partition can have its own set of compliance rules and transfer restrictions. This allows for more granular control over token transfers based on the specific characteristics of each partition. For instance, a partition representing a particular share class may have different transfer restrictions or payout rights compared to other partitions. +4. Efficient Asset Management : Partitions streamline the management of complex asset structures. Instead of deploying separate contracts for each share class or asset category, issuers can manage multiple partitions within a single proposed contract, reducing deployment costs and simplifying overall asset management. + +### Compliance Management + +![image](../assets/eip-7518/sequentialDiagram.png) + +This proposal includes functions for managing token transfers in accordance with regulatory requirements and issuer-defined rules. The `canTransfer` function checks whether a transfer is allowed based on factors such as token restrictions, frozen addresses, transferable balances, and token locking. + +To facilitate dynamic compliance management, it introduces the concept of off-chain vouchers. These vouchers are signed messages generated by an authorized entity (e.g., the issuer or a designated compliance service) that attest to the compliance of a specific transfer. The `canTransfer` function can verify these vouchers to determine the eligibility of a transfer. + +Here's an example of how off-chain vouchers can be used with the proposal: + +1. The token issuer defines a set of compliance rules and requirements for token transfers. +2. When a user initiates a transfer, they submit a request to a designated compliance service with the necessary details (sender, recipient, amount, etc.). +3. The compliance service evaluates the transfer request against the predefined rules and requirements, considering factors such as investor eligibility, transfer restrictions, and regulatory compliance. +4. If the transfer is deemed compliant, the compliance service generates a signed voucher containing the relevant details and returns it to the user. +5. The user includes the signed voucher as an additional parameter when calling the `safeTransferFrom` function on the proposed contract. +6. The `canTransfer` function verifies the authenticity and validity of the voucher by checking the signature and ensuring that the voucher details match the transfer parameters. +7. If the voucher is valid and the transfer meets all other requirements, the transfer is allowed to proceed. + +By leveraging off-chain vouchers, the proposal enables dynamic compliance management, allowing issuers to enforce complex and evolving compliance rules without the need to update the token contract itself. This approach provides flexibility and adaptability in the face of changing regulatory requirements. + +### Token Recovery + +In case of lost or compromised wallets, the proposal includes a `forceTransfer` function that allows authorized entities (e.g., the issuer or a designated recovery agent) to transfer tokens from one address to another. This function bypasses the usual transfer restrictions and can be used as a recovery mechanism. + +### Payout Management + +Provides functions for efficient payout distribution to token holders. The `payout` function allows sending payouts to a single address, while `batchPayout` enables sending payouts to multiple addresses in a single transaction. These functions streamline the process of distributing dividends, interest, or other payments to token holders. + +### Real World Example + +![image](../assets/eip-7518/exampleUsecase.png) + +#### Use Case 1: Tokenization of Commercial Real Estate + +In this use case, a commercial real estate property with 100 floors is being tokenized using this proposal. Each floor is represented as a unique non-fungible token (NFT) partition, allowing for fractional ownership and separate management of individual floors. + +1. Property Representation: The entire commercial property is tokenized using the proposed contract, with each floor being assigned a unique tokenId representing an NFT partition. + +2. Fractional Ownership: Each floor's NFT partition can be divided into multiple fungible tokens, enabling fractional ownership. For instance, if a floor is divided into 100 tokens, multiple investors can own portions of that floor. + +3. Dynamic Pricing: Since each floor is a separate partition, the pricing of tokens within a partition can be adjusted dynamically based on factors such as floor level, amenities, or market demand. This flexibility allows for accurate representation of the varying values of different floors. + +4. Transfer of Ownership: The ownership of each floor's NFT partition can be transferred seamlessly to token holders using the safeTransferFrom function. This enables the seamless transfer of ownership rights for specific floors. + +5. Compliance Management: Different compliance rules and transfer restrictions can be applied to each partition (floor) based on regulatory requirements or issuer-defined rules. The canTransfer function can be used to enforce these rules before allowing transfers. + +6. Payouts: The payout and batchPayout functions can be used to distribute rental income, dividends, or other payouts to token holders of specific floor partitions efficiently. + +By leveraging proposal, this use case demonstrates the ability to tokenize complex real estate assets while maintaining granular control over ownership, pricing, compliance, and payouts for individual units within the property. + +#### Use Case 2: Tokenization of Securities with Reg S and Reg D Partitions + +In this use case, a company is tokenizing its securities and wants to comply with different regulations for U.S. accredited investors (Reg D) and non-U.S. investors (Reg S). + +1. Initial Partitions: The company deploys an proposed standard and creates two partitions: one for Reg D investors (accredited U.S. investors) and another for Reg S investors (non-U.S. investors). + +2. Dynamic Allocation: As the offering progresses, the company can dynamically mint tokens into the appropriate partition based on investor eligibility. For example, if a U.S. accredited investor wants to participate, tokens can be minted in the Reg D partition, while tokens for non-U.S. investors are minted in the Reg S partition. + +3. Compliance Management: Each partition can have its own set of compliance rules and transfer restrictions. The canTransfer function can be integrated with off-chain compliance services to verify the eligibility of a transfer based on the specific rules for each partition. + +4. Temporary Non-Fungibility: During the initial offering period, tokens in the Reg D and Reg S partitions may need to be treated as non-fungible due to different regulatory requirements. However, after the holding period, the company can create a new joint partition and allow token holders to deposit their old partitioned tokens to receive the new joint partition tokens, merging the two classes. + +5. Payouts: The payout and batchPayout functions can be used to distribute dividends, interest payments, or other payouts to token holders in each partition based on their respective rights and privileges. + +By utilizing the proposal, this use case demonstrates the ability to tokenize securities while maintaining compliance with different regulatory regimes, dynamically allocating tokens based on investor eligibility, and efficiently managing payouts and potential mergers of different share classes. + +#### Use Case 3: Force Transfer for AML/KYC/Compliance Violations + +In the world of tokenized securities, maintaining compliance with regulatory requirements is of utmost importance. This proposal provides a robust mechanism to handle situations where an investor's tokens need to be forcibly transferred due to violations of Anti-Money Laundering (AML), Know Your Customer (KYC), or other compliance-related regulations. + +Let's consider the scenario of Alice, an investor who holds tokens in the proposed token compliant security token contract. During the regular compliance checks conducted by the token issuer or a designated compliance service, it is discovered that Alice's wallet address is associated with suspicious activities related to money laundering or other financial crimes. + +In such a situation, the regulatory authorities or the contract administrators may decide to freeze Alice's account and initiate a forced transfer of her tokens to a designated address controlled by the issuer or a recovery agent. The `forceTransfer` function in this proposal enables this process. + +## Rationale + +### Enhancing Compliance Management + +The `canTransfer` function facilitates compliance checks during token transfers, offering adaptability through diverse implementation methods such as on-chain storage, oracle utilization, or any off-chain methodologies. This versatility ensures seamless integration with existing compliance frameworks, particularly in enforcing regulatory standards like KYC/AML. Additionally, functionalities like `freezeAddress`, `restrictTransfer`, `lockToken` and `forceTransfer` empower entities to regulate token movements based on specified conditions or regulatory requirements. Complementing these, the `unlockToken` function enhances transparency and accountability by facilitating the release of tokens post-compliance actions. + +### Interoperability with other standard + +The functions `wrapToken` and `wrapTokenFromPartition` are essential for simplifying conversions within the token system. `wrapToken` is specifically designed for wrapping ERC-20-like tokens to this protocol, on the other hand, `wrapTokenFromPartition` is used when we want to convert tokens from non-fungible tokens or any multi-standard token into proposed protocol. It allows for more specialized conversions, ensuring tokens from different standards can work together smoothly. + +The `unwrapToken` function is used to reverse the process of wrapping tokens. When tokens are wrapped, they're usually locked or held in a special way to ensure they're used correctly. users can unlock or release these tokens, returning them to their original standard, essentially, frees up tokens that were previously locked, giving users more control over their assets in the ecosystem. + +### Payment distribution + +The `payout` function enables direct payments to individual token holders for one-off or event-triggered distributions, facilitating targeted disbursements. Meanwhile, the `batchPayout` function processes multiple payments in a single transaction, optimizing efficiency for larger-scale or regular payouts on the blockchain + +## Backwards Compatibility + +The proposal is fully compatible with [ERC-1155](./eip-1155.md) , and any [ERC-1155](./eip-1155.md) compliant wallet or marketplace can interact with the proposal's tokens. The additional functions introduced by this proposal do not conflict with the [ERC-1155](./eip-1155.md) interface, ensuring seamless integration with existing ecosystem tools and infrastructure. + +## Security Considerations + +1. Access Control: The proposal includes functions that can significantly impact token transfers and balances, such as `forceTransfer`, `freezeAddress`, and `lockTokens`. It is crucial to implement proper access control mechanisms, such as role-based permissions, to ensure that only authorized entities can execute these functions. +2. Parameter Validation: Functions like `safeTransferFrom`, `lockTokens`, and `forceTransfer` should validate input parameters to prevent unauthorized or unintended actions. This includes checking for valid addresses, sufficient balances, and appropriate permissions. +3. Reentrancy Protection: The contract should implement reentrancy guards to prevent potential vulnerabilities arising from external calls, especially in functions that transfer tokens or update balances. +4. Overflow/Underflow Protection: The contract should use safe math libraries or built-in overflow protection to prevent integer overflow and underflow vulnerabilities. +5. Payout Security: The `payout` and `batchPayout` functions should ensure that only authorized entities can initiate payouts and that the total payout amount does not exceed the available balance. Proper access control and input validation are essential to prevent unauthorized or fraudulent payouts. +6. Off-Chain Voucher Security: When using off-chain vouchers for dynamic compliance management, it is crucial to ensure the security and integrity of the voucher generation process. The compliance service responsible for generating vouchers should have robust security measures in place to prevent unauthorized voucher creation or tampering. Additionally, the proposed contract should thoroughly validate the authenticity and validity of vouchers before allowing transfers to proceed. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/assets/erc-7518/exampleUsecase.png b/assets/erc-7518/exampleUsecase.png new file mode 100644 index 0000000000000000000000000000000000000000..74386d843f3959427ec0478cd0c15e3171471392 GIT binary patch literal 151294 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

e=8~U`Ww#jXRIK~ruMD5h*4H<{qE)|{t$#7^ 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 J;?!*({Xe=ut1$oo literal 0 HcmV?d00001 diff --git a/assets/erc-7518/sequentialDiagram.png b/assets/erc-7518/sequentialDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..0b6c45f7f07b5efb38dc73b0e189d42ce3e0c4c6 GIT binary patch literal 94192 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 DDM3^s literal 0 HcmV?d00001 From 17939031da4fe39f172e77f1c7bee89898989ba5 Mon Sep 17 00:00:00 2001 From: sshmatrix <19473027+sshmatrix@users.noreply.github.com> Date: Wed, 19 Jun 2024 08:36:44 +0530 Subject: [PATCH 038/126] Update ERC-7700: Minor Edits for Clarity Merged by EIP-Bot. --- ERCS/erc-7700.md | 76 +++--- assets/erc-7700/images/Database.svg | 103 ++++---- assets/erc-7700/images/Keygen.svg | 90 +++---- assets/erc-7700/images/L1.svg | 22 +- assets/erc-7700/images/L2.svg | 31 +-- assets/erc-7700/images/Schema.svg | 366 ++++++++++++++-------------- 6 files changed, 342 insertions(+), 346 deletions(-) diff --git a/ERCS/erc-7700.md b/ERCS/erc-7700.md index 17af1dc8eb..c46616ce53 100644 --- a/ERCS/erc-7700.md +++ b/ERCS/erc-7700.md @@ -28,10 +28,10 @@ In this context, a specification which allows storage routing to external router ## Specification ### Overview -The following specification revolves around the structure and description of a cross-chain storage router tasked with the responsibility of writing to an L2 or database storage. This document introduces `StorageRoutedToL2()` and `StorageRoutedToDatabase()` storage routers, and proposes that new `StorageRoutedTo__()` reverts be allowed through new EIPs that sufficiently detail their interfaces and designs. Some foreseen examples of new storage routers include `StorageRoutedToSolana()` for Solana, `StorageRoutedToFilecoin()` for Filecoin, `StorageRoutedToIPFS()` for IPFS, `StorageRoutedToIPNS()` for IPNS, `StorageRoutedToArweave()` for Arweave, `StorageRoutedToArNS()` for ArNS, `StorageRoutedToSwarm()` for Swarm etc. +The following specification revolves around the structure and description of a cross-chain storage router tasked with the responsibility of writing to an L2 or database storage. This document introduces `StorageRoutedToL2()` and `StorageRoutedToDatabase()` storage routers, along with the trivial `StorageRoutedToL1()` router, and proposes that new `StorageRoutedTo__()` reverts be allowed through new EIPs that sufficiently detail their interfaces and designs. Some foreseen examples of new storage routers include `StorageRoutedToSolana()` for Solana, `StorageRoutedToFilecoin()` for Filecoin, `StorageRoutedToIPFS()` for IPFS, `StorageRoutedToIPNS()` for IPNS, `StorageRoutedToArweave()` for Arweave, `StorageRoutedToArNS()` for ArNS, `StorageRoutedToSwarm()` for Swarm etc. ### L1 Router: `StorageRoutedToL1()` -A minimal L1 router only requires the `contractL1` address to which routing must be made, while the clients must ensure that the calldata is invariant under routing to another contract. One example implementation of an L1 router is given below. +A minimal L1 router is trivial and only requires the L1 `contract` address to which routing must be made, while the clients must ensure that the calldata is invariant under routing to another contract. One example implementation of an L1 router is given below. ```solidity // Define revert event @@ -57,10 +57,10 @@ function setValue( }; ``` -In this example, the routing must prompt the client to build the transaction with the exact same original calldata, and submit it to the `contractL1` by calling the exact same function. +In this example, the routing must prompt the client to build the transaction with the exact same original calldata, and submit it to the L1 `contract` by calling the exact same function. ```solidity -// Function in router L1 contract +// Function in routed L1 contract function setValue( bytes32 node, bytes32 key, @@ -74,7 +74,7 @@ function setValue( ![Fig.2 L1 Call Lifecycle](../assets/eip-7700/images/L1.svg) ### L2 Router: `StorageRoutedToL2()` -A minimal L2 router only requires the list of `chainId` values and the corresponding `contract` addresses, while the clients must ensure that the calldata is invariant under routing to L2. One example implementation of an L2 router in an L1 contract is shown below. +A minimal L2 router only requires the list of `chainId` values and the corresponding L2 `contract` addresses, while the clients must ensure that the calldata is invariant under routing to L2. One example implementation of an L2 router in an L1 contract is shown below. ```solidity // Define revert event @@ -246,7 +246,7 @@ bytes32 extradata = keccak256( ) ``` -The remaining `protocol` field is a protocol-specific identifier limiting the scope to a specific protocol represented by a unique contract address. This identifier cannot be global and must be uniquely defined for each implementating `contract` such that: +The remaining `protocol` field is a protocol-specific identifier limiting the scope to a specific protocol represented by a unique contract address. This identifier cannot be global and must be uniquely defined for each implementating L1 `contract` such that: ```js /* Protocol identifier in CAIP-10 format */ @@ -347,47 +347,47 @@ Example of a complete `Post` typed object for updating multiple ENS records for ```ts /* Example of a POST request */ let post: Post = { - "node": "0xe4ad669b4f80715d286697885cfdf2552d3042e07717247924532662b1eda1da", - "preimage": "sub.domain.eth", - "chainId": 1, - "approval" : "0x1cc5e5efa312dc292560a26e3dba2584070b02ec203c51440a3e23d49ba56b342a4404d8b0d9dc26a94190691e47652343183bf1c64bf9c5081a2f1d887937f11b", - "payload" : { - "contenthash": { - "value" : "ipfs://QmRAQB6YaCyidP37UdDnjFY5vQuiBrcqdyoW1CuDgwxkD4", - "signature": "0x0679eaedb300308680a0e8c11725e891d1500fb98b65d6d09d538e2655567fdf06b989689a01db312ad6df0752cbcb1756b3405a7163f8b4b7c01e70b1a9c5c31c", - "timestamp": 1708322868, - "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec800000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000414abb7b2b9fc395910b4387ff69897ee639fe1cf9b79c31bf2d3743134e77a9b222ec175e563d13d60bc722c8829ce91d9af51bcd949816f95979abef4378d84e1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000026e3010170122022380b0884d9e85ef3ff5f71ea7a25874738da71f38b999dc8ffec2f6389a3670000000000000000000000000000000000000000000000000000" + node: "0xe8e5c24bb5f0db1f3cab7d3a7af2ecc14a7a4e3658dfb61c9b65a099b5f086fb", + preimage: "dev.namesys.eth", + chainId: 1, + approval: "0xa94da8233afb27d087f6fbc667cc247ef2ed31b5a1ff877ac823b5a2e69caa49069f0daa45a464d8db2f8e4e435250cb446d8f279d45a2b865ebf2fff291f69f1c", + payload: { + contenthash: { + value: "ipfs://QmYSFDzEcmk25JPFrHBHSMMLcTKLm6SvuZvKpijTHBnAYX", + signature: "0x24730d1d85d556245b7766aef413188e22f219c8de263ccbfafee4413f0937c32e4f44068d84c7424f923b878dcf22184f8df86506de1cea3dad932c5bd5e9de1c", + timestamp: 1708322868, + data: "0x2b45eb2b000000000000000000000000fe889053f7a0d2571f1898d2835c3cbdf50d766b000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000004124730d1d85d556245b7766aef413188e22f219c8de263ccbfafee4413f0937c32e4f44068d84c7424f923b878dcf22184f8df86506de1cea3dad932c5bd5e9de1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a94da8233afb27d087f6fbc667cc247ef2ed31b5a1ff877ac823b5a2e69caa49069f0daa45a464d8db2f8e4e435250cb446d8f279d45a2b865ebf2fff291f69f1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000026e301017012209603ccbcef5c2acd57bdec6a63e8a0292f3ce6bb583b6826060bcdc3ea84ad900000000000000000000000000000000000000000000000000000" }, - "address": [ + address: [ { - "coinType": 0, - "value": "1FfmbHfnpaZjKFvyi1okTjJJusN455paPH", - "signature": "0x60ecd4979ae2c39399ffc7ad361066d46fc3d20f2b2902c52e01549a1f6912643c21d23d1ad817507413dc8b73b59548840cada57481eb55332c4327a5086a501b", - "timestamp": 1708322877, - "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec800000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000419c7c185335898d7ec57cffb842e88116a82f367237815f35e16d5f8b28dc3e7b0f0b40edd9f9fc48f771f921986c45973f4c2a82e8c2ebe1732a9f552f8b033a1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001111000000000000000000000000000000000001" + coinType: 0, + value: "1FfmbHfnpaZjKFvyi1okTjJJusN455paPH", + signature: "0x60ecd4979ae2c39399ffc7ad361066d46fc3d20f2b2902c52e01549a1f6912643c21d23d1ad817507413dc8b73b59548840cada57481eb55332c4327a5086a501b", + timestamp: 1708322877, + data: "0x2b45eb2b000000000000000000000000fe889053f7a0d2571f1898d2835c3cbdf50d766b000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000004160ecd4979ae2c39399ffc7ad361066d46fc3d20f2b2902c52e01549a1f6912643c21d23d1ad817507413dc8b73b59548840cada57481eb55332c4327a5086a501b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a94da8233afb27d087f6fbc667cc247ef2ed31b5a1ff877ac823b5a2e69caa49069f0daa45a464d8db2f8e4e435250cb446d8f279d45a2b865ebf2fff291f69f1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a0e6ca5444e4d8b7c80f70237f332320387f18c7" }, { - "coinType": 60, - "value": "0x839B3B540A9572448FD1B2335e0EB09Ac1A02885", - "signature": "0xaad74ddef8c031131b6b83b3bf46749701ed11aeb585b63b72246c8dab4fff4f79ef23aea5f62b227092719f72f7cfe04f3c97bfad0229c19413f5cb491e966c1b", - "timestamp": 1708322917, - "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec800000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000419bb4494a9ac6b37d5d979cbb6c43cccbbd8790ebbd8f898d8427e1ebfd8bb8bd29a2fbc2b20b0a53c3fdde9dd8ce3df648112754742156d3a5ac6fd1b80d8bd01b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c68747470733a2f2f6e616d657379732e78797a2f6c6f676f2e706e6700000000" + coinType: 60, + value: "0x47C10B0491A138Ddae6cCfa26F17ADCfCA299753", + signature: "0xaad74ddef8c031131b6b83b3bf46749701ed11aeb585b63b72246c8dab4fff4f79ef23aea5f62b227092719f72f7cfe04f3c97bfad0229c19413f5cb491e966c1b", + timestamp: 1708322917, + data: "0x2b45eb2b000000000000000000000000fe889053f7a0d2571f1898d2835c3cbdf50d766b0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000041aad74ddef8c031131b6b83b3bf46749701ed11aeb585b63b72246c8dab4fff4f79ef23aea5f62b227092719f72f7cfe04f3c97bfad0229c19413f5cb491e966c1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a94da8233afb27d087f6fbc667cc247ef2ed31b5a1ff877ac823b5a2e69caa49069f0daa45a464d8db2f8e4e435250cb446d8f279d45a2b865ebf2fff291f69f1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000047c10b0491a138ddae6ccfa26f17adcfca299753" } ], - "text": [ + text: [ { - "key": "avatar", - "value": "https://domain.com/avatar", - "signature": "0xbc3c7f1b511de151bffe8df033859295d83d400413996789e706e222055a2353404ce17027760c927af99e0bf621bfb24d3bfc52abb36bcfbe6e20cf43db7c561b", - "timestamp": 1708329377, - "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000041dc6ca55c1d1c75eec223a7eb01eb5942a2bdb79708c25ff2827cfc0343f97fb76faefd9fbc40de5103956bbdc841f2cc2d53630cd2836a6b76d8d2c107ccadd21b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046e6e6e6e00000000000000000000000000000000000000000000000000000000" + key: "avatar", + value: "https://namesys.xyz/logo.png", + signature: "0xbc3c7f1b511de151bffe8df033859295d83d400413996789e706e222055a2353404ce17027760c927af99e0bf621bfb24d3bfc52abb36bcfbe6e20cf43db7c561b", + timestamp: 1708329377, + data: "0x2b45eb2b000000000000000000000000fe889053f7a0d2571f1898d2835c3cbdf50d766b0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000041bc3c7f1b511de151bffe8df033859295d83d400413996789e706e222055a2353404ce17027760c927af99e0bf621bfb24d3bfc52abb36bcfbe6e20cf43db7c561b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a94da8233afb27d087f6fbc667cc247ef2ed31b5a1ff877ac823b5a2e69caa49069f0daa45a464d8db2f8e4e435250cb446d8f279d45a2b865ebf2fff291f69f1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c68747470733a2f2f6e616d657379732e78797a2f6c6f676f2e706e6700000000" }, { - "key": "com.github", - "value": "namesys-eth", - "signature": "0xc9c33ff219e90510f79b6c9bb489917ee6e00ab123c55abe1117e71ea0d171356cf316420c71cfcf4bd63a791aaf37388ef1832e582f54a8c2df173917240fff1b", - "timestamp": 1708322898, - "data": "0x2b45eb2b0000000000000000000000005ee86839080d2593b30604e3eeb78271fdc29ec80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000041bfd0ab74712b98bc472ef0e5bbb031acba077fc98a54cdfcb3f11e64b02d7fe21477ba5ea9d508a0265616d74a8df99b9c8f3c04e6bfd41f2df554fe11e1fe141c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cbfb2300d60b8602db32ad4ac57279e7a3632e35bb5966eb686e0ac8ec8e7b4a6e306a13a0adee15fce5a9e2bbf3a016db023b0ab66f04bde62a13343287e3851b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046b6b6b6b00000000000000000000000000000000000000000000000000000000" + key: "com.github", + value: "namesys-eth", + signature: "0xc9c33ff219e90510f79b6c9bb489917ee6e00ab123c55abe1117e71ea0d171356cf316420c71cfcf4bd63a791aaf37388ef1832e582f54a8c2df173917240fff1b", + timestamp: 1708322898, + data: "0x2b45eb2b000000000000000000000000fe889053f7a0d2571f1898d2835c3cbdf50d766b0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000041c9c33ff219e90510f79b6c9bb489917ee6e00ab123c55abe1117e71ea0d171356cf316420c71cfcf4bd63a791aaf37388ef1832e582f54a8c2df173917240fff1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a94da8233afb27d087f6fbc667cc247ef2ed31b5a1ff877ac823b5a2e69caa49069f0daa45a464d8db2f8e4e435250cb446d8f279d45a2b865ebf2fff291f69f1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b6e616d657379732d657468000000000000000000000000000000000000000000" } ] } diff --git a/assets/erc-7700/images/Database.svg b/assets/erc-7700/images/Database.svg index f00d57d15e..daf80183d7 100644 --- a/assets/erc-7700/images/Database.svg +++ b/assets/erc-7700/images/Database.svg @@ -8,9 +8,9 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="538.99292mm" - height="246.12524mm" - viewBox="0 0 538.99292 246.12525" + width="1000mm" + height="707mm" + viewBox="0 0 1000 707.00002" version="1.1" id="svg6" inkscape:version="1.0.2 (e86c8708, 2021-01-15)" @@ -179,16 +179,16 @@ + transform="translate(101.0709,4.805857)" + style="fill:none"> L1 + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-width:0.771;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;">L1 + style="stroke:#ff5300;stroke-opacity:1;fill:none"> DB + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-width:0.771;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;">DB @@ -339,34 +340,34 @@ id="path1225-8" sodipodi:nodetypes="cc" /> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1"> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1"> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1" /> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1"> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1"> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1" /> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1"> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1"> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1" /> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1"> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1"> + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1" /> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1" /> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1" /> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1" /> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1" /> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1" /> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1" /> CLIENT + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.771;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;">CLIENT image/svg+xml - + @@ -173,16 +173,16 @@ + style="stroke:#ffffff;stroke-opacity:1"> + style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-opacity:1" /> + style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-opacity:1" /> + style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:3.41429;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> otherkeys @@ -980,32 +980,32 @@ transform="matrix(0.27231888,0,0,0.27231888,213.85157,218.02482)" /> hkdf + style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.517;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3.08168;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"> + style="fill:#8c8c8c;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"> + style="fill:#8c8c8c;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"> + style="fill:#8c8c8c;fill-opacity:1;stroke:#ffffff;stroke-opacity:1" /> + style="fill:#8c8c8c;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"> sha- 256 sign @@ -1080,27 +1080,27 @@ + style="stroke-width:4.39179;stroke-miterlimit:4;stroke-dasharray:none;stroke:#ffffff;stroke-opacity:1;fill:#8c8c8c;fill-opacity:1"> + style="stroke-width:18.1111;stroke-miterlimit:4;stroke-dasharray:none;stroke:#ffffff;stroke-opacity:1;fill:#8c8c8c;fill-opacity:1"> + style="stroke:#ffffff;stroke-width:18.1111;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#8c8c8c;fill-opacity:1" /> + style="stroke:#ffffff;stroke-width:18.1111;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#8c8c8c;fill-opacity:1" /> + style="stroke:#ffffff;stroke-width:8.79949;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#8c8c8c;fill-opacity:1"> + style="stroke:#ffffff;stroke-width:8.79949;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#8c8c8c;fill-opacity:1" /> + style="stroke:#ffffff;stroke-opacity:1;fill:#ffffff;fill-opacity:1"> PBKDf 2 @@ -280,7 +280,7 @@ transform="translate(101.0709,4.2183)" style="stroke:#ff5300;stroke-opacity:1"> @@ -506,13 +506,13 @@ CLIENT + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.771;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;">CLIENT diff --git a/assets/erc-7700/images/L2.svg b/assets/erc-7700/images/L2.svg index 6906ecc807..b63cecba00 100644 --- a/assets/erc-7700/images/L2.svg +++ b/assets/erc-7700/images/L2.svg @@ -179,16 +179,16 @@ + transform="translate(101.0709,4.805857)" + style="fill:none"> @@ -278,15 +279,15 @@ + style="stroke:#ff5300;stroke-opacity:1;fill:none"> L2 + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-width:0.771;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;">L2 CLIENT + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';stroke:#ffffff;stroke-width:0.771;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#ffffff;fill-opacity:1;">CLIENT diff --git a/assets/erc-7700/images/Schema.svg b/assets/erc-7700/images/Schema.svg index c201698411..a91b558df2 100644 --- a/assets/erc-7700/images/Schema.svg +++ b/assets/erc-7700/images/Schema.svg @@ -8,9 +8,9 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="420.7149mm" + width="420.71497mm" height="251.75276mm" - viewBox="0 0 420.71492 251.75276" + viewBox="0 0 420.71498 251.75276" version="1.1" id="svg6" inkscape:version="1.0.2 (e86c8708, 2021-01-15)" @@ -26,7 +26,7 @@ image/svg+xml - + @@ -229,14 +229,14 @@ 3 ) L2 database IPNS ar SOLANA IPFS IPNS @@ -978,7 +978,7 @@ id="path2263-1-4-5-4-9-6-5-6-3-9-6-2" sodipodi:nodetypes="ccc" /> 3 + transform="translate(0,1.5874523)" + style="fill:#b3b3b3;fill-opacity:0.62096775;stroke:#ffffff;stroke-opacity:1"> L2 IPFS DATABASE ARWEAVE SOLANA @@ -1241,8 +1242,8 @@ id="path2263-1-4-5-4-9-6-5-4-7-3-4-3" sodipodi:nodetypes="cc" /> json-rpc http ipfs2 ar-io arns arns @@ -1433,22 +1434,22 @@ + style="fill:#ffffff;fill-opacity:1"> + style="overflow:hidden;fill:#ffffff;fill-opacity:1;stroke-width:0.0149671" /> + style="overflow:hidden;fill:#ffffff;fill-opacity:1;stroke-width:0.0149671" /> + style="overflow:hidden;fill:#ffffff;fill-opacity:1;stroke-width:0.0149671" /> + style="fill:#ffffff;fill-opacity:1;stroke-width:0.35886" /> + style="fill:#ffffff;fill-opacity:1"> + style="fill:#ffffff;fill-opacity:1"> + style="fill:#ffffff;fill-opacity:1"> + style="fill:#ffffff;fill-opacity:1" /> + style="fill:#ffffff;fill-opacity:1" /> + style="fill:#ffffff;fill-opacity:1" /> @@ -1486,120 +1487,113 @@ id="g1429" transform="translate(-12.012112,-25.668147)"> + id="g3649-8-3" + transform="matrix(0.41454246,0,0,0.45705146,243.9498,204.35368)" + style="fill:#ffffff;fill-opacity:1;stroke-width:0.999361;stroke-miterlimit:4;stroke-dasharray:none"> + + + + + requires CID/keygen + + requires signature + + id="g1235-3-3-6" + style="fill:#ffffff;fill-opacity:1"> + id="g1233-0-3-8" + style="fill:#ffffff;fill-opacity:1"> + d="m 336.688,343.962 v 0 C 314.716,343.961 296.84,326.086 296.84,304.114 v -66.176 c 0,-21.972 17.876,-39.847 39.848,-39.847 h 103.83 c 0.629,0 1.254,0.019 1.876,0.047 v -65.922 c 0,-16.969 -13.756,-30.725 -30.725,-30.725 H 30.726 C 13.756,101.49 0,115.246 0,132.215 v 277.621 c 0,16.969 13.756,30.726 30.726,30.726 h 380.943 c 16.969,0 30.725,-13.756 30.725,-30.726 v -65.922 c -0.622,0.029 -1.247,0.048 -1.876,0.048 z" + id="path1227-7-1-8" + style="fill:#ffffff;fill-opacity:1" /> + d="m 440.518,219.925 h -103.83 c -9.948,0 -18.013,8.065 -18.013,18.013 v 66.176 c 0,9.948 8.065,18.013 18.013,18.013 h 103.83 c 9.948,0 18.013,-8.064 18.013,-18.013 v -66.176 c 0,-9.949 -8.065,-18.013 -18.013,-18.013 z m -68.052,77.099 c -14.359,0 -25.999,-11.64 -25.999,-25.999 0,-14.359 11.64,-25.999 25.999,-25.999 14.359,0 25.999,11.64 25.999,25.999 0,14.359 -11.64,25.999 -25.999,25.999 z" + id="path1229-4-3-3" + style="fill:#ffffff;fill-opacity:1" /> - - requires CID/keygen - - requires signature - - - - - - - - + d="M 358.169,45.209 C 351.295,24.403 328.856,13.109 308.051,19.983 L 151.958,71.552 h 214.914 z" + id="path1231-1-6-5" + style="fill:#ffffff;fill-opacity:1" /> - requires $ payment - + requires $ payment + API @@ -1616,19 +1610,19 @@ y="297.26746" id="tspan2163-0-2-6-4-3-0-7-4-0-1-9-4">metadata Date: Wed, 19 Jun 2024 22:25:33 +0800 Subject: [PATCH 039/126] Add ERC: ERC-20/ERC-721 Unified Token Interface Merged by EIP-Bot. --- ERCS/erc-7629.md | 330 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 ERCS/erc-7629.md diff --git a/ERCS/erc-7629.md b/ERCS/erc-7629.md new file mode 100644 index 0000000000..275da09470 --- /dev/null +++ b/ERCS/erc-7629.md @@ -0,0 +1,330 @@ +--- +eip: 7629 +title: ERC-20/ERC-721 Unified Token Interface +description: introduces a single interface for ERC-20/ERC-721 tokens, enabling seamless interaction by defining common functions for both token types. +author: 0xZeus1111 (@0xZeus1111), Nvuwa (@Nvuwa) +discussions-to: https://ethereum-magicians.org/t/erc-7629-unified-token/18793 +status: Draft +type: Standards Track +category: ERC +created: 2024-02-18 +requires: 20, 165, 721 +--- + + +## Abstract + +This proposal introduces a protocol that establishes a unified interface for managing both [ERC-20](./eip-20.md) fungible tokens and [ERC-721](./eip-721.md) non-fungible tokens (NFTs) on the Ethereum blockchain. By defining a common set of functions applicable to both token types, developers can seamlessly interact with [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) tokens using a single interface. This simplifies integration efforts and enhances interoperability within decentralized applications (DApps). + + +## Motivation + +The proposal aims to address the demand for assets combining the liquidity of [ERC-20](./eip-20.md) tokens and the uniqueness of [ERC-721](./eip-721.md) tokens. Current standards present a fragmentation, requiring users to choose between these features. This proposal fills that gap by providing a unified token interface, enabling smooth transitions between [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) characteristics to accommodate diverse blockchain applications. + +## Specification + +- Introduces a token contract that combines features from both [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) standards. +- Supports state transitions between [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) modes, facilitating seamless conversion and utilization of both liquidity and non-fungibility. +- Defines essential functions and events to support token interactions, conversions, and queries. +- Implements low gas consumption [ERC-20](./eip-20.md) mode to maintain efficiency comparable to typical [ERC-20](./eip-20.md) token transfers. + + +Compliant contracts MUST implement the following Solidity interface: + +```solidity + +pragma solidity ^0.8.0; +/** + * @title ERC-7629 Unify Token Interface + * @dev This interface defines the ERC-7629 Unify Token, which unifies ERC-721 and ERC-20 assets. + */ +interface IERC7629 is IERC165 { + // ERC-20 Transfer event + event ERC20Transfer( + address indexed from, + address indexed to, + uint256 amount + ); + + // ERC-721 Transfer event + event ERC721Transfer( + address indexed from, + address indexed to, + uint256 indexed tokenId + ); + + // ERC-721 Transfer event + event Transfer( + address indexed from, + address indexed to, + uint256 indexed tokenId + ); + + // Approval event for ERC-20 and ERC-721 + event Approval( + address indexed owner, + address indexed approved, + uint256 indexed tokenId + ); + + // Approval event for ERC-20 and ERC-721 + event Approval( + address indexed owner, + address indexed approved, + uint256 indexed tokenId + ); + + // Approval event for ERC-20 + event ERC20Approval( + address indexed owner, + address indexed approved, + uint256 indexed tokenId + ); + + // ApprovalForAll event for ERC-721 + event ApprovalForAll( + address indexed owner, + address indexed operator, + bool approved + ); + + // ERC-20 to ERC-721 Conversion event + event ERC20ToERC721(address indexed to, uint256 amount, uint256 tokenId); + + // ERC-721 to ERC-20 Conversion event + event ERC20ToERC721(address indexed to, uint256 amount, uint256[] tokenIds); + + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the number of decimals used in the token. + */ + function decimals() external view returns (uint8); + + /** + * @dev Returns the total supply of the ERC-20 tokens. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the balance of an address for ERC-20 tokens. + * @param owner The address to query the balance of. + */ + function balanceOf(address owner) external view returns (uint256); + + /** + * @dev Returns the total supply of ERC-20 tokens. + */ + function erc20TotalSupply() external view returns (uint256); + + /** + * @dev Returns the balance of an address for ERC-20 tokens. + * @param owner The address to query the balance of. + */ + function erc20BalanceOf(address owner) external view returns (uint256); + + /** + * @dev Returns the total supply of ERC-721 tokens. + */ + function erc721TotalSupply() external view returns (uint256); + + /** + * @dev Returns the balance of an address for ERC-721 tokens. + * @param owner The address to query the balance of. + */ + function erc721BalanceOf(address owner) external view returns (uint256); + + /** + * @notice Get the approved address for a single NFT + * @dev Throws if `tokenId` is not a valid NFT. + * @param tokenId The NFT to find the approved address for + * @return The approved address for this NFT, or the zero address if there is none + */ + function getApproved(uint256 tokenId) external view returns (address); + + /** + * @dev Checks if an operator is approved for all tokens of a given owner. + * @param owner The address of the token owner. + * @param operator The address of the operator to check. + */ + function isApprovedForAll( + address owner, + address operator + ) external view returns (bool); + + /** + * @dev Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner. + * @param owner The address of the token owner. + * @param spender The address of the spender. + */ + function allowance( + address owner, + address spender + ) external view returns (uint256); + + /** + * @dev Returns the array of ERC-721 token IDs owned by a specific address. + * @param owner The address to query the tokens of. + */ + function owned(address owner) external view returns (uint256[] memory); + + /** + * @dev Returns the address that owns a specific ERC-721 token. + * @param tokenId The token ID. + */ + function ownerOf(uint256 tokenId) external view returns (address erc721Owner); + + /** + * @dev Returns the URI for a specific ERC-721 token. + * @param tokenId The token ID. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); + + /** + * @dev Approve or disapprove the operator to spend or transfer all of the sender's tokens. + * @param spender The address of the spender. + * @param amountOrId The amount of ERC-20 tokens or ID of ERC-721 tokens. + */ + function approve( + address spender, + uint256 amountOrId + ) external returns (bool); + + /** + * @dev Set or unset the approval of an operator for all tokens. + * @param operator The address of the operator. + * @param approved The approval status. + */ + function setApprovalForAll(address operator, bool approved) external; + + /** + * @dev Transfer ERC-20 tokens or ERC-721 token from one address to another. + * @param from The address to transfer ERC-20 tokens or ERC-721 token from. + * @param to The address to transfer ERC-20 tokens or ERC-721 token to. + * @param amountOrId The amount of ERC-20 tokens or ID of ERC-721 tokens to transfer. + */ + function transferFrom( + address from, + address to, + uint256 amountOrId + ) external returns (bool); + + /** + * @notice Transfers the ownership of an NFT from one address to another address + * @dev Throws unless `msg.sender` is the current owner, an authorized + * operator, or the approved address for this NFT. Throws if `_rom` is + * not the current owner. Throws if `_to` is the zero address. Throws if + * `tokenId` is not a valid NFT. When transfer is complete, this function + * checks if `to` is a smart contract (code size > 0). If so, it calls + * `onERC721Received` on `to` and throws if the return value is not + * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. + * @param from The current owner of the NFT + * @param to The new owner + * @param tokenId The NFT to transfer + * @param data Additional data with no specified format, sent in call to `to` + */ + function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external payable; + + /** + * @notice Transfers the ownership of an NFT from one address to another address + * @dev This works identically to the other function with an extra data parameter, + * except this function just sets data to "". + * @param from The current owner of the NFT + * @param to The new owner + * @param tokenId The NFT to transfer + */ + function safeTransferFrom(address from, address to, uint256 tokenId) external payable; + + /** + * @dev Transfer ERC-20 tokens to an address. + * @param to The address to transfer ERC-20 tokens to. + * @param amount The amount of ERC-20 tokens to transfer. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Retrieves the unit value associated with the token. + * @return The unit value. + */ + function getUnit() external view returns (uint256); + + /** + * @dev Converts ERC-721 token to ERC-20 tokens. + * @param tokenId The unique identifier of the ERC-721 token. + */ + function erc721ToERC20(uint256 tokenId) external; + + /** + * @dev Converts ERC-20 tokens to an ERC-721 token. + * @param amount The amount of ERC-20 tokens to convert. + */ + function erc20ToERC721(uint256 amount) external; +} + + +``` +## Rationale + +Common Interface for Different Token Types: + +- Introduces a unified interface to address the fragmentation caused by separate [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) standards. +- Standardizes functions like transferFrom, mint, and burn, enabling developers to interact with both token types without implementing distinct logic. + +Transfer Functionality: + +- Includes transferFrom function for seamless movement of tokens between addresses, as it's a core component of both [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) standards. + +Minting and Burning: + +- Incorporates mint and burn functions for creating and destroying tokens, essential for managing token supply and lifecycle. + +Balance and Ownership Queries: + +- Provides functions like balanceOf and ownerOf for retrieving token balances and ownership information, crucial for both [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) tokens. + +Compatibility and Extensibility: + +- Ensures compatibility with existing [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) implementations, minimizing disruption during transition. +- Allows extension with additional functions and events for future enhancements. + +Security Considerations: + +- Implements mechanisms to prevent common issues like reentrancy attacks and overflows, ensuring the security and robustness of the unified interface. + + + +## Backwards Compatibility + + +The proposed this proposal introduces a challenge in terms of backward compatibility due to the distinct balance query mechanisms utilized by [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) standards. [ERC-20](./eip-20.md) employs `balanceOf` to check an account's token balance, while [ERC-721](./eip-721.md) uses `balanceOf` to inquire about the quantity of tokens owned by an account. To reconcile these differences, the ERC must consider providing either two separate functions catering to each standard or adopting a more generalized approach. + +### Compatibility Points + +The primary compatibility point lies in the discrepancy between [ERC-20](./eip-20.md)'s balanceOf and [ERC-721](./eip-721.md)'s balanceOf functionalities. Developers accustomed to the specific balance query methods in each standard may face challenges when transitioning to this proposal. + +### Proposed Solutions + +Dual Balance Query Functions: + +Introduce two distinct functions, `erc20BalanceOf` and `erc721TotalSupply`, to align with the conventions of [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md), respectively. Developers can choose the function based on the token type they are working with. + + + +## Security Considerations + +- Due to the dual nature of this proposal, potential differences in protocol interpretation may arise, necessitating careful consideration during development. +- Comprehensive security audits are recommended, especially during mode transitions by users, to ensure the safety of user assets. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + From ed6f7306c852624419f691d67110b4678c02f389 Mon Sep 17 00:00:00 2001 From: sshmatrix <19473027+sshmatrix@users.noreply.github.com> Date: Wed, 19 Jun 2024 22:16:56 +0530 Subject: [PATCH 040/126] Update ERC-7700: Image Size Fixes Merged by EIP-Bot. --- assets/erc-7700/images/Database.svg | 62 ++-- assets/erc-7700/images/Keygen.svg | 492 +++++++++++++++++----------- assets/erc-7700/images/L1.svg | 44 +-- assets/erc-7700/images/L2.svg | 36 +- assets/erc-7700/images/Schema.svg | 30 +- 5 files changed, 391 insertions(+), 273 deletions(-) diff --git a/assets/erc-7700/images/Database.svg b/assets/erc-7700/images/Database.svg index daf80183d7..0da7511326 100644 --- a/assets/erc-7700/images/Database.svg +++ b/assets/erc-7700/images/Database.svg @@ -8,9 +8,9 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="1000mm" - height="707mm" - viewBox="0 0 1000 707.00002" + width="538.99292mm" + height="246.26625mm" + viewBox="0 0 538.99292 246.26626" version="1.1" id="svg6" inkscape:version="1.0.2 (e86c8708, 2021-01-15)" @@ -184,11 +184,11 @@ borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:zoom="0.33322534" - inkscape:cx="1031.0773" - inkscape:cy="404.37025" + inkscape:zoom="0.21711624" + inkscape:cx="949.51301" + inkscape:cy="-145.17033" inkscape:document-units="mm" - inkscape:current-layer="g1170" + inkscape:current-layer="g1001" showgrid="false" inkscape:window-width="1426" inkscape:window-height="847" @@ -206,10 +206,10 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-33.411763,-65.062998)"> + transform="translate(-33.411763,-64.921999)"> + transform="translate(-3.5162968,-1.0583333)"> HTTP POST [signer] [signature] [approval] + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-width:0.257131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">HTTP POST [signer] [signature] [approval] [callData] + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-width:0.257131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">[callData] setValue(calldata) @@ -275,7 +283,7 @@ sodipodi:nodetypes="ccccccccccc" /> L1 + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-width:0.771;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">L1 + style="fill:none;stroke:#ff5300;stroke-opacity:1"> DB + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-width:0.771;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">DB revert StorageRoutedToDatabase(gatewayURL) + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-width:0.257131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">revert StorageRoutedToDatabase(gatewayURL) CLIENT + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.771;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">CLIENT image/svg+xml - + @@ -178,17 +178,17 @@ borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:zoom="0.23843962" - inkscape:cx="-198.86147" - inkscape:cy="1094.0833" + inkscape:zoom="0.52865915" + inkscape:cx="630.21657" + inkscape:cy="471.71105" inkscape:document-units="mm" - inkscape:current-layer="g10042-7-1-9" + inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1426" - inkscape:window-height="847" - inkscape:window-x="6" + inkscape:window-width="1470" + inkscape:window-height="891" + inkscape:window-x="0" inkscape:window-y="37" - inkscape:window-maximized="0" + inkscape:window-maximized="1" fit-margin-top="20" fit-margin-left="20" fit-margin-right="20" @@ -202,18 +202,18 @@ id="layer1" transform="translate(-26.391586,-23.472401)"> + y="98.045441" + rx="2" + ry="2" /> + d="m 272.51322,247.78398 a 5.2465189,5.2465189 0 0 0 -8.9564,-3.70983 5.2465357,5.2465357 0 0 0 3.70907,8.95636 l 6.6e-4,5e-5 6.6e-4,-5e-5 a 5.2465768,5.2465768 0 0 0 5.24591,-5.24653 z m -0.99279,2.28529 -3.20058,-0.58432 3.27015,-3.84974 a 4.840164,4.840164 0 0 1 -0.0695,4.43407 z m -5.36323,-0.97914 -0.5764,-1.61395 1.10949,-1.30613 1.68588,0.30778 0.57639,1.61394 -1.10949,1.30613 z m 5.21449,-3.84626 -2.10535,2.4785 -1.69817,-4.75491 a 4.7905362,4.7905362 0 0 1 3.11157,1.40345 4.8621746,4.8621746 0 0 1 0.69195,0.87296 z m -7.51807,-0.87296 a 4.7918942,4.7918942 0 0 1 3.26643,-1.41019 l 1.09312,3.06076 -4.96932,-0.90724 a 4.8750409,4.8750409 0 0 1 0.60977,-0.74333 z m -0.84068,1.12777 3.20058,0.58432 -3.27016,3.84975 a 4.840164,4.840164 0 0 1 0.0696,-4.43406 z m 0.14873,4.8254 2.10534,-2.47849 1.69818,4.7549 a 4.7905362,4.7905362 0 0 1 -3.11157,-1.40345 4.8621746,4.8621746 0 0 1 -0.69195,-0.87296 z m 4.25164,2.28314 -1.09312,-3.06074 1.56759,0.28621 10e-4,2.3e-4 3.40033,0.62078 a 4.8750409,4.8750409 0 0 1 -0.60977,0.74333 4.7918942,4.7918942 0 0 1 -3.26642,1.41018 z" + id="path1739-4-8-8" + style="fill:#ff4f00;fill-opacity:1;stroke:#ff4f00;stroke-width:0.418151;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + sodipodi:nodetypes="cc" /> CAIP-10 [address] @@ -349,14 +349,14 @@ r="17.812727" /> MESSAGE - - - - + style="fill:#ff2d00;fill-opacity:1;stroke:#ff2d00;stroke-width:0.278532;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 177.9369,234.44779 v 13.98443" + id="path2542-3-1" + sodipodi:nodetypes="cc" /> + + sodipodi:nodetypes="ccccc" /> - + sodipodi:nodetypes="ccccc" /> signature salt + transform="matrix(1.0510655,0,0,-1.0510655,79.506092,414.78574)"> - - - - keypair + sodipodi:nodetypes="ccccccccc" /> hashkey - - + sodipodi:nodetypes="cc" /> @@ -736,17 +696,17 @@ @@ -756,12 +716,12 @@ id="path1062-2-9-3-6" sodipodi:nodetypes="cccc" /> @@ -794,18 +754,18 @@ ry="0.6067974" /> CAIP-02 keys - - - @@ -1048,17 +986,15 @@ id="tspan2969" /> 256 + id="tspan2163-0-2-6-4-3-6-5-5">256 - - - - - - - + sodipodi:nodetypes="ccc" /> 2 + + + + + + + + + + + + + + + + + + spice + + + + + + + + pepper + + + + + + + + + + + diff --git a/assets/erc-7700/images/L1.svg b/assets/erc-7700/images/L1.svg index 4830d0c67a..5b96abeecd 100644 --- a/assets/erc-7700/images/L1.svg +++ b/assets/erc-7700/images/L1.svg @@ -184,11 +184,11 @@ borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:zoom="0.26187232" - inkscape:cx="1322.4215" - inkscape:cy="246.92061" + inkscape:zoom="0.45148859" + inkscape:cx="1007.3131" + inkscape:cy="395.27208" inkscape:document-units="mm" - inkscape:current-layer="layer1" + inkscape:current-layer="g1285-3" showgrid="false" inkscape:window-width="1426" inkscape:window-height="847" @@ -209,11 +209,11 @@ transform="translate(-33.411763,-65.062998)"> + transform="translate(-7.8102005,9.7303483)" + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> Execute on Mainnet [contract] [callData] + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-width:0.257131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">Execute on Mainnet [contract] [callData] setValue(calldata) + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9.26868999999999943px;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';stroke:#0085ff;stroke-width:0.309;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#0085ff;fill-opacity:1;">setValue(calldata) + transform="translate(39.487066,46.742856)" + style="fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-opacity:1"> revert StorageRoutedToL1(contract) + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-width:0.257131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">revert StorageRoutedToL1(contract) + transform="translate(-45.85781,129.10179)" + style="fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-opacity:1"> response + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-width:0.257131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">response diff --git a/assets/erc-7700/images/L2.svg b/assets/erc-7700/images/L2.svg index b63cecba00..992e67b6a6 100644 --- a/assets/erc-7700/images/L2.svg +++ b/assets/erc-7700/images/L2.svg @@ -184,11 +184,11 @@ borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:zoom="0.44908742" - inkscape:cx="1559.1201" - inkscape:cy="537.66515" + inkscape:zoom="0.32431882" + inkscape:cx="937.13724" + inkscape:cy="456.37038" inkscape:document-units="mm" - inkscape:current-layer="g1170" + inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="1426" inkscape:window-height="847" @@ -209,7 +209,7 @@ transform="translate(-33.411763,-65.062998)"> Execute on L2 [contract] [callData] + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#ff5300;fill-opacity:1;stroke:#ff5300;stroke-width:0.257131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">Execute on L2 [contract] [callData] setValue(calldata) + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9.26869px;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-width:0.309;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">setValue(calldata) revert StorageRoutedToL2(chainId, contract) + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'SF Mono';-inkscape-font-specification:'SF Mono';fill:#0085ff;fill-opacity:1;stroke:#0085ff;stroke-width:0.257131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">revert StorageRoutedToL2(chainId, contract) response diff --git a/assets/erc-7700/images/Schema.svg b/assets/erc-7700/images/Schema.svg index a91b558df2..e78b204601 100644 --- a/assets/erc-7700/images/Schema.svg +++ b/assets/erc-7700/images/Schema.svg @@ -26,7 +26,7 @@ image/svg+xml - + @@ -234,9 +234,9 @@ borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:zoom="0.54495921" - inkscape:cx="911.64201" - inkscape:cy="649.62539" + inkscape:zoom="0.53003188" + inkscape:cx="771.81564" + inkscape:cy="587.05652" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -346,7 +346,7 @@ x="201.24846" y="154.62598" /> + style="fill:#ffffff;fill-opacity:0.14112903;stroke:#ffffff;stroke-opacity:1;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"> SOLANA + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.75192px;font-family:SPOTNIK;-inkscape-font-specification:SPOTNIK;text-align:end;text-anchor:end;fill:#c5c5c5;fill-opacity:1;stroke:#c5c5c5;stroke-width:0.49;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="tspan1256-8-1-1-1">SOLANA @@ -1610,7 +1608,7 @@ y="297.26746" id="tspan2163-0-2-6-4-3-0-7-4-0-1-9-4">metadata From 612c3f025244f7215e02c5b0c98d16384f86ede8 Mon Sep 17 00:00:00 2001 From: Vidor <1101164+V1d0r@users.noreply.github.com> Date: Thu, 20 Jun 2024 09:28:25 +0100 Subject: [PATCH 041/126] Update ERC-7578: Security Considerations Merged by EIP-Bot. --- ERCS/erc-7578.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7578.md b/ERCS/erc-7578.md index 8c9ed1052e..26489b91e1 100644 --- a/ERCS/erc-7578.md +++ b/ERCS/erc-7578.md @@ -2,7 +2,7 @@ eip: 7578 title: Physical Asset Redemption description: Provides the holder of physical asset backed NFTs readily available real-world information on the underlying physical asset. -author: Lee Vidor (@V1d0r) , David Tan , Lee Smith +author: Lee Vidor (@V1d0r) , David Tan , Lee Smith , Gabriel Stoica (@gabrielstoica) discussions-to: https://ethereum-magicians.org/t/erc-7578-physical-asset-redemption/17556 status: Draft type: Standards Track @@ -232,7 +232,7 @@ contract ERC7578 is ERC721, IERC7578 { ## Security Considerations -For further discussion. +To ensure the authenticity of a token's properties, the `setProperties()` method should only be called by a trusted externally owned account (EOA) or contract. This trusted entity must verify that the properties accurately reflect the real physical attributes of the token. ## Copyright From 3317a6b708b44a0e72cef8d8485c9a00390643a0 Mon Sep 17 00:00:00 2001 From: Elias Rad <146735585+nnsW3@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:44:00 +0300 Subject: [PATCH 042/126] Update ERC-1046: Rectify typographical inaccuracies (#494) * fix typos eip-1.md * fix typos erc-1046.md * fix typos erc-1056.md * fix typos erc-1062.md * fix typos erc-1066.md * fix typos erc-1077.md * fix typos erc-1078.md * fix typos erc-1078.md --- ERCS/eip-1.md | 2 +- ERCS/erc-1046.md | 4 ++-- ERCS/erc-1056.md | 2 +- ERCS/erc-1062.md | 4 ++-- ERCS/erc-1066.md | 2 +- ERCS/erc-1077.md | 4 ++-- ERCS/erc-1078.md | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ERCS/eip-1.md b/ERCS/eip-1.md index 4e683737db..08df84e8ed 100644 --- a/ERCS/eip-1.md +++ b/ERCS/eip-1.md @@ -91,7 +91,7 @@ A PR moving an EIP from Last Call to Final SHOULD contain no changes other than >*EIP Authors are notified of any algorithmic change to the status of their EIP* -**Withdrawn** - The EIP Author(s) have withdrawn the proposed EIP. This state has finality and can no longer be resurrected using this EIP number. If the idea is pursued at later date it is considered a new proposal. +**Withdrawn** - The EIP Author(s) have withdrawn the proposed EIP. This state has finality and can no longer be resurrected using this EIP number. If the idea is pursued at a later date it is considered a new proposal. **Living** - A special status for EIPs that are designed to be continually updated and not reach a state of finality. This includes most notably EIP-1. diff --git a/ERCS/erc-1046.md b/ERCS/erc-1046.md index 2aaaac64ca..8151c125cb 100644 --- a/ERCS/erc-1046.md +++ b/ERCS/erc-1046.md @@ -58,13 +58,13 @@ interface InteroperabilityMetadata { /** * This MUST be true if this is ERC-721 Token Metadata, otherwise, this MUST be omitted. - * Setting this to true indicates to wallets that the address should be treated as a ERC-721 token. + * Setting this to true indicates to wallets that the address should be treated as an ERC-721 token. **/ erc721?: boolean | undefined; /** * This MUST be true if this is ERC-1155 Token Metadata, otherwise, this MUST be omitted. - * Setting this to true indicates to wallets that the address should be treated as a ERC-1155 token. + * Setting this to true indicates to wallets that the address should be treated as an ERC-1155 token. **/ erc1155?: boolean | undefined; } diff --git a/ERCS/erc-1056.md b/ERCS/erc-1056.md index cb468d8eb1..31c33e298e 100644 --- a/ERCS/erc-1056.md +++ b/ERCS/erc-1056.md @@ -222,7 +222,7 @@ Contract Events are a useful feature for storing data from smart contracts exclu 2. Lookup all events for given identity address using web3, but only for the `previousChange` block -3. Do something with event +3. Do something with the event 4. Find `previousChange` from the event and repeat diff --git a/ERCS/erc-1062.md b/ERCS/erc-1062.md index 0f304b6cb2..2e555d1cdc 100644 --- a/ERCS/erc-1062.md +++ b/ERCS/erc-1062.md @@ -18,9 +18,9 @@ The following standard details the implementation of how to combine the IPFS cry We think that this implementation is not only aim to let more developers and communities to provide more use cases, but also leverage the human-readable features to gain more user adoption accessing decentralized resources. We considered the IPFS ENS resolver mapping standard a cornerstone for building future Web3.0 service. ## Motivation -To build fully decentralized web service, itโ€™s necessary to have a decentralized file storage system. Here comes the IPFS, for three following advantages : +To build a fully decentralized web service, itโ€™s necessary to have a decentralized file storage system. Here comes the IPFS, for three following advantages : - Address large amounts of data, and has unique cryptographic hash for every record. -- Since IPFS is also based on peer to peer network, it can be really helpful to deliver large amounts of data to users, with safer way and lower the millions of cost for the bandwidth. +- Since IPFS is also based on peer to peer network, it can be really helpful to deliver large amounts of data to users, in a safer way and lower the millions of cost for the bandwidth. - IPFS stores files in high efficient way via tracking version history for every file, and removing the duplications across the network. Those features makes perfect match for integrating into ENS, and these make users can easily access content through ENS, and show up in the normal browser. diff --git a/ERCS/erc-1066.md b/ERCS/erc-1066.md index 5b29e2dd32..7f60fe566e 100644 --- a/ERCS/erc-1066.md +++ b/ERCS/erc-1066.md @@ -480,7 +480,7 @@ AwesomeCoin DEX TraderBot Status codes are encoded as a `byte`. Hex values break nicely into high and low nibbles: `category` and `reason`. For instance, `0x01` stands for general success (ie: `true`) and `0x00` for general failure (ie: `false`). -As a general approach, all even numbers are blocking conditions (where the receiver does not have control), and odd numbers are nonblocking (the receiver is free to contrinue as they wish). This aligns both a simple bit check with the common encoding of Booleans. +As a general approach, all even numbers are blocking conditions (where the receiver does not have control), and odd numbers are nonblocking (the receiver is free to continue as they wish). This aligns both a simple bit check with the common encoding of Booleans. `bytes1` is very lightweight, portable, easily interoperable with `uint8`, cast from `enum`s, and so on. diff --git a/ERCS/erc-1077.md b/ERCS/erc-1077.md index 5ada67aea4..4ae82ddc2c 100644 --- a/ERCS/erc-1077.md +++ b/ERCS/erc-1077.md @@ -19,7 +19,7 @@ Allows users to offer [EIP-20] token for paying the gas used in a call. ## Abstract -A main barrier for adoption of DApps is the requirement of multiple tokens for executing in chain actions. Allowing users to sign messages to show intent of execution, but allowing a third party relayer to execute them can circumvent this problem, while ETH will always be required for ethereum transactions, it's possible for smart contract to take [EIP-191] signatures and forward a payment incentive to an untrusted party with ETH for executing the transaction. +A main barrier for the adoption of DApps is the requirement of multiple tokens for executing in chain actions. Allowing users to sign messages to show intent of execution, but allowing a third party relayer to execute them can circumvent this problem, while ETH will always be required for ethereum transactions, it's possible for smart contract to take [EIP-191] signatures and forward a payment incentive to an untrusted party with ETH for executing the transaction. ## Motivation @@ -83,7 +83,7 @@ In order to be compliant, the transaction **MUST** request to sign a "messageHas The fields **MUST** be constructed as this method: -The first and second fields are to make it [EIP-191] compliant. Starting a transaction with `byte(0x19)` ensure the signed data from being a [valid ethereum transaction](https://github.com/ethereum/wiki/wiki/RLP). The second argument is a version control byte. The third being the validator address (the account contract address) according to version 0 of [EIP-191]. The remaining arguments being the application specific data for the gas relay: chainID as per [EIP-1344], execution nonce, execution data, agreed gas Price, gas limit of gas relayed call, gas token to pay back and gas relayer authorized to receive reward. +The first and second fields are to make it [EIP-191] compliant. Starting a transaction with `byte(0x19)` ensure the signed data from being a [valid ethereum transaction](https://github.com/ethereum/wiki/wiki/RLP). The second argument is a version control byte. The third being the validator address (the account contract address) according to version 0 of [EIP-191]. The remaining arguments being the application specific data for the gas relay: chainID as per [EIP-1344], execution nonce, execution data, agreed gas Price, gas limit of gas relayed call, gas token to pay back and gas relayer authorized to receive the reward. The [EIP-191] message must be constructed as following: ```solidity diff --git a/ERCS/erc-1078.md b/ERCS/erc-1078.md index 7990475fbc..ee1a697c35 100644 --- a/ERCS/erc-1078.md +++ b/ERCS/erc-1078.md @@ -12,12 +12,12 @@ requires: 191, 681, 725, 1077 ## Abstract -This presents a method to replace the usual signup/login design pattern with a minimal ethereum native scheme, that doesnโ€™t require passwords, backing up private keys nor typing seed phrases. From the user point of view it will be very similar to patterns theyโ€™re already used to with second factor authentication (without relying in a central server), but for dapp developers it requires a new way to think about ethereum transactions. +This presents a method to replace the usual signup/login design pattern with a minimal ethereum native scheme, that doesnโ€™t require passwords, backing up private keys nor typing seed phrases. From the user's point of view it will be very similar to patterns theyโ€™re already used to with second factor authentication (without relying in a central server), but for dapp developers it requires a new way to think about ethereum transactions. ## Simple Summary -The unique identifier of the user is a contract which implements both Identity and the Executable Signed Messages ERCs. The user should not need provide this address directly, only a ens name pointing to it. These types of contracts are indirectly controlled by private keys that can sign messages indicating intents, which are then deployed to the contract by a third party (or a decentralized network of deployers). +The unique identifier of the user is a contract that implements both Identity and the Executable Signed Messages ERCs. The user should not need provide this address directly, only a ens name pointing to it. These types of contracts are indirectly controlled by private keys that can sign messages indicating intents, which are then deployed to the contract by a third party (or a decentralized network of deployers). In this context, therefore, a device "logging into" an app using an identity, means that the device will generate a private key locally and then request an authorization to add that key as one of the signers of that identity, with a given set of permissions. Since that private key is only used for signing messages, it is not required to hold ether, tokens or assets, and if lost, it can be simply be replaced by a new one โ€“ย the user's funds are kept on the identity contract. From dd8834d280f7347c47958e680eaca93bb1a61c82 Mon Sep 17 00:00:00 2001 From: Francesco Sullo Date: Thu, 20 Jun 2024 21:58:30 -0700 Subject: [PATCH 043/126] Update ERC-7656: Update naming for clarity Merged by EIP-Bot. --- ERCS/erc-7656.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ERCS/erc-7656.md b/ERCS/erc-7656.md index f7f0eac2a6..bceea4f3b7 100644 --- a/ERCS/erc-7656.md +++ b/ERCS/erc-7656.md @@ -1,7 +1,7 @@ --- eip: 7656 -title: Generalized Token-Linked Contracts -description: Define a registry for generic contracts linked to a specific NFT +title: Generalized Token-Linked Services +description: Define a registry for generic services linked to a specific NFT author: Francesco Sullo (@sullof) discussions-to: https://ethereum-magicians.org/t/variation-to-erc6551-to-deploy-any-kind-of-contract-linked-to-an-nft/19223 status: Draft @@ -13,15 +13,15 @@ requires: 165, 1167, 5313, 6551 ## Abstract -This proposal introduces a variation of [ERC-6551](./eip-6551.md) that extends to all types of contracts linked to non-fungible tokens (NFTs), i.e., contracts owned by a single NFT and thus by the owner of the NFT. It achieves this goal using generic language for functions, errors, and events, and avoids conflicting with the strict restrictions imposed by the original proposal. +This proposal introduces a variation of [ERC-6551](./eip-6551.md) that extends to all types of services linked to non-fungible tokens (NFTs), i.e., contracts extending an NFT, owned by a single NFT and thus by the owner of the NFT. It achieves this goal using generic language for functions, errors, and events, and avoids conflicting with the strict restrictions imposed by the original proposal. ## Motivation [ERC-6551](./eip-6551.md) aims to bind smart accounts to tokens, allowing its registry to deploy accounts owned by a specific tokenID. The issue we attempt to address with this new proposal is that [ERC-6551](./eip-6551.md) explicitly requires any contract deployed via the `ERC6551Registry` to implement `IERC6551Account` and `IERC6551Execute`, i.e., it must be an account. This requirement is underscored by the choices for the names of functions and events in the interface. Additionally, [ERC-6551](./eip-6551.md) specifies that the `ERC6551Registry` smart contract is deployed as a singleton at a specific address on any chain. Due to this centralization of services, projects building on it are prone to consider any contract deployed via that registry that is not an account as spam or invalid. -With this new ERC, we propose a more generic registry that uses generic function/event names to allow the deployment of any kind of contract that makes sense when associated with an NFT, so that the contract is under the full control of the NFT's owner. Since one of this proposal's goals is flexibility, there is no expectation for an `ERC7656Registry` contract to be deployed as a singleton, allowing any project to adjust it to their needs. Consequently, we require that any registry explicitly supports the `IERC7656Registry` interface. +With this new ERC, we propose a more generic registry that uses generic function/event names to allow the deployment of any kind of contract that makes sense when associated with an NFT, so that the contract is under the full control of the NFT's owner. In comparison with [ERC-6551](./eip-6551.md), since one of this proposal's goals is flexibility, there is no expectation for an `ERC7656Registry` contract to be deployed as a singleton, allowing any project to adjust it to their needs; consequently, we require that any registry explicitly supports the `IERC7656Registry` interface. -The expansion of the registry's capabilities to manage contracts beyond accounts provides several advantages: +The expansion of the registry's capabilities to manage contracts implementing any kind of service beyond accounts provides several advantages: - **Flexibility**: Developers can allow NFTs to interact with a broader range of linked contracts, unlocking new use cases and functionalities (lending systems, vested asset distribution, fractional ownership, identity, etc.) - **Compatibility**: By ensuring that account-like contracts can still be identified as such, the proposal maintains backward compatibility with [ERC-6551](./eip-6551.md). @@ -113,10 +113,14 @@ ERC-1167 Footer (15 bytes) (32 bytes) ``` -Any contract created using a `ERC7656Registry` SHOULD implement the `IERC7656Contract` interface: +Any contract created using a `ERC7656Registry` SHOULD implement the `IERC7656Service` interface: ```solidity +<<<<<<< HEAD +interface IERC7656Service { +======= interface IERC7656Linked { +>>>>>>> master /** * @notice Returns the token linked to the contract * @return chainId The chainId of the token @@ -263,10 +267,10 @@ contract ERC7656Registry is IERC7656Registry { } ``` -A simple implementation of `IERC7656Linked`: +An example of implementation of `IERC7656Service`: ```solidity -contract LinkedContract is IERC7656Linked, EIP5313 { +contract LinkedService is IERC7656Service, EIP5313 { function token() public view virtual returns (uint256, address, uint256) { bytes memory footer = new bytes(0x60); From 576f7f7bb823be8883f09536f057f8b7ea5f4927 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Sat, 22 Jun 2024 22:53:42 +0200 Subject: [PATCH 044/126] Update ERC-7540: Remove inequality Merged by EIP-Bot. --- ERCS/erc-7540.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ERCS/erc-7540.md b/ERCS/erc-7540.md index c91f0c19da..5a675875ce 100644 --- a/ERCS/erc-7540.md +++ b/ERCS/erc-7540.md @@ -74,8 +74,6 @@ After submission, Requests go through Pending, Claimable, and Claimed stages. An Note that `maxDeposit` increases and decreases in sync with `claimableDepositRequest`. -An important Vault inequality is that following a Request(s), the cumulative requested quantity MUST be more than `pendingDepositRequest + maxDeposit - claimed`. The inequality may come from fees or other state transitions outside implemented by Vault logic such as cancellation of a Request, otherwise, this would be a strict equality. - Requests MUST NOT skip or otherwise short-circuit the Claim state. In other words, to initiate and claim a Request, a user MUST call both request* and the corresponding claim* function separately, even in the same block. Vaults MUST NOT "push" tokens onto the user after a Request, users MUST "pull" the tokens via the Claim function. For asynchronous Vaults, the exchange rate between `shares` and `assets` including fees and yield is up to the Vault implementation. In other words, pending redemption Requests MAY NOT be yield-bearing and MAY NOT have a fixed exchange rate. @@ -192,7 +190,7 @@ The output `requestId` is used to discriminate the request along with the `contr In either case, the `shares` MUST be removed from the custody of `owner` upon `requestRedeem` and burned by the time the request is Claimed. -Redeem Request approval of `shares` for a `msg.sender` NOT equal to `owner` may come either from ERC-20 approval over the `shares` of `owner` or if the `owner` has approved the `msg.sender` as an operator. +Redeem Request approval of `shares` for a `msg.sender` NOT equal to `owner` may come either from ERC-20 approval over the `shares` of `owner` or if the `owner` has approved the `msg.sender` as an operator. This MUST be consistent with similar behaviour pointed out in [ERC-6909](./eip-6909.md), within "Approvals and Operators" section: "In accordance with the transferFrom method, spenders with operator permission are not subject to allowance restrictions, spenders with infinite approvals SHOULD NOT have their allowance deducted on delegated transfers, but spenders with non-infinite approvals MUST have their balance deducted on delegated transfers." When the Request is Claimable, `claimableRedeemRequest` will be increased for the `controller`. `redeem` or `withdraw` can subsequently be called by `controller` to receive `assets`. A Request MAY transition straight to Claimable state but MUST NOT skip the Claimable state. From bbfa0b69473de1b7980706d0243e6fba15aed4b8 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:05:34 +0200 Subject: [PATCH 045/126] Update ERC-7540: Move to Final Merged by EIP-Bot. --- ERCS/erc-7540.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ERCS/erc-7540.md b/ERCS/erc-7540.md index 5a675875ce..f072211639 100644 --- a/ERCS/erc-7540.md +++ b/ERCS/erc-7540.md @@ -4,8 +4,7 @@ title: Asynchronous ERC-4626 Tokenized Vaults description: Extension of ERC-4626 with asynchronous deposit and redemption support author: Jeroen Offerijns (@hieronx), Alina Sinelnikova (@ilinzweilin), Vikram Arun (@vikramarun), Joey Santoro (@joeysantoro), Farhaan Ali (@0xfarhaan), Joรฃo Martins (@0xTimepunk) discussions-to: https://ethereum-magicians.org/t/eip-7540-asynchronous-erc-4626-tokenized-vaults/16153 -status: Last Call -last-call-deadline: 2024-06-25 +status: Final type: Standards Track category: ERC created: 2023-10-18 From 7cd433ae635636fd9ae2859c0e58a9c53881543b Mon Sep 17 00:00:00 2001 From: Gary Ghayrat <61768337+garyghayrat@users.noreply.github.com> Date: Tue, 25 Jun 2024 10:08:04 -0400 Subject: [PATCH 046/126] Update ERC-6538: Move to Final Merged by EIP-Bot. --- ERCS/erc-6538.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ERCS/erc-6538.md b/ERCS/erc-6538.md index 3b25731dc0..fcb1ffc88b 100644 --- a/ERCS/erc-6538.md +++ b/ERCS/erc-6538.md @@ -4,8 +4,7 @@ title: Stealth Meta-Address Registry description: A canonical contract for entities to register stealth meta-addresses directly or through a third party using signatures. author: Matt Solomon (@mds1), Toni Wahrstรคtter (@nerolation), Ben DiFrancesco (@apbendi), Vitalik Buterin (@vbuterin), Gary Ghayrat (@garyghayrat) discussions-to: https://ethereum-magicians.org/t/stealth-meta-address-registry/12888 -status: Last Call -last-call-deadline: 2024-06-11 +status: Final type: Standards Track category: ERC created: 2023-01-24 From c591605609fd622a2fb24fb3a535009f2cf0f87e Mon Sep 17 00:00:00 2001 From: Gavin Date: Tue, 25 Jun 2024 22:28:47 +0800 Subject: [PATCH 047/126] Update ERC-5625: Move to Review Merged by EIP-Bot. --- ERCS/erc-5625.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-5625.md b/ERCS/erc-5625.md index d12d6cae8a..dc480c8b37 100644 --- a/ERCS/erc-5625.md +++ b/ERCS/erc-5625.md @@ -4,7 +4,7 @@ title: NFT Metadata JSON Schema dStorage Extension description: Add a dStorage property to non-fungible tokens (NFTs) metadata JSON schema to provide decentralized storage information of NFT assets author: Gavin Fu (@gavfu) discussions-to: https://ethereum-magicians.org/t/eip-5625-nft-metadata-json-schema-dstorage-extension/10754 -status: Stagnant +status: Review type: Standards Track category: ERC created: 2022-09-08 From 24edb27e943d23c565ee85dbaffb6ba48ed00184 Mon Sep 17 00:00:00 2001 From: Gavin Date: Tue, 25 Jun 2024 22:30:14 +0800 Subject: [PATCH 048/126] Update ERC-7588: Move to Review Merged by EIP-Bot. --- ERCS/erc-7588.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7588.md b/ERCS/erc-7588.md index 81ca154ec2..2cd4682820 100644 --- a/ERCS/erc-7588.md +++ b/ERCS/erc-7588.md @@ -4,7 +4,7 @@ title: Blob Transactions Metadata JSON Schema description: Attaching metadata to blobs carried by blob transactions author: Gavin Fu (@gavfu), Leo Wang (@wanglie1986), Bova Chen (@appoipp), Aiden X (@4ever9) discussions-to: https://ethereum-magicians.org/t/erc7588-attaching-metadata-to-blobs-carried-by-blob-transactions/17873 -status: Draft +status: Review type: Standards Track category: ERC created: 2024-01-01 From 65f29dd58f9db148f6c5b076d32e69ebaa411147 Mon Sep 17 00:00:00 2001 From: indreams Date: Tue, 25 Jun 2024 07:33:20 -0700 Subject: [PATCH 049/126] Update ERC-7015: Move to Review Merged by EIP-Bot. --- ERCS/erc-7015.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ERCS/erc-7015.md b/ERCS/erc-7015.md index 3cb425dfb9..ac21a71b1e 100644 --- a/ERCS/erc-7015.md +++ b/ERCS/erc-7015.md @@ -2,9 +2,9 @@ eip: 7015 title: NFT Creator Attribution description: Extending NFTs with cryptographically secured creator attribution. -author: Carlos Flores (@strollinghome) +author: indreams (@strollinghome) discussions-to: https://ethereum-magicians.org/t/eip-authorship-attribution-for-erc721/14244 -status: Draft +status: Review type: Standards Track category: ERC created: 2023-05-11 @@ -29,7 +29,7 @@ This EIP takes advantage of the fact that contract addresses can be precomputed **Signing Mechanism** -Creator consent is given by signing anย [EIP-712](./eip-712.md)ย compatible message; all signatures compliant with this EIP MUST include all fields defined. The struct signed can be any arbitrary data that defines how to create the token; it must hashed in an EIP-712 compatible format with a proper EIP-712 domain. +Creator consent is given by signing anย [EIP-712](./eip-712.md)ย compatible message; all signatures compliant with this EIP MUST include all fields defined. The struct signed can be any arbitrary data that defines how to create the token; it must hashed in an EIP-712 compatible format with a proper EIP-712 domain. The following shows some examples of structs that could be encoded into `structHash` (defined below): @@ -48,7 +48,7 @@ struct TokenCreation { Creator attribution is given through a signature verification that MUST be verified by the NFT contract being deployed and an event that MUST be emitted by the NFT contract during the deployment transaction. The event includes all the necessary fields for reconstructing the signed digest and validating the signature to ensure it matches the specified creator. The event name is `CreatorAttribution` and includes the following fields: - `structHash`: hashed information for deploying the NFT contract (e.g. name, symbol, admins etc). This corresponds to the value `hashStruct` as defined in the [EIP-712 definition of hashStruct](./eip-712.md#definition-of-hashstruct) standard. -- `domainName`: the domain name of the contract verifying the singature (for EIP-712 signature validation). +- `domainName`: the domain name of the contract verifying the singature (for EIP-712 signature validation). - `version`: the version of the contract verifying the signature (for EIP-712 signature validation) - `creator`: the creator's account - `signature`: the creatorโ€™s signature @@ -57,11 +57,11 @@ The event is defined as follows: ```solidity event CreatorAttribution( - bytes32 structHash, - string domainName, - string version, + bytes32 structHash, + string domainName, + string version, address creator, - bytes signature + bytes signature ); ``` From 3121e32d6cad50090a3abc3d2e9f614f248ddace Mon Sep 17 00:00:00 2001 From: Peter Kohl-Landgraf <32535675+pekola@users.noreply.github.com> Date: Tue, 25 Jun 2024 21:03:10 +0200 Subject: [PATCH 050/126] Update ERC-6123: enhance termination functionality to include termination terms (as string) Merged by EIP-Bot. --- ERCS/erc-6123.md | 12 ++++++------ assets/erc-6123/contracts/ISDC.sol | 8 ++++---- assets/erc-6123/contracts/SDC.sol | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ERCS/erc-6123.md b/ERCS/erc-6123.md index 33fd88b5c8..03434e73f2 100644 --- a/ERCS/erc-6123.md +++ b/ERCS/erc-6123.md @@ -106,18 +106,18 @@ function afterTransfer(uint256 transactionHash, bool success) external; #### Trade Termination: `requestTermination` -Allows an eligible party to request a mutual termination with a termination amount she is willing to pay +Allows an eligible party to request a mutual termination with a termination amount she is willing to pay and provide further termination terms (e.g. an XML) ```solidity -function requestTradeTermination(string memory tradeId, int256 _terminationPayment) external; +function requestTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; ``` #### Trade Termination: `confirmTradeTermination` -Allows an eligible party to confirm a previously requested (mutual) trade termination, including termination payment value +Allows an eligible party to confirm a previously requested (mutual) trade termination, including termination payment value and termination terms ```solidity -function confirmTradeTermination(string memory tradeId, int256 _terminationPayment) external; +function confirmTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; ``` ### Trade Events @@ -171,7 +171,7 @@ event TradeSettlementPhase(); Emitted when termination request is initiated by a counterparty ```solidity -event TradeTerminationRequest(address cpAddress, string tradeId); +event TradeTerminationRequest(address cpAddress, string tradeId, string terminationTerms); ``` #### TradeTerminationConfirmed @@ -179,7 +179,7 @@ event TradeTerminationRequest(address cpAddress, string tradeId); Emitted when termination request is confirmed by a counterparty ```solidity -event TradeTerminationConfirmed(address cpAddress, string tradeId); +event TradeTerminationConfirmed(address cpAddress, string tradeId, string terminationTerms); ``` #### TradeTerminated diff --git a/assets/erc-6123/contracts/ISDC.sol b/assets/erc-6123/contracts/ISDC.sol index 1c14923c7a..ce1af245c1 100644 --- a/assets/erc-6123/contracts/ISDC.sol +++ b/assets/erc-6123/contracts/ISDC.sol @@ -112,14 +112,14 @@ interface ISDC { * @param cpAddress the address of the requesting party * @param tradeId the trade identifier which is supposed to be terminated */ - event TradeTerminationRequest(address cpAddress, string tradeId); + event TradeTerminationRequest(address cpAddress, string tradeId, string terminationTerms); /** * @dev Emitted when early termination request is confirmed by the opposite party * @param cpAddress the party which confirms the trade termination * @param tradeId the trade identifier which is supposed to be terminated */ - event TradeTerminationConfirmed(address cpAddress, string tradeId); + event TradeTerminationConfirmed(address cpAddress, string tradeId, string terminationTerms); /** * @dev Emitted when trade processing is halted @@ -186,12 +186,12 @@ interface ISDC { * @dev emits a {TradeTerminationRequest} * @param tradeId the trade identifier which is supposed to be terminated */ - function requestTradeTermination(string memory tradeId, int256 _terminationPayment) external; + function requestTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; /** * @notice Called from a party to confirm an incepted termination, which might trigger a final settlement before trade gets closed * @dev emits a {TradeTerminationConfirmed} * @param tradeId the trade identifier of the trade which is supposed to be terminated */ - function confirmTradeTermination(string memory tradeId, int256 _terminationPayment) external; + function confirmTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; } diff --git a/assets/erc-6123/contracts/SDC.sol b/assets/erc-6123/contracts/SDC.sol index 1bf032fa64..12a1e2a9d6 100644 --- a/assets/erc-6123/contracts/SDC.sol +++ b/assets/erc-6123/contracts/SDC.sol @@ -173,11 +173,11 @@ abstract contract SDC is ISDC { * TerminationRequest is emitted * can be called only when ProcessState = Funded and TradeState = Active */ - function requestTradeTermination(string memory _tradeId, int256 _terminationPayment) external override onlyCounterparty onlyWhenSettled { + function requestTradeTermination(string memory _tradeId, int256 _terminationPayment, string memory terminationTerms) external override onlyCounterparty onlyWhenSettled { require(keccak256(abi.encodePacked(tradeID)) == keccak256(abi.encodePacked(_tradeId)), "Trade ID mismatch"); - uint256 hash = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment))); + uint256 hash = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment,terminationTerms))); pendingRequests[hash] = msg.sender; - emit TradeTerminationRequest(msg.sender, _tradeId); + emit TradeTerminationRequest(msg.sender, _tradeId, terminationTerms); } /* @@ -186,14 +186,14 @@ abstract contract SDC is ISDC { * confirming party generates same hash, looks into pendingRequests, if entry is found with correct address, tradeState is put to terminated * can be called only when ProcessState = Funded and TradeState = Active */ - function confirmTradeTermination(string memory _tradeId, int256 _terminationPayment) external override onlyCounterparty onlyWhenSettled { + function confirmTradeTermination(string memory _tradeId, int256 _terminationPayment, string memory terminationTerms) external override onlyCounterparty onlyWhenSettled { address pendingRequestParty = msg.sender == party1 ? party2 : party1; - uint256 hashConfirm = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment))); + uint256 hashConfirm = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment,terminationTerms))); require(pendingRequests[hashConfirm] == pendingRequestParty, "Confirmation of termination failed due to wrong party or missing request"); delete pendingRequests[hashConfirm]; mutuallyTerminated = true; terminationPayment = _terminationPayment; - emit TradeTerminationConfirmed(msg.sender, _tradeId); + emit TradeTerminationConfirmed(msg.sender, _tradeId, terminationTerms); /* Trigger final Settlement */ tradeState = TradeState.Valuation; emit TradeSettlementRequest(tradeData, settlementData[settlementData.length - 1]); From c6e8914cafa608f6a0b38ee0eee0c4fdcae109e5 Mon Sep 17 00:00:00 2001 From: bsangs <49545238+bsangs@users.noreply.github.com> Date: Wed, 26 Jun 2024 21:58:18 +0900 Subject: [PATCH 051/126] Update ERC-6900: Fix typo in responsibilities section Merged by EIP-Bot. --- ERCS/erc-6900.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index cec5f898dc..6cd2d28886 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -434,7 +434,7 @@ struct PluginManifest { ### Expected behavior -#### Responsibilties of `StandardExecutor` and `PluginExecutor` +#### Responsibilities of `StandardExecutor` and `PluginExecutor` `StandardExecutor` functions are used for open-ended calls to external addresses. From b5d679eb9b2cf2c3fc8ea074313861b2661bbed8 Mon Sep 17 00:00:00 2001 From: Konrad Date: Wed, 26 Jun 2024 17:56:30 +0100 Subject: [PATCH 052/126] Update ERC-7579: Add missing execution encoding specs Merged by EIP-Bot. --- ERCS/erc-7579.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ERCS/erc-7579.md b/ERCS/erc-7579.md index 6ecc71f62a..f7f997ee35 100644 --- a/ERCS/erc-7579.md +++ b/ERCS/erc-7579.md @@ -107,6 +107,12 @@ Here is a visual representation of the execution mode: Accounts are NOT REQUIRED to implement all execution modes. The account MUST declare what modes are supported in `supportsAccountMode` (see below) and if a mode is requested that is not supported by the account, the account MUST revert. +The account MUST encode the execution data the following ways: + +- For single calls, the `target`, `value` and `callData` are packed in this order (ie `abi.encodePacked` in Solidity). +- For delegatecalls, the `target` and `callData` are packed in this order (ie `abi.encodePacked` in Solidity). +- For batch calls, the `targets`, `values` and `callDatas` are put into an array of `Exeuction` structs that includes these fields in this order (ie `Execution(address target, uint256 value, bytes memory callData)`). Then, this array is encoded with padding (ie `abi.encode` in Solidity). + #### Account configurations To comply with this standard, smart accounts MUST implement the account config interface below: From a31bcc963fefa6ca479062da8d26411ac8aeda08 Mon Sep 17 00:00:00 2001 From: Lanyin Z <106770708+lanyinzly@users.noreply.github.com> Date: Thu, 27 Jun 2024 00:03:40 -0400 Subject: [PATCH 053/126] Update ERC-7527: Update erc-7527.md Merged by EIP-Bot. --- ERCS/erc-7527.md | 82 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/ERCS/erc-7527.md b/ERCS/erc-7527.md index 6fa3d8ffda..042dcbce31 100644 --- a/ERCS/erc-7527.md +++ b/ERCS/erc-7527.md @@ -46,6 +46,8 @@ Three interfaces are included here: `Agency`, `App`, and `Factory`. `Agency` and `App` MAY be implemented by the same contract or MAY be separately implemented. If separately implemented, they SHALL be mutually bounded and not upgradable after initialization. +`Agency` and `App` should implement `iconstructor` interface to initialize the parameters within the contract and validate the configuration parameters. If factory is used to deploy `Agency` and `App`, factory will automatically call the two functions when deploying. + `App` SHALL implement `onlyAgency()` modifier and `mint` and `burn` SHALL apply `onlyAgency()` as a modifier, which restricts calls to `Mint` and `Burn` only have effect if they are called through the corresponding `Agency`. `Agency` is OPTIONAL to implement `onlyApp()`. @@ -66,14 +68,15 @@ pragma solidity ^0.8.20; /** * @dev The settings of the agency. * @param currency The address of the currency. If `currency` is 0, the currency is Ether. - * @param premium The base premium of the currency. + * @param basePremium The base premium of the currency. * @param feeRecipient The address of the fee recipient. * @param mintFeePercent The fee of minting. * @param burnFeePercent The fee of burning. */ + struct Asset { address currency; - uint256 premium; + uint256 basePremium; address feeRecipient; uint16 mintFeePercent; uint16 burnFeePercent; @@ -94,22 +97,27 @@ interface IERC7527Agency { * @dev Emitted when `tokenId` token is wrapped. * @param to The address of the recipient of the newly created non-fungible token. * @param tokenId The identifier of the newly created non-fungible token. - * @param price The price of wrapping. + * @param premium The premium of wrapping. * @param fee The fee of wrapping. */ - event Wrap(address indexed to, uint256 indexed tokenId, uint256 price, uint256 fee); + event Wrap(address indexed to, uint256 indexed tokenId, uint256 premium, uint256 fee); /** * @dev Emitted when `tokenId` token is unwrapped. * @param to The address of the recipient of the currency. * @param tokenId The identifier of the non-fungible token to unwrap. - * @param price The price of unwrapping. + * @param premium The premium of unwrapping. * @param fee The fee of unwrapping. */ - event Unwrap(address indexed to, uint256 indexed tokenId, uint256 price, uint256 fee); + event Unwrap(address indexed to, uint256 indexed tokenId, uint256 premium, uint256 fee); + + /** + * @dev Constructor of the instance contract. + */ + function iconstructor() external; /** - * @dev Wrap currency premium into a non-fungible token. + * @dev Wrap some amount of currency into a non-fungible token. * @param to The address of the recipient of the newly created non-fungible token. * @param data The data to encode into ifself and the newly created non-fungible token. * @return The identifier of the newly created non-fungible token. @@ -117,7 +125,10 @@ interface IERC7527Agency { function wrap(address to, bytes calldata data) external payable returns (uint256); /** - * @dev Unwrap a non-fungible token into currency premium. + * @dev Unwrap a non-fungible token into some amount of currency. + * + * Todo: event + * * @param to The address of the recipient of the currency. * @param tokenId The identifier of the non-fungible token to unwrap. * @param data The data to encode into ifself and the non-fungible token with identifier `tokenId`. @@ -133,20 +144,20 @@ interface IERC7527Agency { function getStrategy() external view returns (address app, Asset memory asset, bytes memory attributeData); /** - * @dev Returns the price and fee of unwrapping. - * @param data The data to encode to calculate the price and fee of unwrapping. - * @return price The price of wrapping. + * @dev Returns the premium and fee of unwrapping. + * @param data The data to encode to calculate the premium and fee of unwrapping. + * @return premium The premium of wrapping. * @return fee The fee of wrapping. */ - function getUnwrapOracle(bytes memory data) external view returns (uint256 price, uint256 fee); + function getUnwrapOracle(bytes memory data) external view returns (uint256 premium, uint256 fee); /** - * @dev Returns the price and fee of wrapping. - * @param data The data to encode to calculate the price and fee of wrapping. - * @return price The price of wrapping. + * @dev Returns the premium and fee of wrapping. + * @param data The data to encode to calculate the premium and fee of wrapping. + * @return premium The premium of wrapping. * @return fee The fee of wrapping. */ - function getWrapOracle(bytes memory data) external view returns (uint256 price, uint256 fee); + function getWrapOracle(bytes memory data) external view returns (uint256 premium, uint256 fee); } ``` @@ -154,7 +165,7 @@ interface IERC7527Agency { `ERC7527App` SHALL inherit `name` from interface `ERC721Metadata`. -``` +``` pragma solidity ^0.8.20; interface IERC7527App { @@ -174,6 +185,11 @@ interface IERC7527App { */ function getAgency() external view returns (address payable); + /** + * @dev Constructor of the instance contract. + */ + function iconstructor() external; + /** * @dev Sets the agency of the non-fungible token. * @param agency The agency of the non-fungible token. @@ -299,6 +315,11 @@ When executing the `unwrap` function, predetermined strategy parameters are pass They can support function oracle based on on-chain and off-chain parameters, but on-chain parameters are suggested only for consensus of on-chain reality. +### `initData` and `iconstructor` + +During the deployment of `App` and `Agency` by the Factory, the Factory uses `initData` as Calldata to call the `Agency` and `App` contracts and also invokes the `iconstructor` functions within `App` and `Agency`. + +The initData is mainly used to call the parameterized initialization functions, while `iconstructor` is often used to validate configuration parameters and non-parameterized initialization functions. ## Backwards Compatibility @@ -327,6 +348,11 @@ contract ERC7527Agency is IERC7527Agency { receive() external payable {} + function iconstructor() external override pure { + (, Asset memory _asset,) = getStrategy(); + require(_asset.basePremium != 0, "LnModule: zero basePremium"); + } + function unwrap(address to, uint256 tokenId, bytes calldata data) external payable override { (address _app, Asset memory _asset,) = getStrategy(); require(_isApprovedOrOwner(_app, msg.sender, tokenId), "LnModule: not owner"); @@ -356,33 +382,33 @@ contract ERC7527Agency is IERC7527Agency { function getStrategy() public pure override returns (address app, Asset memory asset, bytes memory attributeData) { uint256 offset = _getImmutableArgsOffset(); address currency; - uint256 premium; - address payable awardFeeRecipient; + uint256 basePremium; + address payable feeRecipient; uint16 mintFeePercent; uint16 burnFeePercent; assembly { app := shr(0x60, calldataload(add(offset, 0))) currency := shr(0x60, calldataload(add(offset, 20))) - premium := calldataload(add(offset, 40)) - awardFeeRecipient := shr(0x60, calldataload(add(offset, 72))) + basePremium := calldataload(add(offset, 40)) + feeRecipient := shr(0x60, calldataload(add(offset, 72))) mintFeePercent := shr(0xf0, calldataload(add(offset, 92))) burnFeePercent := shr(0xf0, calldataload(add(offset, 94))) } - asset = Asset(currency, premium, awardFeeRecipient, mintFeePercent, burnFeePercent); + asset = Asset(currency, basePremium, feeRecipient, mintFeePercent, burnFeePercent); attributeData = ""; } function getUnwrapOracle(bytes memory data) public pure override returns (uint256 premium, uint256 fee) { uint256 input = abi.decode(data, (uint256)); (, Asset memory _asset,) = getStrategy(); - premium = _asset.premium + input * _asset.premium / 100; + premium = _asset.basePremium + input * _asset.basePremium / 100; fee = premium * _asset.burnFeePercent / 10000; } function getWrapOracle(bytes memory data) public pure override returns (uint256 premium, uint256 fee) { uint256 input = abi.decode(data, (uint256)); (, Asset memory _asset,) = getStrategy(); - premium = _asset.premium + input * _asset.premium / 100; + premium = _asset.basePremium + input * _asset.basePremium / 100; fee = premium * _asset.mintFeePercent / 10000; } @@ -419,6 +445,8 @@ contract ERC7527App is ERC721Enumerable, IERC7527App { _; } + function iconstructor() external {} + function getName(uint256) external pure returns (string memory) { return "App"; } @@ -465,7 +493,7 @@ contract ERC7527Factory is IERC7527Factory { abi.encodePacked( appInstance, agencySettings.asset.currency, - agencySettings.asset.premium, + agencySettings.asset.basePremium, agencySettings.asset.feeRecipient, agencySettings.asset.mintFeePercent, agencySettings.asset.burnFeePercent, @@ -475,6 +503,10 @@ contract ERC7527Factory is IERC7527Factory { } IERC7527App(appInstance).setAgency(payable(agencyInstance)); + + IERC7527Agency(payable(agencyInstance)).iconstructor(); + IERC7527App(appInstance).iconstructor(); + if (agencySettings.initData.length != 0) { (bool success, bytes memory result) = agencyInstance.call(agencySettings.initData); From 26c7dff775c3e3135fe89031aab7e2f3debf9fcd Mon Sep 17 00:00:00 2001 From: Lanyin Z <106770708+lanyinzly@users.noreply.github.com> Date: Thu, 27 Jun 2024 04:54:14 -0400 Subject: [PATCH 054/126] Update ERC-7615: Update erc-7615.md Merged by EIP-Bot. --- ERCS/erc-7615.md | 69 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/ERCS/erc-7615.md b/ERCS/erc-7615.md index 86f5317fde..71057699f0 100644 --- a/ERCS/erc-7615.md +++ b/ERCS/erc-7615.md @@ -68,21 +68,39 @@ In the fourth step, the subscriber contract SHOULD handle all possible `selector As mentioned above, there are Unconditional Push and Conditional Push two types of implementation. To implement Unconditional Push, the publisher contract SHOULD implement the following interface: -```solidity +``` interface IPushForce { + event ForceApprove(bytes4 indexed selector, address indexed target); + event ForceCancel(bytes4 indexed selector, address indexed target); + event RenounceForceApprove(); + event RenounceForceCancel(); + + error MustRenounce(); + error ForceApproveRenounced(); + error ForceCancelRenounced(); + function isForceApproved(bytes4 selector, address target) external returns (bool); function forceApprove(bytes4 selector, address target) external; function forceCancel(bytes4 selector, address target) external; + function isRenounceForceApprove() external returns (bool); + function isRenounceForceCancel() external returns (bool); + function renounceForceApprove(bytes memory) external; + function renounceForceCancel(bytes memory) external; } ``` `isForceApproved` is to query whether `selector` has already unconditionally bound to the subscriber contract with the address `target`. `forceApprove` is to bind `selector` to the subscriber contract `target`. `forceCancel` is to cancel the binding relationship between `selector` and `target`, where `isLocked` function of `target` returns `true` is REQUIRED. +`renounceForceApprove` is used to relinquish the `forceApprove` permission. After calling the `renounceForceApprove` function, `forceApprove` can no longer be called. Similarly, `renounceForceCancel` is used to relinquish the `forceCancel` permission. After calling the `renounceForceCancel` function, `forceCancel` can no longer be called. + To implement Conditional Push, the publisher contract SHOULD implement the following interface: -```solidity +``` interface IPushFree { + event Approve(bytes4 indexed selector, address indexed target, bytes data); + event Cancel(bytes4 indexed selector, address indexed target, bytes data); + function inbox(bytes4 selector) external returns (bytes memory); function isApproved(bytes4 selector, address target, bytes calldata data) external returns (bool); function approve(bytes4 selector, address target, bytes calldata data) external; @@ -129,23 +147,31 @@ Using function selectors to retrieve the addresses of subscriber contracts allow more detailed configuration. For the subscriber contract, having the specific function of the request source based on the push information enables more accurate handling of the push information. +### Renounce Safety Enhancement + +Both `forceApprove` and `forceCancel` permissions can be relinquished using their respective renounce functions. When both `renounceForceApprove` and `renounceForceCancel` are called, the registered push targets can longer be changed, greatly enhancing security. + ## Reference Implementation -```solidity +``` pragma solidity ^0.8.24; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {IPushFree, IPushForce} from "./interfaces/IPush.sol"; -import {IPull} from "./interfaces/IPull.sol"; +import {IExec} from "./interfaces/IExec.sol"; contract Foo is IPushFree, IPushForce { using EnumerableSet for EnumerableSet.AddressSet; + bool public override isRenounceForceApprove; + bool public override isRenounceForceCancel; + mapping(bytes4 selector => mapping(uint256 tokenId => EnumerableSet.AddressSet targets)) private _registry; mapping(bytes4 selector => EnumerableSet.AddressSet targets) private _registryOfAll; + // mapping(bytes4 => bytes) public inbox; modifier notLock(bytes4 selector, address target, bytes memory data) { - require(!IPull(target).isLocked(selector, data), "Foo: lock"); + require(!IExec(target).isLocked(selector, data), "Foo: lock"); _; } @@ -157,7 +183,7 @@ contract Foo is IPushFree, IPushForce { data = abi.encode(loadData); } - + function isApproved(bytes4 selector, address target, bytes calldata data) external view override returns (bool) { uint256 tokenId = abi.decode(data, (uint256)); return _registry[selector][tokenId].contains(target); @@ -182,13 +208,35 @@ contract Foo is IPushFree, IPushForce { } function forceApprove(bytes4 selector, address target) external override { + if (isRenounceForceApprove) revert ForceApproveRenounced(); _registryOfAll[selector].add(target); } function forceCancel(bytes4 selector, address target) external override notLock(selector, target, "") { + if (isRenounceForceCancel) revert ForceCancelRenounced(); _registryOfAll[selector].remove(target); } + function renounceForceApprove(bytes memory data) external override { + (bool burn) = abi.decode(data, (bool)); + if (burn != true) { + revert MustRenounce(); + } + + isRenounceForceApprove = true; + emit RenounceForceApprove(); + } + + function renounceForceCancel(bytes memory data) external override { + (bool burn) = abi.decode(data, (bool)); + if (burn != true) { + revert MustRenounce(); + } + + isRenounceForceCancel = true; + emit RenounceForceCancel(); + } + function send(uint256 message) external { _push(this.send.selector, message); } @@ -200,25 +248,26 @@ contract Foo is IPushFree, IPushForce { address[] memory targets = _registry[selector][message].values(); for (uint256 i = 0; i < targets.length; i++) { - IPull(targets[i]).pull(selector, abi.encode(message)); + IExec(targets[i]).exec(selector, abi.encode(message)); } targets = _registryOfAll[selector].values(); for (uint256 i = 0; i < targets.length; i++) { - IPull(targets[i]).pull(selector, abi.encode(message)); + IExec(targets[i]).exec(selector, abi.encode(message)); } } } -contract Bar is IPull { +contract Bar is IExec { event Log(bytes4 indexed selector, bytes data, bytes inboxData); function isLocked(bytes4, bytes calldata) external pure override returns (bool) { return true; } - function pull(bytes4 selector, bytes calldata data) external { + function exec(bytes4 selector, bytes calldata data) external { bytes memory inboxData = IPushFree(msg.sender).inbox(selector); + emit Log(selector, data, inboxData); } } From d6b9a08b221c59946d9496499232f7d18cd71b5f Mon Sep 17 00:00:00 2001 From: Gregory Markou <16929357+GregTheGreek@users.noreply.github.com> Date: Fri, 28 Jun 2024 19:27:31 +0300 Subject: [PATCH 055/126] Add ERC: Single Sign-On for Account Discovery Merged by EIP-Bot. --- ERCS/erc-7555.md | 211 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 ERCS/erc-7555.md diff --git a/ERCS/erc-7555.md b/ERCS/erc-7555.md new file mode 100644 index 0000000000..88d666f1fa --- /dev/null +++ b/ERCS/erc-7555.md @@ -0,0 +1,211 @@ +--- +eip: 7555 +title: Single Sign-On for Account Discovery +description: Discover accounts using a signing key that do not use the secp256k1 curve. +author: Alexander Mรผller (@alexmmueller), Gregory Markou (@GregTheGreek), Willem Olding (@Wollum), Belma Gutlic (@morrigan), Marin Petruniฤ‡ (@mpetrunic), Pedro Gomes (@pedrouid) +discussions-to: https://ethereum-magicians.org/t/erc-7555-single-sign-on-for-account-discovery/16536 +status: Draft +type: Standards Track +category: ERC +created: 2023-11-10 +requires: 4337 +--- + +## Abstract +This proposal establishes a standardized interface and functionality for applications to discover user accounts besides the readily available EOA. Specifically discovering normal accounts and smart accounts that may have been deployed or configured using a signing key that is not the standard Ethereum secp256k1 curve. The objective is to ensure uniformity of address retrieval across applications, and domains. + +## Motivation +The recent progress in account abstraction has led to significantly increased flexibility enabling use cases such as multi-signature transactions, social recovery, contract/account whitelisting, session keys and much more. However, with increased flexibility there comes an increased complexity. One area of increased complexity is account fragmentation -both at the EOA and smart account level - following from the inability to correctly identify all existing addresses by a user. In this EIP we present a potential solution that aims to unify the discovery and handling of such accounts. + +Prior to [ERC-4337](./eip-4337.md), the standard approach to interacting with a smart contract account required a valid signature from a keypair using secp256k1. Since ERC-4337, alternative signing options have become popular, such as passkey, yubikey or ios/android secure enclaves, which do not conform to the secp256k1 curve, and require a paymaster to submit the transaction on the users behalf. Since providers implement additional logic into the key generation process (shamir, mpc, secure enclave, etc) alternative signers have no uniform way for a user to produce the same externally-owned account adresses, or smart account addresses across different applications. + +Secure hardware devices such as native passkeys, or yubikeys generate a unique keypair per domain. The implication is for application developers that natively integrate authentication methods such as those, will never be able to recover a uniform keypair. Practically, if we have the following scenario where there are two applications: a mobile app (App A), and a web based application (App B). If both implement a solution such as passkey, App A and App B would recover two different keys. This poses a hurdle to the user who would expect to have the same address across services (much like they would using a hardware wallet, or other wallets). + +With the introduction of 4337, this problem is amplified. An application that wants its users to leverage 4337 (to abstract keys away, and generally improve the onboarding experience) will not be able to detect if a user has an existing smart account deployed. This will lead to the developer (or third party service providing the onboarding experience) to deploy a smart account on behalf of the user at the given address scoped to the apps domain. + +Not being able to correctly identify existing accounts owned by a user will lead to account fragmentation. The fragmentation, as described early, exists because applications will identify them as a new user, and not one whom may already have an account. Leading to a single user having many unassociated accounts, with assets scattered amongst them, and no way to unify them. + +This standard aims to achieve: +1. Standard way for applications to request a users signing address. +2. Standard way for applications to provide single sign-on (SSO) functionality for alternative signing methods. +3. Standard way for applications to disclose smart accounts that have been created through their own service. + +This standard **does not** aim to achieve: +1. How a user can sign messages across domains. +2. How a provider generates a keypair for a user. +3. How an application handles the user interface logic. + + +## Specification +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Definitions +- **Smart account** - An ERC-4337 compliant smart contract account that has a modular architecture. +- **Domain** - A string of text acting as an identification to a server or wesbite (eg: `ethereum.org` or `ABCDE12345.com.example.app`). +- **EOA** - Accounts that are controlled by a single private key. +- **Provider** - A third party service provider that is able to authenticate a user and produce a keypair for the user. + +### Redirects +An application looking to authenticate a user must navigate the user to a given provider's URI based on the `URI Request Syntax`. The application must implement a valid redirect URI for the callback in order to receive a valid response. + +#### Available Routes +- `/auth/`: The route used to authenticate a user, and request credentials. +- `/sendTransaction/`: The route used to send a transaction payload for a user to sign. This is more of a convenient method to allow applications to do both authentication, and plugin registration within a single redirect, instead of requiring the user to perform two redirects. + +### Schema +The `smart_account_address` should be returned in the CAIP-10 format. + +#### Auth Route +##### Request Schema +```= swagger + parameters: + - in: query + name: redirect_uri + schema: + type: string + description: The uri that the provider should redirect back to. + - in: query + name: chain_id + schema: + type: string + description: The chain_id of a given network. +``` +##### Response Schema +```= swagger + parameters: + - in: query + name: smart_account_address + schema: + type: string + description: The on-chain address for a given smart account, formatted using CAIP-10 +``` + +##### Request Syntax +```= swagger +https:///auth/? + redirect_uri= + &chain_id= +``` +##### Response Syntax +```= swagger +https:///auth/? + smart_account_address= +``` + +#### sendTransaction Route +##### Request Schema +```= swagger + parameters: + - in: query + name: redirect_uri + schema: + type: string + description: The uri that the provider should redirect back to. + - in: query + name: chain_id + schema: + type: string + description: The chain_id of a given network. + - in: query + name: transaction + schema: + type: string + description: The RLP encoded transaction that needs to be signed +``` +##### Response Schema +```= swagger + parameters: + - in: query + name: smart_account_address + schema: + type: string + description: The on-chain address for a given smart account, formatted using CAIP-10 + - in: query + name: tx_hash + schema: + type: string + description: The hash of the transaction +``` + +##### Request Syntax +```= swagger +https:///sendTransaction/? + redirect_uri= + &chain_id= + &transaction= +``` +##### Response Syntax +```= swagger +https:///sendTransaction/? + smart_account_address= + &tx_hash= +``` + +## Rationale +### Redirects +Taking inspiration from how SSO functions in the web today. We implement a similar redirect pattern, consisting of a simple request/response. + +#### Application +##### Initial Request +An application would redirect a user to a specified provider, only passing along the callback url information. This is to ensure the providers website can remain stateless, and not rely on web requests. +##### Response from provider +When a user is redirected to the application, it can parse the response for a signer address, and associated smart account address. + +#### Provider +Upon a user navigating to the provider website, the provider would parse the redirect url and authenticate the user. The authentication method does not matter, such that it can produce a valid public address, and recover any smart accounts that may have been deployed through the provider. + +## Backwards Compatibility + +No backward compatibility issues found. + +## Reference Implementation +Using `location.replace()` vs `location.href` is up to the application to decide how they wish the experience to be handled. + +Sample URI Request +```= +https://eth-sso.ethereum.org/auth?redirect_uri=http://myapp.com/eth-sso/callback/&chain_id=1 +``` +Sample Response +```= +http://myapp.com/callback/?smart_account_address=0xb...c +``` + +Application logic +```javascript= +// https://myapp.com +// User triggered authentication function +function auth() { + window.location.replace("https://eth-sso.ethereum.org/auth?redirect_uri=myapp.com&chain_id=1/eth-sso/callback/"); +}; + +// App level routing logic (generic router) +route("/eth-sso/callback/", function() { + let params = (new URL(document.location)).searchParams; + let smartAccountAddress = params.get("smart_account_address"); +}); +``` + +Provider Logic +```javascript= +// eg: https://eth-sso.ethereum.org/auth +route("/eth-sso/callback/", function("/auth") { + let params = (new URL(document.location)).searchParams; + let redirectUrl = params.get("redirect_uri"); + // Authenticate the user (eg: with passkeys) + let address = "..."; + // Get smart account if available + let smartAccountAddress = getSmartAccount(address); + window.location.replace(`http://${redirectUrl}/?smart_account_address=${smartAccountAddress}`); +}); +``` + +## Security Considerations + + +- Is there a concern that a user can spoof another persons address, and that could be malicious? For example, circumventing the provider, and manually calling the redirect_url with a chosen address. A way around this would be having the user actually sign a challenge message, perhaps leveraging SIWE. + +The absence of wildcard support in the redirect URI is intended to protect users from nested open redirect vulnerabilities. Allowing wildcards could enable attackers to redirect users to different pages under the supported wildcard, creating a vulnerability to open redirects. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file From a066efe10b222106d51eb4b26674a211d96ff8bc Mon Sep 17 00:00:00 2001 From: Oren Date: Tue, 2 Jul 2024 21:03:55 +0300 Subject: [PATCH 056/126] Fix typo (#290) --- ERCS/erc-4337.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-4337.md b/ERCS/erc-4337.md index 53db9e0376..e4a26772cb 100644 --- a/ERCS/erc-4337.md +++ b/ERCS/erc-4337.md @@ -990,7 +990,7 @@ An array of reputation entries with the fields: #### * debug_bundler_addUserOps Accept UserOperations into the mempool. -Assume the given UserOperations all pass validation (without actually validating them), and accept them directly into th mempool +Assume the given UserOperations all pass validation (without actually validating them), and accept them directly into the mempool **Parameters:** From cfcf8e0caf9b507ee56657083ad11b7adac39b37 Mon Sep 17 00:00:00 2001 From: Yvonne Zhang Date: Wed, 3 Jul 2024 11:32:44 -0700 Subject: [PATCH 057/126] Add ERC: Signature Validation Extension for Permit Merged by EIP-Bot. --- ERCS/erc-7597.md | 134 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 ERCS/erc-7597.md diff --git a/ERCS/erc-7597.md b/ERCS/erc-7597.md new file mode 100644 index 0000000000..bbb29dab88 --- /dev/null +++ b/ERCS/erc-7597.md @@ -0,0 +1,134 @@ +--- +eip: 7597 +title: Signature Validation Extension for Permit +description: An ERC to extend ERC-2612 Permit to support ERC-1271-based signature validation. +author: Yvonne Zhang (@yvonnezhangc), Aloysius Chan (@circle-aloychan) +discussions-to: https://ethereum-magicians.org/t/add-erc-contract-signature-validation-extension-for-eip-2612-permit/18157 +status: Draft +type: Standards Track +category: ERC +created: 2024-01-15 +requires: 1271, 2612 +--- + +# EIP: Contract signature validation extension for [ERC-2612](./eip-2612.md) Permit + +## Abstract + +This proposal aims to extend the functionality of the existing [ERC-2612](./eip-2612.md) Permit to support gasless [ERC-20](./eip-20.md) approval operations initiated by smart contract wallets. + +## Motivation + +The current signature validation scheme in [ERC-2612](./eip-2612.md), based on V, R, S parameters, restricts signature validation to EOA wallets. + +With the growing popularity of smart contract wallets and increased adoption of [ERC-1271](./eip-1271.md), it is necessary to allow for flexible signature validation methods and the use of custom logic in each contract's signature verification. By accepting unstructured signature bytes as input, custom algorithms and signature schemes can be utilized, enabling a wider range of wallet types. + +## Specification + +Compliant contracts must implement the `permit` using the following spec + +``` +function permit(address owner, address spender, uint value, uint deadline, bytes memory signature) external +``` +as well as two other interfaces previously mandated by [ERC-2612](./eip-2612.md): +``` +function nonces(address owner) external view returns (uint) +function DOMAIN_SEPARATOR() external view returns (bytes32) +``` + +A call to `permit(owner, spender, value, deadline, signature)` will set `allowance[owner][spender]` to value, increment `nonces[owner]` by 1, and emit a corresponding `Approval` event, if and only if the following conditions are met: + +- The current blocktime is less than or equal to `deadline`. +- `owner` is not the zero address. +- `nonces[owner]` (before the state update) is equal to nonce. +- `signature` validation: + - If `owner` is an EOA, `signature` is a valid secp256k1 signature in the form of `abi.encodePacked(r, s, v)`. + - If `owner` is a contract, `signature` is validated by calling `isValidSignature()` on the `owner` contract. + +If any of these conditions are not met, the permit call must revert. + +## Rationale + +By replacing the existing V, R, S signature validation scheme and introducing support for unstructured bytes input, contract developers can use a unified interface to validate signature from both EOAs and SC wallets. This allows for the utilization of different signature schemes and algorithms fitting the wallet type, paving the way for smart contract wallets and advanced wallet types to enhance their signature validation processes, promoting flexibility and innovation. + +## Backwards Compatibility + +This proposal is fully backward-compatible with the existing ERC-2612 standard. Contracts that currently rely on the V, R, S signature validation scheme will continue to function without any issues. + +If both V, R, S signature validation and the new unstructured bytes signature validation need to be supported for backward compatibility reasons, developers can reduce duplicates by adapting the following code block as an example: + +``` +function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s +) external { + _permit(owner, spender, value, deadline, abi.encodePacked(r, s, v)); +} +``` + +## Reference Implementation + +Sample `permit` implemented with OZ's SignatureChecker + +```solidity +/** + * @notice Update allowance with a signed permit + * @dev Signature bytes can be used for both EOA wallets and contract wallets. + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time) + * @param signature Unstructured bytes signature signed by an EOA wallet or a contract wallet + */ +function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + bytes memory signature +) external { + require(deadline >= now, "Permit is expired"); + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + bytes32 digest = keccak256(abi.encodePacked( + hex"1901", + DOMAIN_SEPARATOR, + keccak256(abi.encode( + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"), + owner, + spender, + value, + nonce, + deadline + )) + )); + + require( + // Check for both ECDSA signature and and ERC-1271 signature. A sample SignatureChecker is available at + // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7bd2b2a/contracts/utils/cryptography/SignatureChecker.sol + SignatureChecker.isValidSignatureNow( + owner, + typedDataHash, + signature + ), + "Invalid signature" + ); + + allowed[owner][spender] = value; + emit Approval(owner, spender, value); +} +``` + +## Security Considerations + +- For contract wallets, the security of `permit` relies on `isValidSignature()` to ensure the signature bytes represent the desired execution from contract wallet owner(s). Contract wallet developers must exercise caution when implementing custom signature validation logic to ensure the security of their contracts. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From b8f5ea75782f5d1d28174e338542957905e7cf8c Mon Sep 17 00:00:00 2001 From: galimba Date: Sun, 7 Jul 2024 22:09:56 +0200 Subject: [PATCH 058/126] Update ERC-7208: applies fixes as per community feedback Merged by EIP-Bot. --- ERCS/erc-7208.md | 953 ++++++------------ assets/erc-7208/erc-7208-compat.md | 36 +- assets/erc-7208/erc-7208-overview.svg | 2 +- .../erc-7208/erc-7208-technical-overview.svg | 4 + 4 files changed, 326 insertions(+), 669 deletions(-) create mode 100644 assets/erc-7208/erc-7208-technical-overview.svg diff --git a/ERCS/erc-7208.md b/ERCS/erc-7208.md index 738348c309..121183398a 100644 --- a/ERCS/erc-7208.md +++ b/ERCS/erc-7208.md @@ -8,782 +8,455 @@ status: Draft type: Standards Track category: ERC created: 2023-06-09 -requires: 165, 721 +requires: 165 --- ## Abstract -On-chain Data Containers are smart contracts that inherit from [ERC-721](./eip-721.md) to store on-chain data in structures called "Properties". Information stored in Properties can be accessed and modified by the implementation of smart contracts called "Property Managers". This ERC defines a series of interfaces for the separation of the storage from the interface implementing the functions that govern the data. We introduce the interface for "Restrictions", structures associated with Properties that apply limitations in the capabilities of Property Managers to access or modify the data stored within Properties. +"On-chain Data Containers" (ODCs) are used for indexing and storing data in Smart Contracts called "Data Objects" (DOs). Information stored in Data Objects can be accessed and modified by the implementation of smart contracts called "Data Managers" (DMs). This ERC defines a series of interfaces for the separation of the storage of data from the implementation of the logic functions that govern such data. We introduce the interfaces for ODCs, the structures associated with DOs for abstracting storage, the DMs to access or modify the data, and finally the interfaces for compatibility Registries that enable Data Portability (horizontal mobility) between different implementations of this ERC. ## Motivation -As the Ethereum ecosystem continues to grow, it is becoming increasingly important to enable more flexible and sophisticated on-chain data management solutions, as well as off-chain assets and their on-chain representation. We have seen too many times where the market hype has driven an explosion of new standard token proposals, most of them targeting a specific business use-case rather than increasing interoperability. +As the Ethereum ecosystem grows, so does the demand for on-chain functionalities. The market encourages a desire for broader adoption and more complex systems, so there is a constant need for improved efficiency. We have seen times where the market hype has driven an explosion of new standard token proposals. While each standard serves its purpose, most often requires more flexibility to manage interoperability with other standards. -This ERC defines several interface for ODCs (On-Chain Data Containers) that together allow for the storage and retrieval of additional on-chain data, referred to as "Properties". This provides an interoperable solution for dynamic data management across various token standards, enabling them to hold mutable states and reflect changes over time. +While such diversity spurs innovation, different projects will implement their bespoke solutions for interoperability, resulting in a highly fragmented landscape. The lack of a standard mechanism for adapting tokenized across ERC standards is accentuating the interoperability issues. -ODCs aim to address the limitations of both previous and future ERCs by enabling on-chain data aggregation, providing an interface for standardized, interoperable, and composable data management solutions. +We recognize there is no โ€œone size fits allโ€ solution to solve the standardization and interoperability challenges. Most assets - either Fungible, Non-Fungible, Digital Twins, Real-world Assets, DePin, etc - have multiple mechanisms for representing them as on-chain tokens by utilizing different standard interfaces. However, for those assets to be exchanged, traded, or interacted with, protocols are required to implement each of those standards to be able to access and modify the on-chain data. Moreover, the immutability of smart contracts plays a role against future-proofing their implementations by supporting new tokenization standards. A collaborative effort must be made to enable On-chain Adapters to enable the interaction between assets tokenized under different standards. -ODCs address some of the existing limitations with previous ERC token contracts by separating the logic from the storage. +We aim to abstract the on-chain data handling from both the logical implementation and the ERC interfaces exposing the underlying data. This EIP proposes a series of interfaces for storing and accessing data on-chain, codifying the underlying assets as generic "Data Points" that may be associated with multiple interoperable and even concurrent ERC interfaces. This proposal is designed to work by coexisting with previous and future token standards, providing a flexible, efficient, and coherent mechanism to manage asset interoperability. -This ERC defines a standard interface for interacting with the concept of "Restrictions" for data stored within an ODC ("Properties"). This enables greater flexibility, interoperability, and utility for multiple ERCs to work together. +- **Data Abstraction**: We propose a standardized interface for enabling developers to separate the data storage code from the underlying token utility logic, reducing the need for supporting and implementing multiple inherited -and often clashing- interfaces to achieve asset compatibility. The data (and therefore the assets) can be stored independently of the logic that governs such data. -This proposal is motivated by the need to extend the capabilities of on-chain stored data beyond the static nature of each ERC, enabling complex logic to be abstracted away from the stored variables. This is particularly relevant for use cases where the state of the NFT needs to change in response to certain events or conditions, as well as when the storage and the logic must be separated. For instance, NFTs representing Account Abstraction contracts, Smart Wallets, or the digital representation of Real World Assets, all of which require dynamic and secure storage. +- **Standard Neutrality**: A neutral approach must enable the underlying data of any tokenized asset to transition seamlessly between different token standards. This will significantly improve interoperability among different standards, reducing fragmentation in the landscape. Our proposal aims to separate the storage of data representing an underlying asset from the standard interface used for representing the token. -NFTs conforming to standards such as [ERC-721](./eip-721.md) have often faced limitations when representing complex digital assets. The Ethereum ecosystem hosts a rich diversity of token standards, each designed to cater to specific use cases. While such diversity spurs innovation, it also results in a highly fragmented landscape, especially for Non-Fungible Tokens (NFTs). Different projects might implement their own ways of managing mutable states, incurring further fragmentation and interoperability issues. While each standard serves its purpose, they often lack the flexibility needed to manage additional on-chain data associated with the utility of these tokens. +- **Consistent Interface**: A uniform interface of primitive functions abstracts the data storage from the use case, irrespective of the underlying token's standard or the interface used for exposing such data. Both data as well as metadata can be stored on-chain, and exposed through the same functions. -Real-world assets have multiple ways in which they can be represented as on-chain tokens by utilizing different standard interfaces. However, for those assets to be exchanged, traded or interacted with, the marketplace is required to implement each of those standards in order to be able to access and modify the on-chain data. +- **Data Portability**: We provide a mechanism for the Horizontal Mobility of data between implementations of this standard, incentivizing the implementation of interoperable solutions and standard adapters. -Therefore, there is a need for standardization to manage these mutable states for tokenization in a manner that abstracts the on-chain data handling from the logical accounting. Such standard would provide all ERCs, regardless of their specific use case, with the mechanisms for interacting with each other in a consistent and predictable way. - -This EIP proposes a series of interfaces for storing and accessing data on-chain, codifying information as generic Properties associated with Restrictions specific to use cases. This enhancement is designed to work by extending existing token standards, providing a flexible, efficient, and coherent way to manage the data associated with: - -- **Standard Neutrality**: The standard aims to separate the data logic from the token standard. This neutral approach would allow ERCs to transition seamlessly between different token standards, promoting interactions with platforms or marketplaces designed for those standards. This could significantly improve interoperability among different standards, reducing fragmentation in the landscape. - -- **Consistent Interface**: A uniform interface abstracts the data storage from the use case, irrespective of the underlying token standard. This consistent interface simplifies interoperability, enabling platforms and marketplaces to interact with a uniform data interface, regardless of individual token standards. This common ground for all tokenization could reduce fragmentation in the ecosystem. - -- **Simplified Upgrades**: A standard interface for representing the utility of the on-chain data would simplify the process of integrating new token standards. This could help to reduce fragmentation caused by outdated standards, facilitating easier transition to new, more efficient, or feature-rich implementations. - -- **Data Abstraction**: A standardized interface would allow developers to separate the data storage code from the underlying token utility logic, reducing the need for off-chain services to implement multiple interfaces and promoting greater unity in the ecosystem. - -- **Actionable data**: Current practices often store token metadata off-chain, rendering it inaccessible for smart contracts without the use of oracles. Moreover, metadata is often used to store information that could otherwise be considered data relevant to the token's inherent identity. This ERC seeks to rectify this issue by introducing a standardized interface for reading and storing additional on-chain data related to ODC. - -A case-by-case limited analysis is provided in the compatibility appendix. ## Specification -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. ### Terms -**ODC**: A uniquely identifiable non-fungible token. An ODC MAY store information within Properties. - -**Property**: A modifiable information unit stored within an ODC. Properties SHOULD be capable of undergoing modifications and MUST be able to act as an indexed data container. - -**Restriction**: A configuration stored within the **ODC**, that SHOULD describe under which conditions a certain **Property Manager** is allowed to modify the information stored within a certain **Property**. - -**Property Manager**: A type of Smart Contract that MUST implement a **PM interface** in order to manage data stored within the **ODC**. - -**Category**: **Property Managers** MUST be grouped in Categories that SHOULD represent access to **Properties**. Each **Property Manager** MAY be part of one or more **Categories**. The assignment of categories SHOULD be managed by Governance. - -### ODC Interface +**ODC**: A uniquely identifiable data container that is used for indexing Data Objects or storing Data Points. -An ODC MUST extend the functionality of [ERC-721](./eip-721.md) through the incorporation of **Properties** in its internal storage. The **Properties** MAY have **Restrictions**. +**ODC Implementation**: One or many Smart Contracts implementing the ODC access-management logic. -### Properties +**Data Point**: A uniquely identifiable unit of information. -**Properties** are modifiable information units stored within an ODC. Properties SHOULD be capable of undergoing modifications and MUST be able to act as an indexed data container. +**Data Object**: One or many Smart Contracts implementing the low-level storage management of stored Data Points -```solidity -/** - * @notice Gets a Property Data point of the ODC. - * @dev This function allows anyone to get a property of the ODC. - * @param ouid The unique ID of the ODC. - * @param propertyKey The key of the property to be retrieved. - * @param dataKey The key of the data inside of Property. - * @return The value of the data point within the Property. - */ - function getPropertyData( - uint256 ouid, - bytes32 propertyKey, - bytes32 dataKey - ) external view returns (bytes32); -``` - -```solidity -/** - * @notice Sets a Property Data point within the ODC. - * @dev This function allows the owner or an authorized operator to set a property of the ODC. - * @param ouid The unique ID of the ODC. - * @param propertyKey The key of the property to be set. - * @param dataKey The Key of the property to be set. - * @param dataValue The value of the data point to be set within the Property. - */ - function setPropertyData( - uint256 ouid, - bytes32 propertyKey, - bytes32 dataKey, - bytes32 dataValue - ) external; -``` +**Data Manager**: One or many Smart Contracts implementing the high-level logic and end-user interface for managing the Data Points. -- **getProperty**: This function MUST retrieve a specific `dataValue` of an ODC, identifiable through the `tokenId`, `propertyKey`, and `dataKey` parameters. +**Data Point Registry**: One or many Smart Contracts that define a space of compatible or interoperable Data Points. +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. -- **setProperty**: This function MUST set or update a specific Property data point within an ODC. This operation is REQUIRED to be executed solely by the owner of the ODC or an approved Smart Contract. This function MUST set the `dataValue` within the `dataKey` of `propertyKey` for the `tokenId`. - -```solidity -/** - * @title Onchain Data Container (ODC) Interface for Properties Data - * Provides functions to manipulate data stored in ODC token - */ -interface IODCProperties { - /** - * @notice Returns data stored in a property - * @param odcid ODC token id - * @param prop Property key - * @param dataType Defines which data is returned (implementation-specific) - * @param hint Data-type dependent, can be zero-length, can be used to specify part of the data to get (for example, if `dataType` specifies a Map, `hint` can be a key in this Map) - * @return Data previously stored. SHOULD be empty array if nothing was stored. - */ - function propertyDataOf( - uint256 odcid, - bytes32 prop, - bytes32 dataType, - bytes calldata hint - ) external view returns (bytes memory); - - /** - * @notice Returns data stored in a property - * @dev Access to this function SHOULD be allowed only to PropertyManagers approved for this property, see IODCPermissions - * @param odcid ODC token id - * @param prop Property key - * @param dataType Defines which data is returned (implementation-specific) - * @param data Data to store (how the data is stored is implementation-specifiec and can be different for different dataType) - */ - function setPropertyData( - uint256 odcid, - bytes32 prop, - bytes32 dataType, - bytes calldata data - ) external; -} -``` - +### ODC + * ODC SHOULD manage internal IDs for each data container. + * ODC SHOULD manage the access of Data Managers to Data Objects. + * ODC SHOULD use the IODC interface: ```solidity -/** - * @title Onchain Data Container (ODC) Interface for Restrictions - * Provides functions to restrict certain actions with ODC token - */ -interface IODCRestrictions { - /** - * @notice Retrievs list of restrictions associated with specified ODC token - * @param odcid ODC token id - * @return rids List of restriction ids - */ - function restrictionsOf(uint256 odcid) external view returns(uint256[] memory rids); - - /** - * @notice Retrieves detalis of restriction - * @param rid Id of restriction to read - * @return odcid ODC token id - * @return prop property the restriction is associated with - * @return restrictionType Type of restriction - * @return data Data of restriction (may be different from the `initData` used to add restriction) - */ - function restriction(uint256 rid) external view returns(uint256 odcid, bytes32 prop, bytes32 restrictionType, bytes memory data); - - /** - * @notice Adds a restriction associated with `odcid` and a property - * @dev Access to this function SHOULD be allowed only to PropertyManagers approved for this property, see IODCPermissions - * @param odcid ODC token id - * @param prop Property key - * @param restrictionType Defines type of Restriction. The implementation should manage list of supported Restriction Types - * @param initData Data to initialize the Restriction - * @return rid ID of added restriction (unique for all `odcid` in this contract, will never change untill removed and can't be reused) - */ - function addRestriction( - uint256 odcid, - bytes32 prop, - bytes32 restrictionType, - bytes calldata initData - ) external returns(uint256 rid); - - /** - * @notice Removes restriction from the ODC - * @param odcid Id of Data Container - * @param prop Property key - * @param rid ID of restriction to remove - * @return If a restriction was deleted - returns true, if it does not exist - returns false, if it exists but can't be deleted - throws an exception - */ - function removeRestriction( - uint256 odcid, - bytes32 prop, - uint256 rid - ) external returns(bool); -} -``` - -```solidity -interface IODCMetadata { - function metadataOf(uint256 odcid) external view returns(string calldata); -} -interface IODCTypes { +interface IODC { /** - * @notice Enum of operations which PropertyManager can initiate on ODC + * @notice Verifies if DataManager is allowed to write specific DataPoint on specific DataObject + * @param dp Identifier of the DataPoint + * @param dm Address of DataManager + * @return if write access is allowed */ - enum PMOperation { - MODIFY_PROPERTY_DATA, - ADD_RESTRICTION, - REMOVE_RESTRICTION - } - /** - * - */ - enum Mutatation { - SPLIT, - MERGE, - ATTACH, - DETACH - } -} -``` + function isApprovedDataManager(DataPoint dp, address dm) external view returns(bool); -```solidity -/** - * @title Onchain Data Container (ODC) Interface for the registry of properties and categories - */ -interface IODCRegistry is IODCTypes { /** - * @notice Returns address responsible for category management: add/remove properties, approve PropertyManagers etc + * @notice Defines if DataManager is allowed to write specific DataPoint + * @param dp Identifier of the DataPoint + * @param dm Address of DataManager + * @param approved if DataManager should be approved for the DataPoint + * @dev Function should be restricted to DataPoint maintainer only */ - function maintenerOf(bytes32 category) external view returns(address); + function allowDataManager(DataPoint dp, address dm, bool approved) external; + /** - * @notice Returns category of specified property + * @notice Verifies if DataObject is allowed to add Hooks to the DataPoint + * @param dp Identifier of the DataPoint + * @param dobj Address of DataObject + * @return if write access is allowed */ - function categoryOf(bytes32 property) external view returns(bytes32 category); + function isApprovedDataObject(DataPoint dp, address dobj) external view returns(bool); + /** - * @notice Returns all properties of specified category + * @notice Defines if DataObject is allowed to add Hooks to the DataPoint + * @param dp Identifier of the DataPoint + * @param dobj Address of DataObject + * @param approved if DataManager should be approved for the DataPoint + * @dev Function should be restricted to datapoint maintainer only */ - function propertiesOf(bytes32 categoy) external view returns(bytes32[] memory properties); + function allowDataObject(DataPoint dp, address dobj, bool approved) external; + /** - * @notice Returns metadata provider for specific property + * @notice Reads stored data + * @param dobj Identifier of DataObject + * @param dp Identifier of the datapoint + * @param operation Read operation to execute on the data + * @param data Operation-specific data + * @return Operation-specific data */ - function metadataProviderOf(bytes32 property) external view returns(IMetadataProvider); + function read(address dobj, DataPoint dp, bytes4 operation, bytes calldata data) external view returns(bytes memory); + /** - * @notice Returns if mutation is allowed for the category + * @notice Store data + * @param dobj Identifier of DataObject + * @param dp Identifier of the datapoint + * @param operation Read operation to execute on the data + * @param data Operation-specific data + * @return Operation-specific data (can be empty) + * @dev Function should be restricted to allowed DMs only */ - function isAllowed(Mutatation mutation, bytes32 category) external view returns(bool); + function write(address dobj, DataPoint dp, bytes4 operation, bytes calldata data) external returns(bytes memory); } ``` +### Data Objects + + * Data Object SHOULD implement the logic directly related to handling the data stored on Data Points. + * Data Object SHOULD implement the logic for transfering management of its Data Points to a different ODC Implementation. + * Data Object SHOULD use the IDataObject interface: + ```solidity -/** - * @title Onchain Data Container (ODC) Interface for mutations between several ODCs - */ -interface IODCMutations is IODCTypes { - function merge( - uint256 from, - uint256 to, - bytes32[] calldata categories - ) external; - function split(uint256 from, bytes32[] calldata categories) external returns (uint256); -} -interface IODCPermissions is IODCTypes { +interface IDataObject { /** - * @notice Verifies if PropertyManager is allowed to execute an operation on the ODC - * @param pm Address of PropertyManager - * @param op Operation to execute - * @param odcid Id of Data Container - * @param prop Property key - * @return if PropertyManager is allowed to execute the operation + * @notice Reads stored data + * @param dp Identifier of the DataPoint + * @param operation Read operation to execute on the data + * @param data Operation-specific data + * @return Operation-specific data */ - function isPropertyManagerAllowed(address pm, PMOperation op, uint256 odcid, bytes32 prop) external returns(bool); -} -``` + function read(DataPoint dp, bytes4 operation, bytes calldata data) external view returns(bytes memory); -```solidity -interface IMetadataProvider { - function metadataOf() view; -} -``` + /** + * @notice Store data + * @param dp Identifier of the DataPoint + * @param operation Read operation to execute on the data + * @param data Operation-specific data + * @return Operation-specific data (can be empty) + */ + function write(DataPoint dp, bytes4 operation, bytes calldata data) external returns(bytes memory); -```solidity -interface IODCErrors is IODCTypes { /** - * @param odcid Id of Data Container - * @param rid Id of restriction wich triggered the error - * @param restrictionReason Restriction-specific explanaition of the reason to revert the call + * @notice Sets ODC Implementation + * @param dp Identifier of the DataPoint + * @param newImpl address of the new ODC implementation */ - error Restricted(uint256 odcid, uint rid, string restrictionReason); - error OperationNotAllowedForPropertyManager(address pm, PMOperation op, uint256 odcid, bytes32 prop); - error MutationNotAllowed(Mutatation mutation, bytes32 category); + function setOdcImplementation(DataPoint dp, address newImpl) external; } ``` +Data Objects can receive `read()` or `write()` requests when a Data Manager is requesting access to a Data Point. -```solidity -/** - * @notice Queries whether a given ODC token has a specific property - * @param ouid ODC unique ID - * @param prop property ID (property to inquire) - * @return bool true if the token has the property, false if not - */ - function hasProperty(uint256 ouid, bytes32 prop) external view returns (bool) -``` +The function `setODCImplementation()` SHOULD enable the delegation of the the management function to an IODC implementation. -```solidity -/** - * @notice Adds a given property to an existing ODC token - * @param ouid ODC unique ID - * @param prop property ID (property to add) - * @param restrictions An array of restrictions to be associated with the property - * @return An array with the respective restriction indexes - */ - function addProperty( - uint256 ouid, - bytes32 prop, - IMetaRestrictions.Restriction[] calldata restrictions - ) external returns (uint256[] memory) -``` -```solidity -/** - * @notice Removes an existing property from an existing ODC - * @param ouid ODC unique ID - * @param prop property ID (property to remove) - */ - function removeProperty(uint256 ouid, bytes32 prop) external -``` +### Data Points -```solidity -/** - * @notice Retrieves all the properties of a given category - * @param category category ID to consult - * @return An array with all the respective property IDs - */ - function propertiesOfCategory(bytes32 category) external view returns (bytes32[] memory) -``` +* Data Point SHOULD be `bytes32` storage units. +* Data Point SHOULD use a 4 bytes prefix for storing information relevant to the compatibility with other Data Points. +* Data Point SHOULD use the last 20 bytes for storage identifying which Registry allocated them. +* The RECOMMENDED internal structure of the Data Point is as follows: ```solidity /** - * @notice Retrieves all enabled properties of a given ODC - * @param ouid ODC unique ID - * @return An array with all the enabled property IDs - */ - function propertiesOf(uint256 ouid) external view returns (bytes32[] memory) + * RECOMMENDED internal DataPoint structure on the Reference Implementation: + * 0xPPPPVVRRIIIIIIIIHHHHHHHHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + * - Prefix (bytes4) + * -- PPPP - Type prefix (i.e. 0x4450 - ASCII representation of letters "DP") + * -- VV - Verison of DataPoint specification (i.e. 0x00 for the reference implementation) + * -- RR - Reserved + * - Registry-local identifier + * -- IIIIIIII - 32 bit implementation-specific id of the DataPoint + * - Chain ID (bytes4) + * -- HHHHHHHH - 32 bit of chain identifier + * - REGISTRY Address (bytes20) + * -- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - Address of Registry which allocated the DataPoint +**/ ``` -```solidity -/** - * @notice Retrieves a given user's ODC unique ID that has a specific property attached - * @param account user's account address - * @param prop property ID - * @return uint256 the ODC token ID, or 0 if such a user's token doesn't exist - */ - function getToken(address account, bytes32 prop) external view returns (uint256) -``` +### Data Point Registry -```solidity -/** - * @notice Retrieves all the ODC unique IDs owned by a given user that have a specific property attached - * @param account user's account address - * @param prop property ID to inquire - * @return An array with all the ODC token IDs that have the property attached - */ - function tokensWithProperty(address account, bytes32 prop) external view returns (uint256[] memory) -``` + * Data Point Registry SHOULD store Data Point access management data for Data Managers and Data Objects + * Data Point Registry SHOULD use the IDataPointRegistry interface: ```solidity -/** - * @notice Checks if a specific property exists in a given ODC token - * @param ouid ODC unique ID - * @param prop property ID - * @return bool true if the property is attached to the given token, false if not - */ - function exists(uint256 ouid, bytes32 prop) external view returns (bool) -``` +interface IDataPointRegistry { -```solidity -/** - * @notice Merges the given categories' related properties from one ODC token to another - * @param from origin ODC unique ID - * @param to target ODC unique ID - * @param categories An array with all the category IDs to merge - */ - function merge( - uint256 from, uint256 to, bytes32[] calldata categories) external -``` + /** + * @notice Verifies if an address has an Admin role for a DataPoint + * @param dp DataPoint + * @param account Account to verify + */ + function isAdmin(DataPoint dp, address account) external view returns (bool); -```solidity -/** - * @notice Splits an ODC token from its specific categories and mints a new one with the related properties attached - * @param ouid origin ODC unique ID - * @param categories category IDs to split - * @return newOuid the resulting (newly minted) ODC token ID - */ - function split(uint256 ouid, bytes32[] calldata categories) external returns (uint256 newOuid) -``` + /** + * @notice Allocates a DataPoint to an owner + * @param owner Owner of the new DataPoint + * @dev Owner SHOULD be granted Admin role during allocation + */ + function allocate(address owner) external payable returns (DataPoint); -```solidity -/** - * @notice Adds a new restriction to a given ODC property - * @param ouid ODC unique ID - * @param prop property ID - * @param restr the restriction to add - * @return uint256 The restriction's id - */ - function addRestriction( - uint256 ouid, - bytes32 prop, - Restriction calldata restr - ) external returns (uint256) { -``` + /** + * @notice Transfers a DataPoint to an owner + * @param dp Data Point to be transferred + * @param owner Owner of the new DataPoint + */ + function transferOwnership(DataPoint dp, address newOwner) external; -```solidity -/** - * @notice Removes a restriction identified by its index from a given ODC's property - * @param ouid ODC unique ID - * @param prop property ID - * @param ridx restriction index - */ - function removeRestriction( - uint256 ouid, - bytes32 prop, - uint256 ridx - ) external -``` + /** + * @notice Grant permission to grant/revoke other roles on the DataPoint inside an ODC Implementation + * This is useful if DataManagers are deployed during lifecycle of the application. + * @param dp DataPoint + * @param account New admin + * @return If the role was granted (otherwise account already had the role) + */ + function grantAdminRole(DataPoint dp, address account) external returns (bool); -```solidity -/** - * @notice Retrieves all restrictions attached to a given ODC's property - * @param ouid ODC unique ID - * @param prop property ID - * @return An array with all the requested restrictions (of type Restriction) - */ - function restrictionsOf(uint256 ouid, bytes32 prop) external view returns (Restriction[] memory) + /** + * @notice Revoke permission to grant/revoke other roles on the DataPoint inside an ODC Implementation + * @param dp DataPoint + * @param account Old admin + * @dev If an owner revokes Admin role from himself, he can add it again + * @return If the role was revoked (otherwise account didn't had the role) + */ + function revokeAdminRole(DataPoint dp, address account) external returns (bool); +} ``` +### Data Managers + * Data Manager MAY use read() or DataObject.read() to read data form Data Objects + * Data Manager MAY use write() to write data to Data Objects + * Data Manager MAY share Data Point with other Data Managers + * Data Manager MAY use multiple Data Points + * Data Manager MAY implement the logic for requesting Data Points from a Data Point Registry. +Data Managers are independent smart contracts that implement the business logic. They can either `read()` from a DataObject address, and `write()` through an ODC Implementation managing the delegated storage of the Data Points. -### Restrictions -**Restrictions** serve as a protective measure, ensuring that changes to **Properties** adhere to predefined rules, thereby maintaining the integrity and intended use of the information stored within the ODC. The **Restrictions** structure provides a layer of governance over the mutable **Properties**. **Property Managers** can check **Restrictions** applied to **Properties** before modifying the data stored within them. This further abstract the logic away from the storage, ensuring that mutability can be achieved and preserving the overall stability and reliability of the ODC. -```solidity -/** - * @dev ridxCounter Utilized to give continuous indices (ridxs) to restrictions - * @dev restrictions Mapping of restrictions' ridxs to the respective restriction data - * @dev byProperty Mapping of properties' unique identifiers to the respective set of ridxs (restrictions' indices) - * @dev byType Mapping of restrictions' unique types to the respective set of ridxs (restrictions' indices) - */ - struct TokenRestrictions { - uint256 ridxCounter; - mapping(uint256 => IMetaRestrictions.Restriction) restrictions; - mapping(bytes32 => EnumerableSet.UintSet) byProperty; - mapping(bytes32 => EnumerableSet.UintSet) byType; - } -``` +## Rationale -```solidity -/** - * @dev Adds a restriction to a given ODC's property - * @param l storage layout - * @param ouid ODC unique ID - * @param property identifier for the property - * @param r the restriction to add - * @return uint256 The index of the newly added restriction - */ - function addRestriction( - Layout storage l, - uint256 ouid, - bytes32 property, - IMetaRestrictions.Restriction memory r - ) internal returns (uint256) -``` +The decision to encode Data Points as bytes32 data containers is primarily driven by flexibility and future-proofing. Using bytes32 allows for a wide range of data encodings. This provides the developer with flexibility to accommodate diverse use cases. Furthermore, as Ethereum and its standards continue to evolve, encoding as bytes32 ensures that the Standard Adapters can support future data types or structures without requiring significant changes to the standard adapter itself. The Data Point encoding should have a prefix so that the Data Object can efficiently identify compatibility issues when accessing the data storage. Additionally, the prefix should be used to find the Data Point Registry and verify admin access of the Data Point. The use of a suffix for identifying the Data Point Registry is also required, in order for the Data Object to quickly discard badly formed transactions that aim to use a Data Point from an unmatching Data Point Registry. -```solidity -/** - * @dev Removes a restriction identified by its index from a given ODC's property - * @param l storage layout - * @param ouid ODC unique ID - * @param property identifier for the property - * @param ridx restriction index - */ - function removeRestriction( - Layout storage l, - uint256 ouid, - bytes32 property, - uint256 ridx - ) internal -``` +Data Objects being independent separate Smart Contracts that implement the same `read`/`write` interface for communicating with Data Managers is a decision mainly driven by the scalability of the system. Offering a simple interface for this 2-layer structure enables different applications to have their own addresses for storage of data as well as assets. It is up to each implementation to manage access to this Data Point storage space. This enables a wide array of complex, dynamic, and interactive use cases to be implemented with multiple ERCs as well as other smart contracts. +Data Objects offer flexibility in storing mutable on-chain data that can be modified as per the requirements of each specific use case. This enables the Data Managers to hold mutable states in delegated storage and reflect changes over time, providing a dynamic layer to the otherwise static nature of storage through most other standardized interfaces. +As the Data Points can be set to respond to a specific ODC implementation, Data Managers can decide to migrate the complete storage of a Data Object from one ODC implementation to another. +By leveraging multiple implementations of the IODC interface, this standard delivers a powerful framework that amplifies the potential of all ERCs (present and future). -### PropertyManagers -Both **Properties** and **Restrictions** within the **ODC** SHALL be stored on-chain and made accessible though **Property Managers**. The interface defining this interaction is as follows: +## Backwards Compatibility -### Categories and Registry +This ERC is intended to augment the functionality of existing token standards without introducing breaking changes. As such, it does not present any backwards compatibility issues. Already deployed ERCs can be wrapped as Data Points or owned in Vaults as Data Objects, and later exposed through any implementation of Data Managers. -Although the owner of ODCs can decide to implement an `allow list` for *Property Managers* that are enabled for interacting with the *Properties* and *Restrictions* stored within it, there are security considerations to be had regarding which *Property Managers* are allowed to interact with the ODCs internal storage. +See Reference Implementation. -The *Registry* is a smart contract for managing the internal governance by managing roles and permissions for *Property Managers*. This component is the single reference point for organizing *Property Managers* in *Categories*. As such, this system increases security by defining who has access to what, mitigating the possibility of unauthorized transactions or modifications. -The *Registry* keeps track of all the *Categories* as well as the *Properties* and *Restrictions* that the *Property Managers* have access to within those *Categories*. +## Reference Implementation -#### Registry Management Functions +We present an example implementation of Asset Vaults, a deterministic Asset Vault Factory, and a Data Object specialized in managing Asset Vaults. The following implementation can be used for wrapping multiple assets under the management of a Data Object, which in turn may be exposed through any Data Manager interface. -```solidity -/** - * @notice Retrieves the category info of a given property - * @param property property ID - * @return category The category ID of the property - * @return splitAllowed true if splitting is allowed, false if not - */ - function getCategoryInfoForProperty(bytes32 property) external view returns (bytes32 category, bool splitAllowed); -``` -```solidity -/** - * @notice Retrieves the info of a given category - * @param category category ID - * @return properties Array of property IDs included within the category - * @return splitAllowed true if splitting is allowed, false if not - */ - function getCategoryInfo(bytes32 category) external view returns (bytes32[] memory properties, bool splitAllowed); -``` +**Example Vault Interface** ```solidity -/** - * @notice Consults if a given address is a manager for a given category - * @param category category ID - * @param manager the address to inquire - * @return bool true if the address is manager for the category, false if not - */ - function isCategoryManager(bytes32 category, address manager) external view returns (bool); -``` +pragma solidity ^0.8.0; +interface IVault { + /** + * @notice Executes a state-changing call on a target + * @param target Contract to call + * @param data Data sent to the target, including function selector + * @param value Native coin value sent with the call + * @dev Access to this function SHOULD be protected + */ + function execute(address target, bytes calldata data, uint256 value) external returns (bytes memory); -```solidity -/** - * @notice Consults if a given address is a registered manager for a given property - * @param property property ID - * @param manager the address to inquire - * @return bool true if the address is manager for the property, false if not - */ - function isPropertyManager(bytes32 property, address manager) external view returns (bool); + /** + * @notice Executes a static call (non state-changing) on a target + * @param target Contract to call + * @param data Data sent to the target, including function selector + */ + function executeStatic(address target, bytes calldata data) external view returns (bytes memory); +} ``` +**Example Vault Factory Interface** ```solidity -/** - * @notice Consults if a given address has been granted ODC minter role - * @param manager the address to inquire - * @return bool true if the address has been granted minter role, false if not - */ - function isMinter(address manager) external view returns (bool); -``` - -### Metadata structure -Non-fungible tokens (NFTs) have rapidly gained prominence in the Ethereum ecosystem, serving as a foundation for various digital assets, ranging from art pieces to real estate tokens, to Identity-based systems. These tokens require metadata: information describing the properties of the token, which provides context and enriches the token's functionality within its ecosystem. -More often than not, developers manually generate NFT metadata for their respective projects, often leading to inconsistent structures and formats across different projects. This inconsistency hampers interoperability between NFT platforms and applications, slightly impeding the growth and development of the Ethereum NFT ecosystem. -Moreover, many protocols and standards rely on Metadata to store actual information that is not actionable by Smart Contracts. This generates a segregated model where NFTs as data-containers are not a self-contained unit, but a digital entity that lives fragmented between on-chain and off-chain storage. -The current EIP introduces a Metadata library that is designed to standardize the generation and handling of ODC metadata, promoting consistency, interoperability, and upgradeability. -The Metadata library includes base properties for [ERC-721](./eip-721.md) tokens and allows for the addition of extra **Properties**. These are flexible and extendable, covering `string`, `date`, `integer`, and `decimal` **Properties**. This broad range of property types caters to the diverse metadata needs across different use cases. -The Metadata library includes functions to generate metadata, add extra **Properties** to the metadata, merge two sets of **Properties**, and encode the metadata in a format compatible with popular NFT platforms like OpenSea. The library promotes reusability and reduces the amount of boilerplate code developers need to write. It is backwards compatible so that previous metadata models can also be implemented by generating a constant metadata link that always points to the same URI, as regular NFTs. +pragma solidity ^0.8.0; -#### ODC Metadata Functions +import "IVault.sol"; -```solidity -/** - * @notice Generates metadata for a given property of a ODC token - * @param prop property ID of ODC token - * @param ouid ODC unique ID - * @return The generated metadata - */ - function getMetadata(bytes32 prop, uint256 ouid) external view returns (Metadata.ExtraProperties memory); +interface IVaultFactory { + function deploy(bytes calldata data) external returns (IVault); + function deployDeterministic(bytes32 salt, bytes calldata data) external returns (IVault); + function computeVaultAddress(address deployer, bytes32 salt, bytes calldata data) external view returns (address); +} ``` -## Rationale - -The decision to encode Properties as bytes32 data containers in the ODC Interface is primarily driven by flexibility and future-proofing. Encoding as bytes32 allows for a wide range of data types to be stored, including but not limited to strings, integers, addresses, and more complex data structures, providing the developer with flexibility to accommodate diverse use cases. Furthermore, as Ethereum and its standards continue to evolve, encoding as bytes32 ensures that the ODC standard can support future data types or structures without requiring significant changes to the standard itself. - -Having a 2-layer data structure of `propertyKey` => `dataKey` => `dataValue` allows different applications to have their own address space. Implementations can manage access to this space using different `propertyKey` for different applications. - -A case-by-case example on potential Properties encodings was performed and summarized is provided in the appendix. - -The inclusion of Properties within an ODC provides the capability to associate a richer set of on-chain accessible information within the storage. This enables a wide array of complex, dynamic, and interactive use cases to be implemented with multiple ERCs as well as other smart contracts. - -Properties in an ODC offer flexibility in storing mutable on-chain data that can be modified as per the requirements of the token's use case. This allows the ODC to hold mutable states and reflect changes over time, providing a dynamic layer to the otherwise static nature of storage through a standardized interface. - -By leveraging Properties within the ODC, this standard delivers a powerful framework that amplifies the potential of all ERCs (present and future). In particular, ODCs can be leveraged to represent Account Abstraction contracts, abstracting the data-storage from the logic that consumes it, enabling for a single data-point to have multiple representations depending on the implementation. - - -## Backwards Compatibility - -This ERC is intended to augment the functionality of existing token standards without introducing breaking changes. As such, it does not present any backwards compatibility issues. Already deployed ERCs can be wrapped as Properties, with the application of on-chain data relevant to each use-case. - -It offers an extension that allows for the storage and retrieval of Properties within an ODC while maintaining compatibility with existing ERCs related to tokenization. - - -## Reference Implementation - - -### The DataStorage Library (ODC storage example) -This library implementation allows the creation of On-chain Data Containers that can store various data types, handle versions of data, and efficiently manage stored data. The `DataStorageLib` provides a system for handling data that is both efficient and flexible. The `struct DataStorageInternal` includes mappings that enable the storage of different data types. -These are: - * `keyValueData` for `bytes32` key-value pairs, - * `keyBytesData` for storing `bytes`, - * `keySetData` for sets of `bytes32` values, - * `keyMapData` for mappings of `bytes32 => bytes32` values. - -Dynamic Data Versions: -The library handles versioning of data. The `DataStorage` struct contains a 'current' version and a mapping that links versions to specific indexes in the storage. This allows the smart contract to maintain a historical record of state changes, as well as revert to previous versions if necessary. - -Clearing and Relocation: -Several functions, such as `clear()`, `wipe()`, and `moveData()` are dedicated to clearing and relocating stored data. This functionality allows efficient management of stored data. - -Addition and Removal of Data: -The library includes functions to set and get values of different data types. Functions such as `setValue()` and `getBytes32Value()` facilitate this functionality. The addition or removal of data is reflected in the respective set (e.g., `kvKeys`, `kbKeys`, `ksKeys`, `kmKeys`) to ensure that the library correctly keeps track of all existing keys. - -Efficient Data Retrieval: -There are several getter functions to facilitate data retrieval from these storage structures. These include getting all keys (`getAllKeys()`), checking if a set contains a value (`getSetContainsValue()`), and getting all entries from a mapping (`getMapAllEntries()`). - -Data Deletion: -The library provides efficient ways to delete data, like `deleteAllFromEnumerableSet()` and `deleteAllFromEnumerableMap()`. +**Example Vault Data Object contract** +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; -#### Examples of Properties and Restrictions +import "IVaultFactory.sol"; +import "BaseDataObject.sol"; -**On-chain Metadata**: This could include the name, description, image URL, and other metadata associated with the ODC. For example, in the case of an art NFT, the `setProperty` function could be used to set the artist's name, the creation date, the medium, and other relevant information. Afterwards, the implementation could include a `tokenUri()` function that procedurally exposes the `Property Data`, directly rendered from within the ODC. +contract VaultDataObject is IERC1271, BaseDataObject { + using EnumerableSet for EnumerableSet.AddressSet; -**Locking Restrictions**: They are utilized when an asset needs to be prevented from `transfer()` events or activity that would potentially change its internal storage. For example, when staking an asset or when locking for Fractionalization. + bytes4 public constant VAULT_FOR_SALT_SELECTOR = bytes4(keccak256("vaultForSalt(bytes32)")); //vaultForSalt(bytes32) returns(address) + bytes4 public constant ALL_VAULTS_SELECTOR = bytes4(keccak256("allVaults()")); //allVaults() returns(address[] memory) -**Ownership History**: The `setProperty` function could be used to record the ownership history of the ODC. For example, each time the ODC is transferred, a new entry could be added to the ownership history property. + bytes4 public constant DEPLOY_VAULT_SELECTOR = bytes4(keccak256("deployVault(address,bytes)")); //deployVault(address factory, bytes calldata data) + bytes4 public constant DEPLOY_DETERMINISTIC_VAULT_SELECTOR = bytes4(keccak256("deployDeterministicVault(address,bytes32,bytes)")); //deployDeterministicVault(address factory, bytes32 salt, bytes calldata data) + bytes4 public constant GRANT_SIGNATURE_VALIDATION_SELECTOR = bytes4(keccak256("grantSignatureValidation(address,bytes32,address)")); //grantSignatureValidation(address vault, bytes32 hash, address signer) + bytes4 public constant REVOKE_SIGNATURE_VALIDATION_SELECTOR = bytes4(keccak256("revokeSignatureValidation(address,bytes32,address)")); //revokeSignatureValidation(address vault, bytes32 hash, address signer) + bytes4 public constant VAULT_EXECUTE_SELECTOR = bytes4(keccak256("vaultExecute(address,address,bytes,uint256)")); //vaultExecute(address vault, address target, bytes calldata data, uint256 value) + bytes4 private constant ERC1271_INVALID_SIGNATURE = 0xffffffff; -**Royalties**: The `setProperty` function could be used to set a royalties property for the ODC. This could specify a percentage of future sales that should be paid to the original creator. + error UnknownVault(); + error UnknownSalt(); + error UnknownDataPoint(); + error DeloyedVaultAlreadyRegistered(); -**Zero-Knowledge Proofs**: The `setProperty` function could be used to store Identity information related to the ODCs owner and signed by KYC provider. This is combined with *Transfer Restrictions* to achieve identity-based Recovery of assets. + event SignatureValidationAdded(address vault, bytes32 hash, address signer); + event SignatureValidationRevoked(address vault, bytes32 hash, address signer); -**Oracle Subscription**: An oracle system can stream data periodically into the Property (i.e. asset price, weather condition, metrics, etc) + struct SignatureVerificationData { + mapping(address vault => mapping(address signer => bool valid)) validSigners; + } -**Storage Abstraction**: Multiple ERCs can make use of the same ODC for storing variables. For example, in a DeFi protocol, a Property with a stored value of `100` could signify `either USDT or USDC`, provided that the logic is connected to both USDT and USDC protocols. + struct DpData { + EnumerableSet.AddressSet deployedVaults; + mapping(bytes32 salt => address vault) deployedDeterministicVaults; + mapping(bytes32 hash => SignatureVerificationData) validSignatures; + } + mapping(uint256 dpIdx => DpData) private dpDataStorage; + mapping(address vault => DataPoint) internal vaultsToDataPoints; -### Wrapping of Assets (Example Property Manager) + /** + * @inheritdoc IERC1271 + */ + function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue) { + ... + } -The Wrapper addresses challenges and requirements that have emerged in the Ethereum ecosystem, specifically regarding the handling and manipulation of assets from different standards. The Wrapper component provides Backwards Compatibility by: + function computeDeterministicVaultAddress(DataPoint dp, address factory, bytes32 salt, bytes calldata data) external view returns (address) { + ... + } -Standardization: With the proliferation of various token standards on Ethereum, there is a need for a universal interface that can handle these token types uniformly. The Wrapper provides standardization, enabling a consistent approach to dealing with various token types, regardless of their implementation. -By allowing different token types to be bundled together into a single entity (ODC), this approach increases the composability and interoperability between various protocols and platforms. A more efficient and flexible asset management is achieved, allowing users to bundle multiple assets into a single ODC and reducing the complexity of handling individual assets. Furthermore, the capability to 'unwrap' these bundled assets as needed, provides users with granular control over their digital assets. -The transferring or interaction with multiple individual tokens often leads to a high accumulation of gas fees. However, by wrapping these assets into a single entity, users can perform operations in a more cost-effective manner. + function datapointOfVault(address vault) public view returns (DataPoint) { + DataPoint dp = vaultsToDataPoints[vault]; + if (DataPoint.unwrap(dp) == bytes32(0)) revert UnknownVault(); + return dp; + } + function dispatchRead(DataPoint dp, bytes4 operation, bytes calldata data) internal view virtual override returns (bytes memory) { + if (operation == VAULT_FOR_SALT_SELECTOR) { + bytes32 salt = abi.decode(data, (bytes32)); + return abi.encode(_vaultForSalt(dp, salt)); + } else if (operation == ALL_VAULTS_SELECTOR) { + return abi.encode(_allVaults(dp)); + } else { + revert UnknownReadOperation(operation); + } + } -```solidity -/** - * @notice This struct is used to receive all parameter for wrap function - * @param tokens The token addresses of the assets. - * @param amounts The amount of each asset to wrap (if applicable). - * @param ids The id of each asset to wrap (if applicable). - * @param types The type of each asset to wrap. - * @param unlockTimestamps The unlocking timestamps of wrapped assets. - * @param existingOuidToUse If different than 0, it represents the ODC to wrap assets on. If 0, new mNFT is minted. - */ - struct WrapData { - address[] tokens; - uint256[] amounts; - uint256[] ids; - uint256[] types; - uint256[] unlockTimestamps; - uint256 existingOuidToUse; + function dispatchWrite(DataPoint dp, bytes4 operation, bytes calldata data) internal virtual override returns (bytes memory) { + if (operation == DEPLOY_VAULT_SELECTOR) { + (address factory, bytes memory factoryData) = abi.decode(data, (address, bytes)); + return abi.encode(_deployVault(dp, factory, factoryData)); + } else if (operation == DEPLOY_DETERMINISTIC_VAULT_SELECTOR) { + (address factory, bytes32 salt, bytes memory factoryData) = abi.decode(data, (address, bytes32, bytes)); + return abi.encode(_deployDeterministicVault(dp, factory, salt, factoryData)); + } else if (operation == GRANT_SIGNATURE_VALIDATION_SELECTOR) { + (address vault, bytes32 hash, address signer) = abi.decode(data, (address, bytes32, address)); + _grantSignatureValidation(dp, vault, hash, signer); + return ""; + } else if (operation == REVOKE_SIGNATURE_VALIDATION_SELECTOR) { + (address vault, bytes32 hash, address signer) = abi.decode(data, (address, bytes32, address)); + _revokeSignatureValidation(dp, vault, hash, signer); + return ""; + } else if (operation == VAULT_EXECUTE_SELECTOR) { + (address vault, address target, bytes memory vaultCallData, uint256 value) = abi.decode(data, (address, address, bytes, uint256)); + return _vaultExecute(dp, vault, target, vaultCallData, value); + } else { + revert UnknownWriteOperation(operation); + } } +} ``` -```solidity -/** - * @notice This function is called by the users to wrap assets inside an ODC - * @param data The data used when wrapping. - */ - function wrap(WrapData memory data) external returns (uint256, uint256[] memory); -``` +**Example Fungible Token Data Manager implementation** ```solidity -/** - * @notice Rewrap - * @dev This function is called by the users to be able to extend fungible assets' amounts. - * @param data The data used when rewrapping. - */ - function rewrap(RewrapData memory data) external returns (uint256, uint256[] memory); -``` +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; -```solidity -/** - * @notice Unwrap - * @dev This function is called by the users to unwrap assets from a ODC. - * @dev This function is called by the users to unwrap assets from a ODC. - * @param ouid The ODC id associated. - * @param restrictionIds The restriction ids of the properties. - * @param tokens The token addresses of the assets. - * @param types The type of each asset to unwrap. - * @param ids The ids of each asset to unwrap if applicable - */ - function unwrap(uint256 ouid, uint256[] calldata restrictionIds, address[] calldata tokens, uint256[] calldata types, uint256[] calldata ids) external; -``` +contract ERC20DataManager is IERC20, IERC20Metadata, IERC20Errors, Ownable, ERC20Approvals, ERC20Transfers, ERC20Burnable, ERC20Mintable, ERC20Metadata { + bytes4 internal BALANCE_OF_SELECTOR = bytes4(keccak256("balanceOf(address)")); + bytes4 internal TOTAL_SUPPLY_SELECTOR = bytes4(keccak256("totalSupply()")); + bytes4 internal MINT_SELECTOR = bytes4(keccak256("mint(address,uint256)")); + bytes4 internal BURN_SELECTOR = bytes4(keccak256("burn(address,uint256)")); + bytes4 internal TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,address,uint256)")); -```solidity -/** - * @notice RegisterNewType - * @dev This function is called by the owner to register a new type of asset. - * @param propertyManager The property manager address that is handling this specific type. - * @param isFungible true if the asset is fungible and false otherwise. - */ - function registerNewType(IExternalTokenPropertyManager propertyManager, bool isFungible) external -``` + DataPoint internal immutable datapoint; + IDataObject public immutable fungibleDO; + IODC public odc; + mapping(address account => mapping(address spender => uint256)) private _allowances; -### Fractionalization (Example Property Manager) + constructor( + bytes32 _datapoint, + address _odc, + address _fungibleDO, + string memory name_, + string memory symbol_ + ) Ownable(msg.sender) ERC20Metadata(name_, symbol_) { + datapoint = DataPoint.wrap(_datapoint); + odc = IODC(_odc); + fungibleDO = IDataObject(_fungibleDO); + } -Fractionalizer is a Property Manager that enables the creation of fraction tokens for a specific ODC. As an ODC can be the repository of information for multiple types of assets, the Fractionalization process may require of a special contract ruling the governance of said assets. For example, if the ODC represents a piece of art, and the fractions represent a percentage of the ownership over it, a governor Property Manager contract would be required to implement the logic detailing under which conditions the full ownership of the ODC is transferable. + function totalSupply() external view override returns (uint256) { + return abi.decode(fungibleDO.read(datapoint, TOTAL_SUPPLY_SELECTOR, ""), (uint256)); + } + function balanceOf(address account) external view override returns (uint256) { + return abi.decode(fungibleDO.read(datapoint, BALANCE_OF_SELECTOR, abi.encode(account)), (uint256)); + } -```solidity -/** - * @notice createNewErc20Fractions - * @param name The name of the erc20 token. - * @param symbol The symbol of the erc20 token. - * @param amountToMint Amount of erc20 fractions to mint to the msg.sender. - * @param ouid The id of ODC to lock. - * @param governor The governor of the fractions, if it's empty, governor is created. - * @param data The governance data to use if the governor is empty. - */ - function createNewErc20Fractions( - string calldata name, - string calldata symbol, - uint256 amountToMint, - uint256 ouid, - address governor, - GovernanceDeployer.GovernanceData calldata data - ) external returns (address) -``` + function _checkMinter() internal view override { + _checkOwner(); + } -```solidity -/** - * @notice createNewErc721Fractions - * @param name The name of the erc721 token. - * @param symbol The symbol of the erc721 token. - * @param baseUri The baseUri of the erc721 token. - * @param idsToMint ids of erc721 fractions to mint to the msg.sender. - * @param ouid The id of ODC to lock. - * @param governor The governor of the fractions, if it's empty, msg.sender is used. - */ - function createNewErc721Fractions( - string calldata name, - string calldata symbol, - string calldata baseUri, - uint256[] calldata idsToMint, - uint256 ouid, - address governor - ) external returns (address) + function _writeTransfer(address from, address to, uint256 amount) internal override { + if (from == address(0)) { + odc.write(address(fungibleDO), datapoint, MINT_SELECTOR, abi.encode(to, amount)); + } else if (to == address(0)) { + odc.write(address(fungibleDO), datapoint, BURN_SELECTOR, abi.encode(from, amount)); + } else { + odc.write(address(fungibleDO), datapoint, TRANSFER_SELECTOR, abi.encode(from, to, amount)); + } + } +} ``` - ## Security Considerations -1. The management of Properties should be handled securely, with appropriate access control mechanisms in place to prevent unauthorized modifications. -2. Storing enriched metadata on-chain could potentially lead to higher gas costs. This should be considered during the design and implementation of ODCs. -3. Increased on-chain data storage could also lead to potential privacy concerns. It's important to ensure that no sensitive or personally identifiable information is stored within ODC metadata. -4. Ensuring decentralized control over the selection of Property Managers is critical to maintain the decentralization ethos of Ethereum. -5. Developers must also be cautious of potential interoperability and compatibility issues with systems that have not yet adapted to this new standard. - -The presence of mutable Properties can be used to implement security measures. In the context of preventing unauthorized access and modifications, an ODC-based system could implement the following strategies, adapted to each use-case: - -**Role-Based Access Control (RBAC)**: Only accounts assigned to specific roles at a Property level can perform certain actions on a Property. For instance, only an 'owner' might be able to call setProperty functions. - -**Time Locks**: Time locks can be used to delay certain actions, giving the community or a governance mechanism time to react if something malicious is happening. For instance, changes to Properties could be delayed depending on the use-case. - -**Multi-Signature (Multisig) Properties**: Multisig Properties could be implemented in a way that require more than one account to approve an action performed on the Property. This could be used as an additional layer of security for critical functions. For instance, changing certain properties might require approval from multiple trusted signers. - +No specific security considerations are derived from this ERC. ## Copyright diff --git a/assets/erc-7208/erc-7208-compat.md b/assets/erc-7208/erc-7208-compat.md index 00a67cc031..571ef9f912 100644 --- a/assets/erc-7208/erc-7208-compat.md +++ b/assets/erc-7208/erc-7208-compat.md @@ -3,38 +3,18 @@ We provide a cherrypicked list of possible points of contact between ERC-7208 (on-chain data container) and other tokenization standards and proposals. -**ERC-1400 (Security Token Standard)**: In aggregate provides a suite of standard interfaces for issuing / redeeming security tokens, managing their ownership and transfer restrictions and providing transparency to token holders on how different subsets of their token balance behave with respect to transfer restrictions, rights and obligations. ODCs can enhance ERC-1400 by offering more dynamic and flexible data management. ODCs allow for the storage and modification of properties related to security tokens, such as compliance information or ownership details. This integration could lead to more efficient and transparent security token offerings. Additionally, hooks and triggers can be implemented within the logic of specific use-cases so that compliance with regulatory frameworks is achieved automatically. +**ERC-1400 (Security Token Standard)**: In aggregate provides a suite of standard interfaces for issuing / redeeming security tokens, managing their ownership and transfer restrictions and providing transparency to token holders on how different subsets of their token balance behave with respect to transfer restrictions, rights and obligations. ERC-7208 can enhance ERC-1400 by offering more dynamic and flexible data management. Data Objects enable the storage and modification of on-chain data related to security tokens, such as compliance information or ownership details. In the case of assets that are already issued under ERC-1400, they can be wrapped into a Vault Data Object and exposed through any Data Manager interface (including ERC-3643 and others). Alternative, if the asset is issued with native Data Point storage, the integration could lead to more efficient and transparent security token offerings. The modular and adaptable nature of ERC-7208 enable transparent enhancements to the internal logic of individual ERC-1400 tokens. -**EIP-2309 (Consecutive batch minting)**: ODCs are compatible with EIP-2309, allowing for the batch minting process to be enriched with additional data. ODCs could store information related to each batch, such as metadata or batch-specific attributes, without disrupting the minting process. +**EIP-2309 (Consecutive batch minting)**: ERC-7208 is compatible with EIP-2309, allowing for the batch minting process to be enriched with additional data. ODCs could store information related to each batch, such as metadata or batch-specific attributes, without disrupting the minting process. This information may be stored on Data Points, internally within the ODC Implementation, or locally at the Data Manager exposing the EIP-2309 interface. -**EIP-2615 (Swap Orders)**: ODCs can work alongside EIP-2615, enhancing swap orders with additional data capabilities. Properties within ODCs can store terms, conditions, or other relevant data for swap orders, facilitating more complex and informed swap transactions. Additionally, Swap Orders can be bundled together by Wrapper Property Manager, generating bundles of orders to be interacted with together. +**EIP-2981 (Royalties)**: ERC-7208 can complement EIP-2981 as a Data Manager by providing a flexible way to handle royalties. Data Objects can store and manage the low level storage of royalty information dynamically and independently from the Data Manager's implemented interface. This enables a complex royalty structure that can change over time or based on certain conditions, like embedding compliance checks on the functions modifying the storage and simultaneously exposing multiple interfaces for accessing the storage. For instance, by leveraging ERC-7208, an individual royalty based NFT can be traded in a compliant manner, concurrently under both an ERC-721 interface as well as an ERC-20 through the use of Data Managers. -**EIP-2981 (Royalties)**: ODCs can complement EIP-2981 as a Property Manager by providing a flexible way to handle royalties. Properties in ODCs can store and manage royalty information dynamically within the data and metadata, allowing for more complex royalty structures that can change over time or based on certain conditions. +**ERC-3643 (Security Tokens)**: ERC-3643 defines a *Security Token interface for Regulated Exchanges* based on ERC-20 token standard. The ERC-7208 can be used for wrapping buckets of tokens (irrespective of their ERC) and adapting their logic to the ERC-3643. Additionally, a Data Object storing native ERC-3643 tokens can be used for improving the compliance logic and enabling the trading of underlying securities simultaneously through multiple interfaces that respond to different regulatory frameworks. Moreover, the separation of the storage enables the logic to implement functionalities that were not initially a part of the original ERC, such as identity-based recovery of assets, role-based access control, the introduction of cross-chain support, etc. -**ERC-3643 (Permissioned Tokens)**: ERC-3643 proposal defines *Security Token interface* based on ERC-20 token standard with additional requirement for sender and receiver of the token to be approved by the token issuer. This interface relies on the *OnchainID* system to provide Identity information, process KYC and other credentials, providing that data on-chain for token holders for minting, burning, and recovery of assets. Compatibility with this interface can be implemented as an ODC **Property Manager**, with the added benefit of a more versatile on-chain identity management derived from alternative **Property Managers**. Implementing ERC-3643 tokens as Property Managers could lead to more robust permissioned tokens. ODC Properties can store and manage permissions, enhancing the control and flexibility of permissioned tokens. +**ERC-4337 (Account Abstraction)**: ERC-7208 can provide a standardized method to store and manage the complex data structures required by abstracted accounts. This can include user preferences, access control lists, recovery options, and other customizable account features. The mutable states of abstracted accounts can be efficiently handled using Data Objects. This, in turn, improves the adaptability and security of abstracted accounts. Additionally, an ERC-7208 implementation supporting meta-transactions and Data Points separated by chain-id can be developed to fully abstract account management across blockchains. -**ERC-4337 (Account Abstraction)**: ODCs can provide a standardized method to store and manage the complex data structures required by abstracted accounts. This can include user preferences, access control lists, recovery options, and other customizable account features. The mutable states of abstracted accounts can be efficiently handled using ODCs. This, in turn, improves the adaptability and security of abstracted accounts. +**EIP-4626 (Tokenized Vaults)**: Tokenized Vaults inherit from a single ERC-20 and ERC-2612 for approvals via EIP-712 secp256k1 signatures. ODCs can enhance EIP-4626 by providing a more dynamic data layer for tokenized vaults. Data Objects and ODCs can store information about the assets in the vault, conditions for access, or other relevant data, enabling more nuanced interactions with tokenized vaults. Additionally, the Data Object can store more than a single ERC-20, greatly increasing the capabilities of Tokenized Vaults. -**EIP-4626 (Tokenized Vaults)**: Tokenized Vaults inherit from ERC-20 and ERC-2612 for approvals via EIP-712 secp256k1 signatures. ODCs can enhance EIP-4626 by providing a more dynamic data layer for tokenized vaults. Properties in ODCs can store information about the assets in the vault, conditions for access, or other relevant data, enabling more nuanced interactions with tokenized vaults. +**ERC-4907 (Shared Ownership)**: The integration of ERC-4907 as a Data Manager with ERC-7208 Data Object storage can enhance the rental experience by allowing for additional rental-related data directly on-chain, such as rental terms, user permissions, and other customizable settings which would be self-contained within Data Points and therefore automatically updated as metadata. ERC-4907's rental mechanism complements ERC-7208's ability to manage mutable on-chain data. By combining these two, NFTs can not only be rented out for specific periods but also have their traits or states dynamically managed and updated during the rental period. This combination enhances security and compliance in NFT transactions, particularly for Real World Asset Tokenization. Rental agreements, regulatory compliance, intelectual property and user rights can be embedded within Data Objects to ensure that the NFT usage adheres to predefined rules. -**ERC-4885 (Fractional Ownership)**: ODCs can improve ERC-4885 Fractional Ownership by providing a standardized way to manage and track metadata related to the life-cycle of the tokens. ODC Properties can store details about each fractional owner and their ownership percentage, making the management of fractional ownership more efficient, versatile and transparent. - -**ERC-4886 (Proxy Ownership Register)**: Combining the security features of ERC-4886 with the data management capabilities of ERC-7208 could open up new use cases by working together to provide a more comprehensive solution for user security and experience. For example, the proxy address in ERC-4886 could be used to interact with various dApps while maintaining the userโ€™s settings and preferences stored in an ERC-7208 ODC. This synergy allows users to have a secure, consistent, and personalized experience across the Ethereum ecosystem. Moreover, ERC-4886 addresses the issue of user confidence when interacting with smart contracts, as it separates the interaction address from the asset storage address. When combined with ERC-7208, it can ensure that users feel safe interacting with various applications, knowing that their settings are stored securely in an ODC and their assets are safe in a different address. - -**ERC-4907 (Shared Ownership)**: The integration of ERC-4907 as a Property Manager with ERC-7208 ODC storage can enhance the rental experience by allowing for additional rental-related data directly on-chain, such as rental terms, user permissions, and other customizable settings which would be self-contained within **Properties** and therefore automatically updated as metadata. ERC-4907's rental mechanism complements ERC-7208's ability to manage mutable on-chain data. By combining these two, NFTs can not only be rented out for specific periods but also have their properties or states dynamically managed and updated during the rental period using ODCs. This combination enhances security and compliance in NFT transactions, particularly for Real World Asset Tokenization. Rental agreements, regulatory compliance, intelectual property and user rights can be embedded within ODCs to ensure that the NFT usage adheres to predefined rules. - -**EIP-5050 (Interactive NFTs)**: The integration of ERC-7208 with ERC-5050 could potentially enable new forms of interactive applications, where the data stored via ODC can be utilized in interactive NFT environments governed by either native ERC-5050 or a Property Manager implementation of ERC-5050. This could open up innovative use cases, especially in areas like gaming, digital art, and decentralized identity, where the interplay of secure data storage and interactive token functionalities is crucial. - -**EIP-5095 (Principal Tokens)**: An ERC-5095 Property Manager implementation could potentially be used to represent specific financial states or obligations as part of a broader data container structure. This way, ERC-5095 tokens could be part of a larger ODC structure, representing a financial component within a multi-faceted on-chain agreement or asset. Compliance, Identity, and predefined rules can be included as part of the logic once it is abstracted away from the storage, which is of particular interest for use cases referrent to Realt World Asset tokenization. - -**EIP-5185 (Metadata Upgradeability)**: ODCs align well with EIP-5185's focus on metadata upgradeability. Properties in ODCs can be used to store and update metadata, allowing for more flexible and dynamic metadata management for tokens. However, the main benefit for implementing EIP-5185 as a Property Manager is the access to dynamically metadata upgradeability by tapping into the stored data within the ODC. - -**EIP-5505 (Asset-Backed NFTs)**: The Wrapper and Fractionalizer Property Managers within ERC-7208 ODCs can be used to ensure that the fractionalization adheres to relevant regulations and custom rules, providing a compliant and flexible framework for ERC-5505 tokens, if they are implemented as ODCs. - -**EIP-5560 (Redeemable NFTs)**: ERC-5560 and ERC-7208 complement each other's capability to tokenize real-world assets. ODCs can be used to manage the data and rules surrounding the redemption process of a physical asset represented by an NFT, including tracking the redemption status, ownership history, and other relevant metadata of the tokenized assets. By leveraging both standards together, digital tokens can not only represent ownership of physical assets but also provide a standardized and regulated mechanism for their redemption. - -**EIP-5633 (Composable Soulbound NFTs)**: ERC-7208 ODCs can be utilized to manage the additional data and rules associated with ERC-5633 soulbound tokens, such as identity management, access and usage rights, and specific account associations. - -**ERC-6960 (Dual Layer Token Standard)**: By implementing ERC-6960 as an ODC, the two-level classification system complements ERC-7208's focus on real-world asset tokenization. The `mainId` and `subId`` structure of ERC-6960 can be utilized to represent various layers of ownership and characteristics of a real-world asset within an ODC framework, providing more nuanced control and representation of assets. This synergy can enable more sophisticated management and fractionalization of assets, adhering to regulatory frameworks and custom management rules. - -**ERC-7540 (Asynchronous ERC-4626 Tokenized Vaults)**: ERC-7540 vaults's are focused on asynchronous deposit and redemption. Integrating ERC-7540, either by Wrapping or by Property Manager, will complement ERC-7208's ODCs and facilitate more complex financial products. Comnplex DeFi products like undercollateralized loans, insurance products, or tokenized stocks often require operations to be handled in a non-instantaneous manner. However, the nature of these products requires adhering to regulatory compliance and identity management solutions. This can easily be achieved by integrating ODCs Identity Property Managers. +**ERC-7540 (Asynchronous ERC-4626 Tokenized Vaults)**: ERC-7540 vaults's are focused on asynchronous deposit and redemption. Integrating ERC-7540, either by Wrapping into a Data Object or by exposing a 7208 Data Manager, will facilitate more complex financial products. DeFi products like undercollateralized loans, insurance products, or tokenized stocks often require operations to be handled in a non-instantaneous manner. However, the nature of these products requires adhering to regulatory compliance and identity management solutions. This can easily be achieved by implementing the use of on-chain adapters that enhance the logic while keeping the data secure. diff --git a/assets/erc-7208/erc-7208-overview.svg b/assets/erc-7208/erc-7208-overview.svg index 049fd93c3d..13cc55680e 100644 --- a/assets/erc-7208/erc-7208-overview.svg +++ b/assets/erc-7208/erc-7208-overview.svg @@ -1,4 +1,4 @@ -ODC Contract
List of restrictions with relatedย type & dataย ย 
List of restrictions with relatedย type & dataย ย 
List of properties which can be enabled/disabled and store any data
List of properties which can be enabled/disabled and store any data
RestrictionsEmpty restriction (used a s a placeholder)Transfer restriction (dissalows transfer of ODC)Lock ERC20 restrictions (locks an ERC20 Property till specified timestamp)ODC TokenDEX Property Manager
  1. POOL_CREATOR (pool_address) Property
  2. LIQUIDITY_PROVIDED (pool_aadress) Property
    adds LOCK_ERC20_RESTRICTION
POOL_CREATOR (pool_address) Property...
KYC Property Manager
  1. KYC_PASSED PROPERTY
    adds TRANSFER_RESTRICTION
KYC_PASSED PROPERTY...
Some DEX
in the ecosystem
Some DEX...
Some KYC Verificator
in the ecosystem
Some KYC Verificator...
Fractionalizer Property Manager
  1. FRACTIONS_BALANCE(address) Property
    adds UNDERLYING_ASSET_RESTRICTION
FRACTIONS_BALANCE(address) Property...
Some Marketplace
in the ecosystem
Some Marketplace...
PropertyRegistry
getCategoryInfoForProperty()ย - finds a category of a property
getCategoryInfoForProperty()ย - finds a category of a property
getCategoryInfo() - returns a list of properties and if the category can be splited
getCategoryInfo() - returns a list of properties and if the category can be sp...
Manages KYCed Property
Manages KYCed Property
Manages fractionalization rules and Properties
Manages fractionalization rules and Properties
Manages categoriesand data governance
User
User
Off-chain
Off-chain
On-Chain
On-Chain
getUri()
JSON with PROPERTY_DATA
JSON with PROPERTY_DATA
Metadata generated
from on-chain data
Metadata generated...
Manages DEX interactions and Properties
Manages DEX interactions and Properties
ERC20 Property Manager
  1. Balance (address) Property
  2. Allowance (address) Property
Balance (address) Property...
Manages ERC20 interactions and Properties
Manages ERC20 interactions and Properties
ERC1155 Property Manager
  1. Balance (address, id) Property
  2. Allowance (address, id) Property
Balance (address, id) Property...
Manages ERC1155 interactions and Properties
Manages ERC1155 interactions and Properties
Text is not SVG - cannot display
\ No newline at end of file +
DataObject
DataObject
DataManager
On-chain Data Container
DataManager
DataManager
DataObject
Buckets of assets
and asset data
Access Management
Business Logic and
user interface for
interacting with the data
\ No newline at end of file diff --git a/assets/erc-7208/erc-7208-technical-overview.svg b/assets/erc-7208/erc-7208-technical-overview.svg new file mode 100644 index 0000000000..d1ab5cc3cc --- /dev/null +++ b/assets/erc-7208/erc-7208-technical-overview.svg @@ -0,0 +1,4 @@ + + + +
Access Management
ODC_ID => DM_ID => DP_ID
...
...
ODC Smart Contract
ODC ID
Management
DataManager (DM)
Implements business logic
Access DM
Manage ODC
User
Manage DM <=> DataObject relationship
DM Mainteiner
Storage
DataPoint_DM1_1
odc_id1
odc_id2
DataPoint_DM1_2
odc_id1
odc_id2
DataPoint_DM2_1
odc_id1
odc_id2
DataPoint_DM2_2
odc_id1
odc_id2
DataPoint Logic
Performs logic directly related
to storage management
Multiple DataObject (DO)
Contracts
DataObject (DO)
Contract
ODC implementation
(ERC-7208)

DataManager (DM)
Implements business logic
DataManager (DM)
Implements business logic
DataManager (DM)
Implements usecase logic
Data is stored in DataPoints
DataObject exposes low level
Data Management interface
ODC implementation
manages access
and indexes DataObjects
DataPoint Registry

Should provide information if an account can grant access to DP for DMs and DOs

function isAdmin(DataPoint, account) returns (bool)
Verify DP Maintainer
is Admin of DataPoint
Allocate DataPoint
DataManagers Implement
business logic
(user-facing interfaces)
DP Registry implementation
separates the space of
compatible DataPoints
\ No newline at end of file From 700052f37aff5dcec29c0993fd721f88464b3fd4 Mon Sep 17 00:00:00 2001 From: galimba Date: Mon, 8 Jul 2024 10:01:45 +0200 Subject: [PATCH 059/126] Update ERC-7208: fixes to example + grammar Merged by EIP-Bot. --- ERCS/erc-7208.md | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/ERCS/erc-7208.md b/ERCS/erc-7208.md index 121183398a..167b928911 100644 --- a/ERCS/erc-7208.md +++ b/ERCS/erc-7208.md @@ -14,7 +14,9 @@ requires: 165 ## Abstract -"On-chain Data Containers" (ODCs) are used for indexing and storing data in Smart Contracts called "Data Objects" (DOs). Information stored in Data Objects can be accessed and modified by the implementation of smart contracts called "Data Managers" (DMs). This ERC defines a series of interfaces for the separation of the storage of data from the implementation of the logic functions that govern such data. We introduce the interfaces for ODCs, the structures associated with DOs for abstracting storage, the DMs to access or modify the data, and finally the interfaces for compatibility Registries that enable Data Portability (horizontal mobility) between different implementations of this ERC. + +"On-chain Data Containers" (ODCs) are used for indexing and storing data in Smart Contracts called "Data Objects" (DOs). Information stored in Data Objects can be accessed and modified by implementing smart contracts called "Data Managers" (DMs). This ERC defines a series of interfaces for the separation of the storage of data from the implementation of the logic functions that govern such data. We introduce the interfaces for ODCs, the structures associated with DOs for abstracting storage, the DMs to access or modify the data, and finally the interfaces for compatibility Registries that enable Data Portability (horizontal mobility) between different implementations of this ERC. + @@ -24,7 +26,9 @@ As the Ethereum ecosystem grows, so does the demand for on-chain functionalities While such diversity spurs innovation, different projects will implement their bespoke solutions for interoperability, resulting in a highly fragmented landscape. The lack of a standard mechanism for adapting tokenized across ERC standards is accentuating the interoperability issues. -We recognize there is no โ€œone size fits allโ€ solution to solve the standardization and interoperability challenges. Most assets - either Fungible, Non-Fungible, Digital Twins, Real-world Assets, DePin, etc - have multiple mechanisms for representing them as on-chain tokens by utilizing different standard interfaces. However, for those assets to be exchanged, traded, or interacted with, protocols are required to implement each of those standards to be able to access and modify the on-chain data. Moreover, the immutability of smart contracts plays a role against future-proofing their implementations by supporting new tokenization standards. A collaborative effort must be made to enable On-chain Adapters to enable the interaction between assets tokenized under different standards. + +We recognize there is no โ€œone size fits allโ€ solution to solve the standardization and interoperability challenges. Most assets - Fungible, Non-Fungible, Digital Twins, Real-world Assets, DePin, etc - have multiple mechanisms for representing them as on-chain tokens using different standard interfaces. However, for those assets to be exchanged, traded, or interacted with, protocols must implement compatibility with those standards before accessing and modifying the on-chain data. Moreover, the immutability of smart contracts plays a role in future-proofing their implementations by supporting new tokenization standards. A collaborative effort must be made to enable interaction between assets tokenized under different standards. The current ERC provides the tools for developing such On-chain Adapters. + We aim to abstract the on-chain data handling from both the logical implementation and the ERC interfaces exposing the underlying data. This EIP proposes a series of interfaces for storing and accessing data on-chain, codifying the underlying assets as generic "Data Points" that may be associated with multiple interoperable and even concurrent ERC interfaces. This proposal is designed to work by coexisting with previous and future token standards, providing a flexible, efficient, and coherent mechanism to manage asset interoperability. @@ -42,7 +46,8 @@ We aim to abstract the on-chain data handling from both the logical implementati ### Terms -**ODC**: A uniquely identifiable data container that is used for indexing Data Objects or storing Data Points. + +**ODC**: A uniquely identifiable data structure used for indexing Data Objects or storing Data Points. **ODC Implementation**: One or many Smart Contracts implementing the ODC access-management logic. @@ -56,7 +61,8 @@ We aim to abstract the on-chain data handling from both the logical implementati The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. -### ODC + +### ODC Interface * ODC SHOULD manage internal IDs for each data container. * ODC SHOULD manage the access of Data Managers to Data Objects. @@ -121,7 +127,8 @@ interface IODC { } ``` -### Data Objects + +### Data Object Interface * Data Object SHOULD implement the logic directly related to handling the data stored on Data Points. * Data Object SHOULD implement the logic for transfering management of its Data Points to a different ODC Implementation. @@ -161,7 +168,8 @@ Data Objects can receive `read()` or `write()` requests when a Data Manager is r The function `setODCImplementation()` SHOULD enable the delegation of the the management function to an IODC implementation. -### Data Points + +### Data Point Structure * Data Point SHOULD be `bytes32` storage units. * Data Point SHOULD use a 4 bytes prefix for storing information relevant to the compatibility with other Data Points. @@ -185,7 +193,9 @@ The function `setODCImplementation()` SHOULD enable the delegation of the the ma **/ ``` -### Data Point Registry + +### Data Point Registry Interface + * Data Point Registry SHOULD store Data Point access management data for Data Managers and Data Objects * Data Point Registry SHOULD use the IDataPointRegistry interface: @@ -234,7 +244,8 @@ interface IDataPointRegistry { } ``` -### Data Managers + +### Data Manager Contract * Data Manager MAY use read() or DataObject.read() to read data form Data Objects * Data Manager MAY use write() to write data to Data Objects @@ -247,9 +258,10 @@ Data Managers are independent smart contracts that implement the business logic. ## Rationale -The decision to encode Data Points as bytes32 data containers is primarily driven by flexibility and future-proofing. Using bytes32 allows for a wide range of data encodings. This provides the developer with flexibility to accommodate diverse use cases. Furthermore, as Ethereum and its standards continue to evolve, encoding as bytes32 ensures that the Standard Adapters can support future data types or structures without requiring significant changes to the standard adapter itself. The Data Point encoding should have a prefix so that the Data Object can efficiently identify compatibility issues when accessing the data storage. Additionally, the prefix should be used to find the Data Point Registry and verify admin access of the Data Point. The use of a suffix for identifying the Data Point Registry is also required, in order for the Data Object to quickly discard badly formed transactions that aim to use a Data Point from an unmatching Data Point Registry. -Data Objects being independent separate Smart Contracts that implement the same `read`/`write` interface for communicating with Data Managers is a decision mainly driven by the scalability of the system. Offering a simple interface for this 2-layer structure enables different applications to have their own addresses for storage of data as well as assets. It is up to each implementation to manage access to this Data Point storage space. This enables a wide array of complex, dynamic, and interactive use cases to be implemented with multiple ERCs as well as other smart contracts. +The decision to encode Data Points as bytes32 data containers is primarily driven by flexibility and future-proofing. Using bytes32 allows for a wide range of data encodings. This provides the developer with many options to accommodate diverse use cases. Furthermore, as Ethereum and its standards continue to evolve, encoding as bytes32 ensures that the Standard Adapters can support future data types or structures without requiring significant changes to the standard adapter itself. The Data Point encoding should have a prefix so that the Data Object can efficiently identify compatibility issues when accessing the data storage. Additionally, the prefix should be used to find the Data Point Registry and verify admin access of the Data Point. The use of a suffix for identifying the Data Point Registry is also required, for the Data Object to quickly discard badly formed transactions that aim to use a Data Point from an unmatching Data Point Registry. + +Data Objects being independent separate Smart Contracts that implement the same `read`/`write` interface for communicating with Data Managers is a decision mainly driven by the scalability of the system. Offering a simple interface for this 2-layer structure enables different applications to have their addresses for storage of data as well as assets. It is up to each implementation to manage access to this Data Point storage space. This enables a wide array of complex, dynamic, and interactive use cases to be implemented with multiple ERCs as well as other smart contracts. Data Objects offer flexibility in storing mutable on-chain data that can be modified as per the requirements of each specific use case. This enables the Data Managers to hold mutable states in delegated storage and reflect changes over time, providing a dynamic layer to the otherwise static nature of storage through most other standardized interfaces. From 302ce653950a31e7efb10ac10d60f421e929ca11 Mon Sep 17 00:00:00 2001 From: galimba Date: Tue, 9 Jul 2024 14:26:21 +0200 Subject: [PATCH 060/126] Update ERC-7208: fixes to example Merged by EIP-Bot. --- ERCS/erc-7208.md | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7208.md b/ERCS/erc-7208.md index 167b928911..c5cc463ca6 100644 --- a/ERCS/erc-7208.md +++ b/ERCS/erc-7208.md @@ -261,6 +261,9 @@ Data Managers are independent smart contracts that implement the business logic. The decision to encode Data Points as bytes32 data containers is primarily driven by flexibility and future-proofing. Using bytes32 allows for a wide range of data encodings. This provides the developer with many options to accommodate diverse use cases. Furthermore, as Ethereum and its standards continue to evolve, encoding as bytes32 ensures that the Standard Adapters can support future data types or structures without requiring significant changes to the standard adapter itself. The Data Point encoding should have a prefix so that the Data Object can efficiently identify compatibility issues when accessing the data storage. Additionally, the prefix should be used to find the Data Point Registry and verify admin access of the Data Point. The use of a suffix for identifying the Data Point Registry is also required, for the Data Object to quickly discard badly formed transactions that aim to use a Data Point from an unmatching Data Point Registry. + +Data Manager implementations decide which Data Points they will be using. Their allocation is managed through a Data Point Registry, and the access to the Data Point is managed by passing through the ODC Implementation. + Data Objects being independent separate Smart Contracts that implement the same `read`/`write` interface for communicating with Data Managers is a decision mainly driven by the scalability of the system. Offering a simple interface for this 2-layer structure enables different applications to have their addresses for storage of data as well as assets. It is up to each implementation to manage access to this Data Point storage space. This enables a wide array of complex, dynamic, and interactive use cases to be implemented with multiple ERCs as well as other smart contracts. Data Objects offer flexibility in storing mutable on-chain data that can be modified as per the requirements of each specific use case. This enables the Data Managers to hold mutable states in delegated storage and reflect changes over time, providing a dynamic layer to the otherwise static nature of storage through most other standardized interfaces. @@ -278,11 +281,16 @@ See Reference Implementation. ## Reference Implementation -We present an example implementation of Asset Vaults, a deterministic Asset Vault Factory, and a Data Object specialized in managing Asset Vaults. The following implementation can be used for wrapping multiple assets under the management of a Data Object, which in turn may be exposed through any Data Manager interface. +We present an example implementation of Asset Vaults, a deterministic Asset Vault Factory, and a Data Object specialized in managing Asset Vaults. The following implementation assumes the existance of both IDataPointRegistry and IODC implementations. + +The Vault Data Object may hold multiple assets locked under management, which in turn may be exposed through a Data Manager interface implementing a logic equivalent to the fractionalization of the Vault. **Example Vault Interface** +Asset Vaults are an example smart contract designed to implement the minimimalistic approach at asset management. Any user can deploy Asset Vaults through the factory. The role of an Asset Vault smart contract is to manage assets on behalf of the owner. We recommend Asset Vaults be `Ownable2Step` for security reasons. + + ```solidity pragma solidity ^0.8.0; interface IVault { @@ -301,9 +309,12 @@ interface IVault { * @param data Data sent to the target, including function selector */ function executeStatic(address target, bytes calldata data) external view returns (bytes memory); + ... } ``` +Vault Factories are example smart contracts that facilitate the deployment of Asset Vaults. + **Example Vault Factory Interface** ```solidity pragma solidity ^0.8.0; @@ -314,9 +325,12 @@ interface IVaultFactory { function deploy(bytes calldata data) external returns (IVault); function deployDeterministic(bytes32 salt, bytes calldata data) external returns (IVault); function computeVaultAddress(address deployer, bytes32 salt, bytes calldata data) external view returns (address); + ... } ``` +A Vault Data Object is an implementation of Base Data Object for managing assets through Asset Vaults. As an implementation of IDataObject, this smart contract must implement `read()` and `write()` functions related to handling the data stored on Data Points under management. The logic of `read()` can be implemented with the use of `dispatchRead()` and the `write()` logic with `dispatchWrite()`. In this example, the Vault Data Object also implements some signature verification logic. + **Example Vault Data Object contract** ```solidity // SPDX-License-Identifier: MIT @@ -354,6 +368,7 @@ contract VaultDataObject is IERC1271, BaseDataObject { EnumerableSet.AddressSet deployedVaults; mapping(bytes32 salt => address vault) deployedDeterministicVaults; mapping(bytes32 hash => SignatureVerificationData) validSignatures; + ... } mapping(uint256 dpIdx => DpData) private dpDataStorage; @@ -409,9 +424,13 @@ contract VaultDataObject is IERC1271, BaseDataObject { revert UnknownWriteOperation(operation); } } + ... } ``` +Finally, we expose a user-facing example Data Manager contract for managing the Data Object. The Data Manager will be implementing `read()` functions for checking the users' `balanceOf()` directly from the Data Object, and `write()` functions through the IODC implementation for the `transfer()` logic. Since multiple Data Managers can make use of the same Data Point, it is possible for several concurrent Data Manager implementations to share a Data Point where they delegate their storage. + + **Example Fungible Token Data Manager implementation** ```solidity @@ -468,7 +487,13 @@ contract ERC20DataManager is IERC20, IERC20Metadata, IERC20Errors, Ownable, ERC2 ## Security Considerations -No specific security considerations are derived from this ERC. +The access control is separated in three layers: + +* **Layer 1**: The Data Point Registry allocates for Data Managers and manages ownerhsip (admin/write rights) of Data Points. +* **Layer 2**: The ODC smart contract implements Access Control by managing Approvals of Data Managers to Data Points. It uses the Data Point Registry to verify who can grant/revoke this access. +* **Layer 3**: The Data Manager exposes functions that can perform `write` operations on the Data Point by calling the ODC implementation. + +No further security considerations are derived specificly from this ERC. ## Copyright From 1e318ea2e5f673a36d5ea9e818c2672c97a0f89f Mon Sep 17 00:00:00 2001 From: Christian Fries Date: Tue, 9 Jul 2024 15:41:54 +0200 Subject: [PATCH 061/126] Website: Feature/eip 6123 added cancelation Merged by EIP-Bot. --- ERCS/erc-6123.md | 38 +- assets/erc-6123/contracts/ISDC.sol | 51 +- assets/erc-6123/contracts/SDC.sol | 47 +- .../erc-6123/contracts/SDCPledgedBalance.sol | 3 +- assets/erc-6123/doc/sdc_trade_states.png | Bin 16204 -> 0 bytes assets/erc-6123/doc/sdc_trade_states.svg | 538 ++++++++++++++++++ assets/erc-6123/doc/sequence.puml | 3 - assets/erc-6123/doc/sequence.svg | 2 +- assets/erc-6123/test/SDCTests.js | 78 ++- 9 files changed, 726 insertions(+), 34 deletions(-) delete mode 100644 assets/erc-6123/doc/sdc_trade_states.png create mode 100644 assets/erc-6123/doc/sdc_trade_states.svg diff --git a/ERCS/erc-6123.md b/ERCS/erc-6123.md index 03434e73f2..6625fe55fa 100644 --- a/ERCS/erc-6123.md +++ b/ERCS/erc-6123.md @@ -76,6 +76,14 @@ A counterparty can confirm a trade by providing its trade specification data, wh function confirmTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; ``` +#### Trade Initiation Phase: `cancelTrade` + +The counterparty that called `inceptTrade` has the option to cancel the trade, e.g., in the case where the trade is not confirmed in a timely manner. + +```solidity +function cancelTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; +``` + #### Trade Settlement Phase: `initiateSettlement` Allows eligible participants (such as counterparties or a delegated agent) to trigger a settlement phase. @@ -120,6 +128,14 @@ Allows an eligible party to confirm a previously requested (mutual) trade termin function confirmTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; ``` +#### Trade Termination: `cancelTradeTermination` + +The party that initiated `requestTradeTermination` has the option to withdraw the request, e.g., in the case where the termination is not confirmed in a timely manner. + +```solidity +function cancelTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; +``` + ### Trade Events The following events are emitted during an SDC Trade life-cycle. @@ -140,6 +156,14 @@ Emitted on trade confirmation - method 'confirmTrade' event TradeConfirmed(address confirmer, string tradeId); ``` +#### TradeCanceled + +Emitted on trade cancellation - method 'cancelTrade' + +```solidity +event TradeCanceled(address confirmer, string tradeId); +``` + #### TradeActivated Emitted when a Trade is activated @@ -153,10 +177,9 @@ event TradeActivated(string tradeId); Emitted when a settlement is requested. May trigger the settlement phase. ```solidity -event TradeSettlementRequest(string tradeData, string lastSettlementData); +event TradeSettlementRequest(address initiator, string tradeData, string lastSettlementData); ``` - ### TradeSettlementPhase Emitted when the settlement phase is started. @@ -165,7 +188,6 @@ Emitted when the settlement phase is started. event TradeSettlementPhase(); ``` - #### TradeTerminationRequest Emitted when termination request is initiated by a counterparty @@ -182,6 +204,14 @@ Emitted when termination request is confirmed by a counterparty event TradeTerminationConfirmed(address cpAddress, string tradeId, string terminationTerms); ``` +#### TradeTerminationCanceled + +Emitted when termination request is canceled by the requesting counterparty + +```solidity +event TradeTerminationCanceled(address cpAddress, string tradeId, string terminationTerms); +``` + #### TradeTerminated Emitted when trade is terminated @@ -211,7 +241,7 @@ The interface design and reference implementation are based on the following con ### State diagram of trade and process states -![image info](../assets/eip-6123/doc/sdc_trade_states.png) +![image info](../assets/eip-6123/doc/sdc_trade_states.svg) ### Sequence diagram of reference implementation 'SDCPledgedBalance.sol' ![image info](../assets/eip-6123/doc/sequence.svg) diff --git a/assets/erc-6123/contracts/ISDC.sol b/assets/erc-6123/contracts/ISDC.sol index ce1af245c1..88804d1fc4 100644 --- a/assets/erc-6123/contracts/ISDC.sol +++ b/assets/erc-6123/contracts/ISDC.sol @@ -78,6 +78,13 @@ interface ISDC { */ event TradeConfirmed(address confirmer, string tradeId); + /** + * @dev Emitted when an incepted trade is canceled by the incepting counterparty + * @param initiator is the address from which trade was canceled + * @param tradeId the trade identifier + */ + event TradeCanceled(address initiator, string tradeId); + /** * @dev Emitted when a confirmed trade is set to active - e.g. when termination fee amounts are provided * @param tradeId the trade identifier of the activated trade @@ -102,24 +109,35 @@ interface ISDC { /** * @dev Emitted when a settlement gets requested + * @param initiator the address of the requesting party * @param tradeData holding the stored trade data * @param lastSettlementData holding the settlementdata from previous settlement (next settlement will be the increment of next valuation compared to former valuation) */ - event TradeSettlementRequest(string tradeData, string lastSettlementData); + event TradeSettlementRequest(address initiator, string tradeData, string lastSettlementData); /** * @dev Emitted when a counterparty proactively requests an early termination of the underlying trade * @param cpAddress the address of the requesting party * @param tradeId the trade identifier which is supposed to be terminated + * @param terminationTerms termination terms */ - event TradeTerminationRequest(address cpAddress, string tradeId, string terminationTerms); + event TradeTerminationRequest(address cpAddress, string tradeId, int256 terminationPayment, string terminationTerms); /** * @dev Emitted when early termination request is confirmed by the opposite party * @param cpAddress the party which confirms the trade termination * @param tradeId the trade identifier which is supposed to be terminated + * @param terminationTerms termination terms + */ + event TradeTerminationConfirmed(address cpAddress, string tradeId, int256 terminationPayment, string terminationTerms); + + /** + * @dev Emitted when a counterparty cancels its requests an early termination of the underlying trade + * @param cpAddress the address of the requesting party + * @param tradeId the trade identifier which is supposed to be terminated + * @param terminationTerms termination terms */ - event TradeTerminationConfirmed(address cpAddress, string tradeId, string terminationTerms); + event TradeTerminationCanceled(address cpAddress, string tradeId, string terminationTerms); /** * @dev Emitted when trade processing is halted @@ -137,7 +155,7 @@ interface ISDC { * @param _withParty is the party the inceptor wants to trade with * @param _tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml * @param _position is the position the inceptor has in that trade - * @param _paymentAmount is the paymentamount which can be positive or negative + * @param _paymentAmount is the payment amount which can be positive or negative (viewed from the inceptor) * @param _initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) */ function inceptTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; @@ -147,12 +165,22 @@ interface ISDC { * @dev emits a {TradeConfirmed} event if trade data match * @param _withParty is the party the confirmer wants to trade with * @param _tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml - * @param _position is the position the inceptor has in that trade - * @param _paymentAmount is the paymentamount which can be positive or negative + * @param _position is the position the confirmer has in that trade (negative of the position the inceptor has in the trade) + * @param _paymentAmount is the payment amount which can be positive or negative (viewed from the confirmer, negative of the inceptor's view) * @param _initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) */ function confirmTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; + /** + * @notice Performs a matching of provided trade data and settlement data of a previous trade inception. Required to be called by inceptor. + * @dev emits a {TradeCanceled} event if trade data match and msg.sender agrees with the party that incepted the trade. + * @param _withParty is the party the inceptor wants to trade with + * @param _tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param _position is the position the inceptor has in that trade + * @param _paymentAmount is the payment amount which can be positive or negative (viewed from the inceptor) + * @param _initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) + */ + function cancelTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; /// Settlement Cycle: Settlement @@ -185,6 +213,7 @@ interface ISDC { * @notice Called from a counterparty to request a mutual termination * @dev emits a {TradeTerminationRequest} * @param tradeId the trade identifier which is supposed to be terminated + * @param terminationTerms the termination terms */ function requestTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; @@ -192,6 +221,16 @@ interface ISDC { * @notice Called from a party to confirm an incepted termination, which might trigger a final settlement before trade gets closed * @dev emits a {TradeTerminationConfirmed} * @param tradeId the trade identifier of the trade which is supposed to be terminated + * @param terminationTerms the termination terms */ function confirmTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; + + /** + * @notice Called from a party to confirm an incepted termination, which might trigger a final settlement before trade gets closed + * @dev emits a {TradeTerminationConfirmed} + * @param tradeId the trade identifier of the trade which is supposed to be terminated + * @param terminationTerms the termination terms + */ + function cancelTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; + } diff --git a/assets/erc-6123/contracts/SDC.sol b/assets/erc-6123/contracts/SDC.sol index 12a1e2a9d6..7d61ef9a41 100644 --- a/assets/erc-6123/contracts/SDC.sol +++ b/assets/erc-6123/contracts/SDC.sol @@ -123,10 +123,10 @@ abstract contract SDC is ISDC { tradeState = TradeState.Inactive; } /* - * generates a hash from tradeData and generates a map entry in openRequests - * emits a TradeIncepted - * can be called only when TradeState = Incepted - */ + * generates a hash from tradeData and generates a map entry in openRequests + * emits a TradeIncepted + * can be called only when TradeState = Incepted + */ function inceptTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external override onlyCounterparty onlyWhenTradeInactive { require(msg.sender != _withParty, "Calling party cannot be the same as withParty"); require(_position == 1 || _position == -1, "Position can only be +1 or -1"); @@ -166,6 +166,20 @@ abstract contract SDC is ISDC { processTradeAfterConfirmation(upfrontPayer, absPaymentAmount); } + /* + * generates a hash from tradeData and checks whether an open request can be found by the opposite party + * if so, the open request is deleted, can only be called by incepting party. + * emits a TradeConfirmed + * can be called only when TradeState = Incepted + */ + function cancelTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external override onlyCounterparty onlyWhenTradeIncepted { + address inceptingParty = msg.sender; + uint256 transactionHash = uint256(keccak256(abi.encode(msg.sender,_withParty,_tradeData,_position,_paymentAmount,_initialSettlementData))); + require(pendingRequests[transactionHash] == inceptingParty, "Cancellation fails due to inconsistent trade data or wrong party address"); + delete pendingRequests[transactionHash]; // Delete Pending Request + tradeState = TradeState.Inactive; + emit TradeCanceled(msg.sender, tradeID); + } /* * Can be called by a party for mutual termination @@ -175,30 +189,43 @@ abstract contract SDC is ISDC { */ function requestTradeTermination(string memory _tradeId, int256 _terminationPayment, string memory terminationTerms) external override onlyCounterparty onlyWhenSettled { require(keccak256(abi.encodePacked(tradeID)) == keccak256(abi.encodePacked(_tradeId)), "Trade ID mismatch"); - uint256 hash = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment,terminationTerms))); + uint256 hash = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment, terminationTerms))); pendingRequests[hash] = msg.sender; - emit TradeTerminationRequest(msg.sender, _tradeId, terminationTerms); + + emit TradeTerminationRequest(msg.sender, _tradeId, _terminationPayment, terminationTerms); } /* - * Same pattern as for initiation * confirming party generates same hash, looks into pendingRequests, if entry is found with correct address, tradeState is put to terminated * can be called only when ProcessState = Funded and TradeState = Active */ function confirmTradeTermination(string memory _tradeId, int256 _terminationPayment, string memory terminationTerms) external override onlyCounterparty onlyWhenSettled { address pendingRequestParty = msg.sender == party1 ? party2 : party1; - uint256 hashConfirm = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment,terminationTerms))); + uint256 hashConfirm = uint256(keccak256(abi.encode(_tradeId, "terminate", -_terminationPayment, terminationTerms))); require(pendingRequests[hashConfirm] == pendingRequestParty, "Confirmation of termination failed due to wrong party or missing request"); delete pendingRequests[hashConfirm]; mutuallyTerminated = true; terminationPayment = _terminationPayment; - emit TradeTerminationConfirmed(msg.sender, _tradeId, terminationTerms); + emit TradeTerminationConfirmed(msg.sender, _tradeId, -_terminationPayment, terminationTerms); /* Trigger final Settlement */ + address initiator = msg.sender; tradeState = TradeState.Valuation; - emit TradeSettlementRequest(tradeData, settlementData[settlementData.length - 1]); + emit TradeSettlementRequest(initiator, tradeData, settlementData[settlementData.length - 1]); } + /* + * Same pattern as for initiation + * confirming party generates same hash, looks into pendingRequests, if entry is found with correct address, tradeState is put to terminated + * can be called only when ProcessState = Funded and TradeState = Active + */ + function cancelTradeTermination(string memory _tradeId, int256 _terminationPayment, string memory terminationTerms) external override onlyCounterparty onlyWhenSettled { + address pendingRequestParty = msg.sender; + uint256 hashConfirm = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment,terminationTerms))); + require(pendingRequests[hashConfirm] == pendingRequestParty, "Cancellation of termination failed due to wrong party or missing request"); + delete pendingRequests[hashConfirm]; + emit TradeTerminationCanceled(msg.sender, _tradeId, terminationTerms); + } function processTradeAfterConfirmation(address upfrontPayer, uint256 upfrontPayment) virtual internal; diff --git a/assets/erc-6123/contracts/SDCPledgedBalance.sol b/assets/erc-6123/contracts/SDCPledgedBalance.sol index c9ac545a2f..17bd7edfe0 100644 --- a/assets/erc-6123/contracts/SDCPledgedBalance.sol +++ b/assets/erc-6123/contracts/SDCPledgedBalance.sol @@ -87,8 +87,9 @@ contract SDCPledgedBalance is SDC { * can be called only when ProcessState = Rebalanced and TradeState = Active */ function initiateSettlement() external override onlyCounterparty onlyWhenSettled { + address initiator = msg.sender; tradeState = TradeState.Valuation; - emit TradeSettlementRequest(tradeData, settlementData[settlementData.length - 1]); + emit TradeSettlementRequest(initiator, tradeData, settlementData[settlementData.length - 1]); } /* diff --git a/assets/erc-6123/doc/sdc_trade_states.png b/assets/erc-6123/doc/sdc_trade_states.png deleted file mode 100644 index 53b896b871973f28b8e01779a97480e586cc700e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16204 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 z 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 diff --git a/assets/erc-6123/doc/sdc_trade_states.svg b/assets/erc-6123/doc/sdc_trade_states.svg new file mode 100644 index 0000000000..fb99c78fc4 --- /dev/null +++ b/assets/erc-6123/doc/sdc_trade_states.svg @@ -0,0 +1,538 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Zeichenblatt-1 + + + + + Anfangszustand + Start + + + + + + + Start + + + + + + + + + + Zusammengesetzter Zustand.228 + + + + + + + + + + Sheet.229 + Incepted + + Sheet.230 + + + + + + + + + + + Incepted + + + + + + + + + + + + Zusammengesetzter Zustand.254 + + + + + + + + + + Sheet.255 + Confirmed + + Sheet.256 + + + + + + + + + + + Confirmed + + + + + + + + + + + + Zusammengesetzter Zustand.408 + + + + + + + + + + Sheet.409 + Terminated + + Sheet.410 + + + + + + + + + + + Terminated + + + + + + + + + + + + Zusammengesetzter Zustand.419 + + + + + + + + + + Sheet.420 + Cancelled + + Sheet.421 + + + + + + + + + + + Cancelled + + + + Dynamischer Verbinder.304 + tx: inceptTrade by Couterparty1 + + + + + tx: inceptTradeby Couterparty1 + + Dynamischer Verbinder.313 + tx: confirmTrade by Counterparty2 + + + + + tx: confirmTradeby Counterparty2 + + Dynamischer Verbinder.66 + tx: afterTransfer + + + + + tx: afterTransfer + + + + + Zustand.71 + + + + + Sheet.354 + Valuation + + + + + + + Valuation + + + Auswahl.292 + + + + + + + + + + Zustand.336 + + + + + Sheet.360 + Settled + + + + + + + Settled + + + + + + Zustand.380 + + + + + Sheet.381 + inTransfer + + + + + + + inTransfer + + + Dynamischer Verbinder.398 + success + + + + + success + + Dynamischer Verbinder.399 + tx: initiateSettlement + + + + + tx: initiateSettlement + + Dynamischer Verbinder.401 + Initiate Transfer of Margin Buffers to SDC Balance + + + + + Initiate Transfer of Margin Buffers to SDC Balance + + Dynamischer Verbinder + tx: perfomSettlement Initiate Transfer of Settlement Amount + + + + + tx: perfomSettlementInitiate Transfer ofSettlement Amount + + Dynamischer Verbinder.411 + fail + + + + + fail + + Dynamischer Verbinder.412 + Transfer Open Settlement Amount from SDC Balance + + + + + Transfer Open Settlement Amount from SDC Balance + + Sheet.413 + + + + + + Endzustand + End + + + + + + + + + End + + Auswahl.415 + + + + + + + Dynamischer Verbinder.417 + + + + Dynamischer Verbinder.418 + Mutuall terminated or Maturity + + + + + Mutuall terminated or Maturity + + Dynamischer Verbinder.422 + OPT: cancelInception + + + + + OPT: cancelInception + + Dynamischer Verbinder.430 + fail + + + + + + fail + + diff --git a/assets/erc-6123/doc/sequence.puml b/assets/erc-6123/doc/sequence.puml index d8a6521cad..5ff19b5c7a 100644 --- a/assets/erc-6123/doc/sequence.puml +++ b/assets/erc-6123/doc/sequence.puml @@ -32,9 +32,6 @@ CP2 ->SettlementToken: tx set 'allowance' for SDC address CP1 ->SDC: tx 'inceptTrade' SDC-->EventHandler: emit TradeIncepted -EventHandler->ValuationService: request initial valuation data -ValuationService->EventHandler: return initial valuation data -EventHandler->SDC: tx 'performSettlement' to store initial valuation data == TradeState 'Incepted' == CP2->SDC: tx 'confirmTrade' SDC->SDC: validate tradedata diff --git a/assets/erc-6123/doc/sequence.svg b/assets/erc-6123/doc/sequence.svg index c8ac1a056e..f7e877a984 100644 --- a/assets/erc-6123/doc/sequence.svg +++ b/assets/erc-6123/doc/sequence.svg @@ -1 +1 @@ -SmartDerivativeContract with Settlement-Token and off-chain Valuation ServiceCP1CP1CP2CP2SDCSDCSettlementTokenSettlementTokenEventHandlerEventHandlerValuationServiceValuationServiceInitialize Tradeallocate balancesallocate balancestx 'deploy' a SDC with token addresstx set 'allowance' for SDC addresstx set 'allowance' for SDC addresstx 'inceptTrade'emit TradeInceptedrequest initial valuation datareturn initial valuation datatx 'performSettlement' to store initial valuation dataTradeState 'Incepted'tx 'confirmTrade'validate tradedataemit TradeConfirmedTradeState 'Confirmed'tx 'transferFrom' margin buffers and termination feesto SDC address for CP1 and CP2tx 'transferFrom' optional Upfront Fee from Paying to Receiving PartyTradeState 'inTransfer'callback tx 'afterSettlement'ProcessState 'Settled'loop[Every Settlement]tx: 'initiateSettlement'TradeState 'Valuation'emit ProcessSettlementRequestrequest valuation datareturn valuation datacallback: tx 'performSettlement'Caps Settlement Amount at Margin Buffer Leveltx 'transferFrom' settlement amount from Paying Party to Receiving Party BalanceTradeState 'inTransfer'emit AwaitingTransferalt[Transfer Check]callback tx 'afterSettlement'[success]TradeState 'Settled'[fail]tx 'transfer' Settlement Amount from SDC Balance to Receiving Partytx 'transfer' Termination Fee from SDC Balance to Receiving Partytx 'approve' - Unlock remaing Party BalancesTradeState 'Terminated' \ No newline at end of file +SmartDerivativeContract with Settlement-Token and off-chain Valuation ServiceCP1CP1CP2CP2SDCSDCSettlementTokenSettlementTokenEventHandlerEventHandlerValuationServiceValuationServiceInitialize Tradeallocate balancesallocate balancestx 'deploy' a SDC with token addresstx set 'allowance' for SDC addresstx set 'allowance' for SDC addresstx 'inceptTrade'emit TradeInceptedTradeState 'Incepted'tx 'confirmTrade'validate tradedataemit TradeConfirmedTradeState 'Confirmed'tx 'transferFrom' margin buffers and termination feesto SDC address for CP1 and CP2tx 'transferFrom' optional Upfront Fee from Paying to Receiving PartyTradeState 'inTransfer'callback tx 'afterSettlement'ProcessState 'Settled'loop[Every Settlement]tx: 'initiateSettlement'TradeState 'Valuation'emit ProcessSettlementRequestrequest valuation datareturn valuation datacallback: tx 'performSettlement'Caps Settlement Amount at Margin Buffer Leveltx 'transferFrom' settlement amount from Paying Party to Receiving Party BalanceTradeState 'inTransfer'emit AwaitingTransferalt[Transfer Check]callback tx 'afterSettlement'[success]TradeState 'Settled'[fail]tx 'transfer' Settlement Amount from SDC Balance to Receiving Partytx 'transfer' Termination Fee from SDC Balance to Receiving Partytx 'approve' - Unlock remaing Party BalancesTradeState 'Terminated' \ No newline at end of file diff --git a/assets/erc-6123/test/SDCTests.js b/assets/erc-6123/test/SDCTests.js index 20fe9ed3e1..b21ff704c4 100644 --- a/assets/erc-6123/test/SDCTests.js +++ b/assets/erc-6123/test/SDCTests.js @@ -30,6 +30,7 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { const settlementAmount1 = 200; // successful settlement in favour to CP1 const settlementAmount2 = -1400; // failing settlement larger than buffer in favour to CP1 const upfront = 10; + const terminationPayment = 100; let SDCFactory; let ERC20Factory; @@ -52,7 +53,7 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { it("Counterparties incept and confirm a trade successfully, upfront is transferred", async () => { let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); await sdc.deployed(); - console.log("SDC Address: %s", sdc.address); +// console.log("SDC Address: %s", sdc.address); await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount+upfront); let trade_id =""; @@ -64,10 +65,25 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { await expect(trade_state).equal(TradeState.Settled); }); + it("Counterparty incepts and cancels trade successfully", async () => { + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + await sdc.deployed(); +// console.log("SDC Address: %s", sdc.address); + await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); + await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount+upfront); + let trade_id =""; + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + await expect(incept_call).to.emit(sdc, "TradeIncepted"); + const confirm_call = await sdc.connect(counterparty1).cancelTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + await expect(confirm_call).to.emit(sdc, "TradeCanceled"); + let trade_state = await sdc.connect(counterparty1).getTradeState(); + await expect(trade_state).equal(TradeState.Inactive); + }); + it("Not enough approval to transfer upfront payment", async () => { let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); await sdc.deployed(); - console.log("SDC Address: %s", sdc.address); +// console.log("SDC Address: %s", sdc.address); await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount); const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); @@ -81,7 +97,7 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { it("Trade Matching fails", async () => { let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); await sdc.deployed(); - console.log("SDC Address: %s", sdc.address); +// console.log("SDC Address: %s", sdc.address); await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount); const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); @@ -90,11 +106,59 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { await expect(confirm_call).to.be.revertedWith("Confirmation fails due to inconsistent trade data or wrong party address"); }); + it("Trade cancellation fails due to wrong party calling cancel", async () => { + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + await sdc.deployed(); +// console.log("SDC Address: %s", sdc.address); + await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); + await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + await expect(incept_call).to.emit(sdc, "TradeIncepted"); + const confirm_call = sdc.connect(counterparty2).cancelTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + await expect(confirm_call).to.be.revertedWith("Cancellation fails due to inconsistent trade data or wrong party address"); + }); + + it("Trade cancellation fails due to wrong arguments", async () => { + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + await sdc.deployed(); +// console.log("SDC Address: %s", sdc.address); + await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); + await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + await expect(incept_call).to.emit(sdc, "TradeIncepted"); + const confirm_call = sdc.connect(counterparty1).cancelTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData23"); + await expect(confirm_call).to.be.revertedWith("Cancellation fails due to inconsistent trade data or wrong party address"); + }); + + it("Counterparties incept and confirm a trade successfully, upfront is transferred, trade is terminated", async () => { + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + await sdc.deployed(); +// console.log("SDC Address: %s", sdc.address); + await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); + await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount+upfront); + + // Incept trade (and fetch tradeId) + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + const receipt = await incept_call.wait(); + const event = receipt.events.find(event => event.event === 'TradeIncepted'); + const trade_id = event.args[1]; + + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, -upfront, "initialMarketData"); + await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); + + const terminate_call = await sdc.connect(counterparty1).requestTradeTermination(trade_id, terminationPayment, "terminationTerms"); + await expect(terminate_call).to.emit(sdc, "TradeTerminationRequest"); + + const confirm_terminate_call = await sdc.connect(counterparty2).confirmTradeTermination(trade_id, -terminationPayment, "terminationTerms"); + await expect(confirm_terminate_call).to.emit(sdc, "TradeTerminationConfirmed"); + let trade_state = await sdc.connect(counterparty1).getTradeState(); + await expect(trade_state).equal(TradeState.Valuation); + }); it("Successful Settlement", async () => { let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); await sdc.deployed(); - console.log("SDC Address: %s", sdc.address); +// console.log("SDC Address: %s", sdc.address); await token.connect(counterparty1).approve(sdc.address,terminationFee+10*marginBufferAmount); //Approve for 10*margin amount await token.connect(counterparty2).approve(sdc.address,terminationFee+10*marginBufferAmount+upfront); let trade_id =""; @@ -105,14 +169,10 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { const initSettlementPhase = sdc.connect(counterparty2).initiateSettlement(); await expect(initSettlementPhase).to.emit(sdc, "TradeSettlementRequest"); const balance_call = await token.connect(counterparty2).balanceOf(counterparty2.address); - console.log("Balance: %s", balance_call); +// console.log("Balance: %s", balance_call); const performSettlementCall = sdc.connect(counterparty1).performSettlement(1,"settlementData"); await expect(performSettlementCall).to.emit(sdc, "TradeSettlementPhase"); let trade_state = await sdc.connect(counterparty1).getTradeState(); await expect(trade_state).equal(TradeState.Settled); - }); - - - }); \ No newline at end of file From f25b023a0a7b59b3be4268222895449644631a9a Mon Sep 17 00:00:00 2001 From: Vidor <1101164+V1d0r@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:19:16 +0100 Subject: [PATCH 062/126] Update ERC-7578: Move to Review Merged by EIP-Bot. --- ERCS/erc-7578.md | 70 +++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 46 deletions(-) diff --git a/ERCS/erc-7578.md b/ERCS/erc-7578.md index 26489b91e1..8adbefe602 100644 --- a/ERCS/erc-7578.md +++ b/ERCS/erc-7578.md @@ -4,7 +4,7 @@ title: Physical Asset Redemption description: Provides the holder of physical asset backed NFTs readily available real-world information on the underlying physical asset. author: Lee Vidor (@V1d0r) , David Tan , Lee Smith , Gabriel Stoica (@gabrielstoica) discussions-to: https://ethereum-magicians.org/t/erc-7578-physical-asset-redemption/17556 -status: Draft +status: Review type: Standards Track category: ERC created: 2023-08-01 @@ -21,37 +21,33 @@ The first wave of NFT use cases encompass predominately the representation of ow Addressing the lack of readily available information and paving the way for mass adoption for a tokenized economy, this proposal requires that each minted token includes a defined number of predefined variables enabling verification of authenticity and facilitating redemption of the underlying physical asset. -- `[TOKEN ISSUER]` **Identification of individual or entity minting the NFT (Issuer)**: [NAME], [UNIQUE IDENTIFICATION NUMBER] OR [NETWORK IDENTIFICATION] +## Specification + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. + +When a token is minted, its properties SHOULD be initialized beforehand, with each field being defined as follows: + +- **Token issuer**: identification of an individual or entity minting the NFT
_The token issuer is the key person connecting the physical asset and the digital representation. By identifying and disclosing the token issuer, a reference point is made instantly available to the NFT holder which allows them to conduct a due diligence on the NFT issuer and assessment of the NFT issuerโ€™s trustworthiness. At the same time, it creates accountability for the token issuer which leads to overall improvement and standardisation of the NFT minting process. Token issuers will compete for best practices and recognition to gain advantages over competitors. A reputable NFT issuer will e.g. keep information on the legal owner of the physical asset prior to the minting of the underlying physical asset to satisfy any AML and KYC concerns. Ideally the NFT issuer is identified by a name but may also be identifiable via unique identification number or network ID that is issued by a service provider who stores relevant information on the NFT issuer._ -- `[LEGAL OWNER OF UNDERLYING PHYSICAL ASSET]` **Identification of legal owner of underlying physical asset:** [NAME], [UNIQUE IDENTIFICATION NUMBER] OR [NETWORK ID] +- **Asset holder**: identification of legal owner of underlying physical asset
_In view of a redemption of the underlying physical asset and enforcing of legal rights, it is (from a legal perspective) essential for the NFT holder to identify the legal owner of the underlying physical asset. It allows the NFT holder to consider the legal counterparty risk. It cannot be assumed that the NFT issuer is the legal owner of the underlying physical asset, therefore it is vital for the NFT holder to have instant access to this additional information. Same as with the NFT issuerโ€™s identity, the legal owner is ideally identified by a name but may also be identifiable via unique identification number or network ID that is issued by a service provider who stores relevant information on the legal owner._ -- `[STORAGE LOCATION]` **Identification of storage location of underlying physical asset:** [PHYSICAL ADDRESS OR JURISDICTION] +- **Storage location**: identification of storage location of underlying physical asset
_Certain physical assets require specific storage conditions. A digital representation of an inappropriately stored physical asset may impact the value of the NFT significantly. Disclosing the storage location and making it directly accessible to the NFT holder, allows them to evaluate the storage risk of the underlying physical asset. In addition, it provides the NFT holder with a second point of contact for the enforcement of the redemption of the underlying physical asset._ -- `[LEGAL CONTRACT]` **Type of legal relationship:** [WITH LINK TO DOCUMENT IPSF] +- **Terms**: identification of legal relationship
_The disclosure and accessibility of the legal basis of the relationship between NFT holder and legal owner of the underlying physical asset promotes token issuers to stipulate and define the legal rights of the involved key parties. It furthermore allows the NFT Holder to conduct a legal risk and enforcement assessment. Ideally, the information is provided by embedding a link to the actual legal documentation such as an agreement or terms. The more information is accessible via the NFT, the better the NFT holder can assess the legal risks associated with enforcement of the redemption of the underlying physical asset._ -- `[APPLICABLE LAW]` **Governing Law and Jurisdiction:** [JURISDICTION] +- **Jurisdiction**: governing law and jurisdiction
_The applicable law is an extension of the legal contract disclosure and makes instantly available to the NFT holder or smart contract under what jurisdiction an enforcement would be governed without the need to review the details legal contract. This allows for an instant assessment of jurisdiction risk._ -- `[DECLARED VALUE]` **Value of the underlying asset:** [VALUE] +- **Declared value**: value of the underlying asset
_Certain auxiliary services such as insurance are tied to a value of the NFT and underlying physical asset. By defining a declared value, NFTs are able to be categorised in certain ways while the declared value provides an indication regarding the underlying assetโ€™s value. The declared value of the underlying physical asset does not necessarily represent the market value._ -## Specification - -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. - -`Properties` MUST be set before a token can be minted. - -The `tokenId` MUST be greater than 0. - -The `terms` parameter MUST be a link to a document that is stored on IPFS. - -When a token has no `properties` set mint reverts. +The `terms` parameter SHOULD be an HTTP link to a document that is stored on IPFS. This is to ensure that the document is immutable and can be verified by the NFT holder. -When a token has a valid properties, if the token burns, the properties MUST be deleted. +When a token with valid properties is to be burned, the properties MUST be removed. ### Contract Interface @@ -62,7 +58,7 @@ pragma solidity ^0.8.21; * @notice Struct encapsulating fields required to by the ERC-7578 standard to represent the physical asset * @param tokenIssuer The network or entity minting the token * @param assetHolder The legal owner of the physical asset - * @param storedLocation The physical storage location + * @param storageLocation The physical location where the asset is stored * @param terms Link to IPFS contract, agreement or terms * @param jurisdiction The legal justification set out in the terms * @param declaredValue The declared value at time of token minting @@ -70,7 +66,7 @@ pragma solidity ^0.8.21; struct Properties { string tokenIssuer; string assetHolder; - string storedLocation; + string storageLocation; string terms; string jurisdiction; Amount declaredValue; @@ -92,7 +88,6 @@ struct Amount { interface IERC7578 { /** * @notice Emitted when the properties of the `tokenId` token are set - * * @param tokenId The ID of the token * @param properties The properties of the token */ @@ -100,47 +95,30 @@ interface IERC7578 { /** * @notice Emitted when the properties of the `tokenId` token are removed - * * @param tokenId The ID of the token */ event PropertiesRemoved(uint256 indexed tokenId); /** - * @notice Retrieves all the properties of the `tokenId` token + * @notice Retrieves all properties of the `tokenId` token * @dev Does NOT revert if token doesn't exist * @param tokenId The token ID of the minted token */ function getProperties(uint256 tokenId) external view returns (Properties memory properties); - - /** - * @notice Sets the properties of the `tokenId` token - * - * IMPORTANT: Properties required to be set when minting a token - * - * @param tokenId The ID of the token - * @param properties The properties of the token - */ - function setProperties(uint256 tokenId, Properties calldata properties) external; } ``` -The `setProperties(uint256 tokenId, Properties calldata properties)` function is called before minting a token. - -The `getProperties(uint256 tokenId)` function MUST return the unique `properties` for a token. - When `properties` are set, the `PropertiesSet(uint256 indexed tokenId, Properties properties)` event is emitted. When `properties` are removed, the `PropertiesRemoved(uint256 indexed tokenId)` event is emitted. -## Rationale - -The `tokenId` must be greater than 0 so the `properties` can be checked before minting. +The `getProperties(uint256 tokenId)` function MUST return the unique `properties` of a token. If the ERC-721 token is burned or has no properties set, it SHOULD return an empty `Properties` struct. -The `_update` function is overridden to check if the `properties` are set before minting and removed after burning. +## Rationale -The `terms` parameter is a HTTP link to a document that is stored on IPFS. This is to ensure that the document is immutable and can be verified by the NFT holder. +By not initializing a token's properties before minting, one risks that the asset's provenance represented by the token cannot be established. -Contract level validation is not used on the properties as we believe the accuracy of the data declared is the responsibility of the token issuer. This builds trust in the token issuer and the token itself. +Contract level validation is not used on the properties as we believe the accuracy of the data declared is the responsibility of the token issuer. This builds trust on the token issuer and the token itself. ## Backwards Compatibility @@ -178,13 +156,13 @@ contract ERC7578 is ERC721, IERC7578 { constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {} /** - * @inheritdoc IERC7578 + * @notice Initializes the ERC-7578 properties of the `tokenId` token */ function setProperties(uint256 tokenId, Properties calldata properties) public { _properties[tokenId] = Properties({ tokenIssuer: properties.tokenIssuer, assetHolder: properties.assetHolder, - storedLocation: properties.storedLocation, + storageLocation: properties.storageLocation, terms: properties.terms, jurisdiction: properties.jurisdiction, declaredValue: Amount({ From c650a1dec344e8032c6e6686cb193838802b35b0 Mon Sep 17 00:00:00 2001 From: piyush-chittara Date: Tue, 9 Jul 2024 19:51:12 +0530 Subject: [PATCH 063/126] Add ERC: Lockable Extension for ERC-1155 Merged by EIP-Bot. --- ERCS/erc-7721.md | 158 +++++++++++++++++++++++++ assets/erc-7721/ERC7721.sol | 217 +++++++++++++++++++++++++++++++++++ assets/erc-7721/IERC7721.sol | 62 ++++++++++ 3 files changed, 437 insertions(+) create mode 100644 ERCS/erc-7721.md create mode 100644 assets/erc-7721/ERC7721.sol create mode 100644 assets/erc-7721/IERC7721.sol diff --git a/ERCS/erc-7721.md b/ERCS/erc-7721.md new file mode 100644 index 0000000000..29c2b3b649 --- /dev/null +++ b/ERCS/erc-7721.md @@ -0,0 +1,158 @@ +--- +eip: 7721 +title: Lockable Extension for ERC-1155 +description: Interface for enabling locking of ERC-1155 using locker and token id based approvals +author: Piyush Chittara (@piyush-chittara) +discussions-to: https://ethereum-magicians.org/t/erc-7721-lockable-extension-for-erc1155/20250 +status: Draft +type: Standards Track +category: ERC +created: 2023-05-25 +requires: 165, 1155 +--- + +## Abstract + +The Lockable Extension for [ERC-1155](./eip-1155.md) introduces a robust locking mechanism for specific Non-Fungible Tokens (NFTs) within the ERC-1155 token standard, allowing for various uses while preventing sale or transfer. The token's `owner` can `lock` it, setting up locker address (either an EOA or a contract) that exclusively holds the power to unlock the token. Owner can also provide approval for `tokenId`, enabling ability to lock asset while address holds the token approval. Token can also be locked by `approved`, assigning locker to itself. Upon token transfer, these rights get purged. + +Inspired by the need for enhanced security and control over tokenized assets, this extension enables token owners to lock individual NFTs with `tokenId`, ensuring that only approved users can withdraw predetermined amounts of locked tokens. Thus, offering a safer approach by allowing token owners to specify approved token IDs and amounts for withdrawal. + +## Motivation + +[ERC-1155](./eip-1155.md) has sparked an unprecedented surge in demand for NFTs. However, despite this tremendous success, the NFT economy suffers from secondary liquidity where it remains illiquid in ownerโ€™s wallet. There are projects which aim to address the liquidity challenge, but they entail the below mentioned inconveniences and risks for owners as they necessitate transferring the participating NFTs to the projects' contracts. + +- Loss of utility: The utility value of NFTs diminishes when they are transferred to an escrow account, no longer remaining under the direct custody of the owners. +- Lack of composability: The market could benefit from increased liquidity if NFT owners had access to multiple financial tools, such as leveraging loans and renting out their assets for maximum returns. Composability serves as the missing piece in creating a more efficient market. +- Smart contract vulnerabilities: NFTs are susceptible to loss or theft due to potential bugs or vulnerabilities present in the smart contracts they rely on. + +The aforementioned issues contribute to a poor user experience (UX), and we propose enhancing the [ERC-1155](./eip-1155.md) standard by implementing a native locking mechanism: +Rather than being transferred to a smart contract, an NFT remains securely stored in self-custody but is locked. +During the lock period, the NFT's transfer is restricted while its other properties remain unchanged. +NFT Owner retains the ability to use or distribute itโ€™s utility. + +NFTs have numerous use cases where the NFT must remain within the owner's wallet, even when it serves as collateral for a loan. Whether it's authorizing access to a Discord server, or utilizing NFT within a play-to-earn (P2E) game, owner should have the freedom to do so throughout the lending period. Just as real estate owner can continue living in their mortgaged house, take personal loan or keep tenants to generate passive income, these functionalities should be available to NFT owners to bring more investors in NFT economy. + + +Lockable NFTs enable the following use cases : + +- NFT-collateralized loans: Utilize NFT as collateral for a loan without locking it on the lending protocol contract. Instead, lock it within ownerโ€™s wallet while still enjoying all the utility of NFT. +- No collateral rentals of NFTs: Borrow an NFT for a fee without the need for significant collateral. Renter can use the NFT but not transfer it, ensuring the lender's safety. The borrowing service contract automatically returns the NFT to the lender once the borrowing period expires. +- Buy Now Pay Later (BNPL): The buyer receives the locked NFT and can immediately begin using it. However, they are unable to sell the NFT until all installments are paid. Failure to complete the full payment results in the NFT returning to the seller, along with a fee. +- Composability: Maximize liquidity by having access to multiple financial tools. Imagine taking a loan against NFT and putting it on rentals to generate passive income. +- Primary sales: Mint an NFT for a partial payment and settle the remaining amount once owner is satisfied with the collection's progress. +- Soulbound: Organization can mint and self-assign `locker`, send token to user and lock the asset. +- Safety: Safely and conveniently use exclusive blue chip NFTs. Lockable extension allows owner to lock NFT and designate secure cold wallet as the unlocker. This way, owner can keep NFT on MetaMask and easily use it, even if a hacker gains access to MetaMask account. Without access to the cold wallet, the hacker cannot transfer NFT, ensuring its safety. + +This proposal is different from other locking proposals in number of ways: + +- This implementation provides a minimal implementation of `lock` and `unlock` and believes other conditions like time-bound are great ideas but can be achieved without creating a specific implementation. Locking and Unlocking can be based on any conditions (e.g. repayment, expiry). Therefore time-bound unlocks a relatively specific use case that can be achieved via smart-contracts themselves without that being a part of the token contract. +- This implementation proposes a separation of rights between locker and approver. Token can be locked with approval and approved can unlock and withdraw tokens (opening up opportunities like renting, lending, BNPL etc), and token can be locked lacking the rights to revoke token, yet can unlock if required (opening up opportunities like account-bound NFTs). +- Our proposal implement ability to `transferAndLock` which can be used to transfer, lock and optionally approve token. Enabling the possibility of revocation after transfer. + +By extending the [ERC-1155](./eip-1155.md) standard, the proposed standard enables secure and convenient management of underlying NFT assets. It natively supports prevalent NFTFi use cases such as staking, lending, and renting. We anticipate that this proposed standard will foster increased engagement of NFT owners in NFTFi projects, thereby enhancing the overall vitality of the NFT ecosystem. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Overview + +[ERC-1155](./eip-1155.md) compliant contracts MAY implement this EIP to provide standard methods of locking and unlocking the token at its current owner address. + +Token owner MAY `lock` the token and assign `locker` to some `address` using `lock(uint256 tokenId, address account, address _locker, uint256 amount)` function, this MUST set `locker` to `_locker`. Token owner or approved MAY `lock` the token using `lock(uint256 tokenId, address account, uint256 amount` function, this MUST set `locker` to `msg.sender`. Token MAY be `unlocked` by `locker` using `unlock(uint256 tokenId, address account, uint256 amount)` function. + +Token owner MAY `approve` specific for specific `tokenId` using `setApprovalForId(uint256 tokenId, address operator, uint256 amount)` ensuring only approved tokenId could be spent by operator. `getApprovalForId(uint256 tokenId, address account, address operator)` SHALL return `amount` approved on `account` by `operator`. + +If the token is `locked`, the `getLocked(uint256 tokenId, address account, address operator)` function MUST return an amount that is `locked` by `operator` on `account`. For tokens that are not `locked`, the `getLocked(uint256 tokenId, address account, address operator)` function MUST return `0`. + +`lock` function MUST revert if `account` has insufficient balance or not `owner` or `approved` of `tokenId`. `unlock` function MUST revert if provided `amount` of `tokenId` is not `locked`. ERC-1155 `safeTransferFrom` of a token MUST revert if `account` transfer `locked` amount, maximum transferable amount MUST be `balance - getLocked`. + +Token MAY be transferred and `locked`, also assign `approval` to `locker` using `transferAndLock` function. This is RECOMMENDED for use-cases where Token transfer and subsequent revocation is REQUIRED. + +### Interface + +``` +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity >=0.7.0 <0.9.0; + +/// @title Lockable Extension for ERC1155 +/// @dev Interface for the Lockable extension +/// @author piyush-chittara + +interface IERCLockable1155 is IERC1155{ + + /** + * @dev Emitted when tokenId is locked + */ + event Lock(uint256 indexed tokenId, address account, address _locker, uint256 amount); + + /** + * @dev Emitted when tokenId is unlocked + */ + event Unlock (uint256 indexed tokenId, address account, address _locker, uint256 amount); + + /** + * @dev Lock the tokenId if msg.sender is owner or approved and set locker to msg.sender + */ + function lock(uint256 tokenId, address account, uint256 amount) external; + + /** + * @dev Lock the tokenId if msg.sender is owner and set locker to _locker + */ + function lock(uint256 tokenId, address account, address _locker, uint256 amount) external; + + /** + * @dev Unlocks the tokenId if msg.sender is locker + */ + function unlock(uint256 tokenId, address account, uint256 amount) external; + + /** + * @dev Tranfer and lock the token if the msg.sender is owner or approved. + * Lock the token and set locker to caller + * Optionally approve caller if bool setApprove flag is true + */ + function transferAndLock(address from, address to, uint256 tokenId, uint256 amount, bool setApprove) external; + + /** + * @dev Returns the wallet, that is stated as unlocking wallet for the tokenId. + * If (0) returned, that means token is not locked. Any other result means token is locked. + */ + function getLocked(uint256 tokenId, address account, address operator) external view returns (uint256); + + function setApprovalForId(uint256 tokenId, address operator, uint256 amount) external; +} +``` + +## Rationale + +This proposal exposes `transferAndLock(address from, address to, uint256 tokenId, uint256 amount, bool setApprove)` which can be used to transfer token and lock at the receiver's address. This additionally accepts input `bool setApprove` which on `true` assign `approval` to `locker`, hence enabling `locker` to revoke the token (revocation conditions can be defined in contracts and `approval` provided to contract). This provides conditional ownership to receiver, without the privilege to `transfer` token. + +## Backwards Compatibility + +This standard is compatible with [ERC-1155](./eip-1155.md) standards. + +Existing Upgradeable [ERC-1155](./eip-1155.md) can upgrade to this standard, enabling locking capability inherently and unlock underlying liquidity features. + +## Test Cases + +## Reference Implementation + +Reference Interface can be found [here](../assets/eip-7721/IERC7721.sol). + +Reference Implementation can be found [here](../assets/eip-7721/ERC7721.sol). + +## Security Considerations + +There are no security considerations related directly to the implementation of this standard for the contract that manages [ERC-1155](./eip-1155.md). + +### Considerations for the contracts that work with lockable tokens + +- Once a certain `amount` is `locked`, specified `amount` can not be transferred from locked `account`. +- If token is `locked` and caller is `locker` and `approved` both, caller can transfer the token. +- `locked` token with `locker` as in-accesible account or un-verified contract address can lead to permanent lock of the token. +- There are no MEV considerations regarding lockable tokens as only authorized parties are allowed to lock and unlock. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/assets/erc-7721/ERC7721.sol b/assets/erc-7721/ERC7721.sol new file mode 100644 index 0000000000..9bfff10e31 --- /dev/null +++ b/assets/erc-7721/ERC7721.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.0; + +import "./interfaces/IERC7721.sol"; +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; + +/// @title ERC7066: Lockable Extension for ERC721 +/// @dev Implementation for the Lockable extension ERC7066 for ERC721 +/// @author StreamNFT + +abstract contract ERC7721 is ERC1155,IERC7721{ + + /*/////////////////////////////////////////////////////////////// + ERC7066 EXTENSION STORAGE + //////////////////////////////////////////////////////////////*/ + + //Mapping from tokenId to user address for locker + mapping(uint256 tokenId => mapping(address account => mapping(address operator => uint256))) private locker; + mapping(uint256 tokenId => mapping(address account => mapping(address operator => uint256))) private nftApproval; + mapping(uint256 tokenId => mapping(address account => uint256)) private lockedAmount; + + + /*/////////////////////////////////////////////////////////////// + ERC7066 LOGIC + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Returns the locked amount for the tokenId on account by operator + */ + function getLocked(uint256 tokenId, address account, address operator) public virtual view override returns(uint256){ + return locker[tokenId][account][operator]; + } + + /** + * @dev Public function to lock the amount of token and set locker to msg.sender. Verifies if the msg.sender is owner or approved + * reverts otherwise + */ + // lock: is locked true or false: + function lock(uint256 tokenId, address account, uint256 amount) public virtual override{ + require( + account == _msgSender() || isApprovedForAll(account, _msgSender()) || getApprovalForId(tokenId,account,_msgSender())>=amount, + "ERC1155: caller is not token owner or approved" + ); + _lock(tokenId, account, _msgSender(), amount); + } + + /** + * @dev Public function to lock the amount of token and set locker to _locker. Verifies if the msg.sender is owner + * reverts otherwise + */ + // lock: is locked true or false: + function lock(uint256 tokenId, address account, address _locker, uint256 amount) public virtual override{ + require( + account == _msgSender() || isApprovedForAll(account, _locker) || getApprovalForId(tokenId,account,_locker)>=amount, + "ERC1155: caller is not token owner or approved" + ); + _lock(tokenId, account,_locker, amount); + } + + /** + * @dev Internal function to lock the token. + */ + function _lock(uint256 tokenId, address account, address _locker, uint256 amount) internal { + require(balanceOf(account, tokenId)>=amount,"ERC1155: Insufficient Balance"); + locker[tokenId][account][_msgSender()]=locker[tokenId][account][_msgSender()]+amount; + lockedAmount[tokenId][account]=lockedAmount[tokenId][account]+amount; + emit Lock(tokenId, account, _locker,amount); + } + + /** + * @dev Public function to unlock the token. Verifies the msg.sender is locker + * reverts otherwise + */ + function unlock(uint256 tokenId, address account, uint256 amount) public virtual override{ + require(locker[tokenId][account][_msgSender()]>=amount,"ERC1155: Insufficient Locked Amount"); + _unlock(tokenId,account,_msgSender(),amount); + } + + /** + * @dev Internal function to unlock the token. + */ + function _unlock(uint256 tokenId, address account, address _locker, uint256 amount) internal{ + locker[tokenId][account][_msgSender()]=locker[tokenId][account][_msgSender()]-amount; + lockedAmount[tokenId][account]=lockedAmount[tokenId][account]-amount; + emit Unlock(tokenId, account, _locker,amount); + } + + /** + * @dev Public function to tranfer and lock the token. Reverts if caller is not owner or approved. + * Lock the token and set locker to caller + *. Optionally approve caller if bool setApprove flag is true + */ + function transferAndLock(address from, address to, uint256 tokenId, uint256 amount, bool setApprove) public virtual { + _transferAndLock(tokenId,from,to,amount,setApprove); + } + + /** + * @dev Internal function to tranfer, update locker/approve and lock the token. + */ + function _transferAndLock(uint256 tokenId, address from, address to, uint256 amount, bool setApprove) internal { + safeTransferFrom(from, to, tokenId, amount, "" ); + if(setApprove){ + nftApproval[tokenId][to][from]=nftApproval[tokenId][to][from]+amount; + } + _lock(tokenId,to,msg.sender,amount); + } + + /** + * @dev Set approval on specific tokenId for token approved and operator on account + */ + function setApprovalForId(uint256 tokenId, address operator, uint256 amount) public virtual { + require (amount>locker[tokenId][_msgSender()][operator], "ERC1155: Insufficient Locked Amount"); + nftApproval[tokenId][_msgSender()][operator]=amount; + } + + /** + * @dev Get amount for token approved and operator on account + */ + function getApprovalForId(uint256 tokenId, address account, address operator) public virtual returns(uint256) { + return nftApproval[tokenId][account][operator]; + } + + /*/////////////////////////////////////////////////////////////// + OVERRIDES + //////////////////////////////////////////////////////////////*/ + + /** + * @dev See {IERC1155-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual override(ERC1155,IERC1155) { + require( + from == _msgSender() || isApprovedForAll(from, _msgSender()) || getApprovalForId(id,from,_msgSender()) >= amount, + "ERC1155: caller is not token owner or approved" + ); + _safeTransferFrom(from, to, id, amount, data); + } + + /** + * @dev Override _beforeTokenTransfer to make sure token is unlocked or msg.sender is approved if + * token is lockApproved + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override { + // if it is a Transfer or Burn, we always deal with one token, that is startTokenId + if (from != address(0)) { + for (uint256 i = 0; i < ids.length; i++) { + uint256 tokenId = ids[i]; + uint256 amount = amounts[i]; + if(locker[tokenId][from][operator]>0){ + require( getApprovalForId(tokenId,from,operator) >= amount,"ERC7066: Locked"); + } else{ + require(balanceOf(from, tokenId)-amount >= lockedAmount[tokenId][from],"ERC7066: Can't Spend Locked"); + } + } + } + super._beforeTokenTransfer(operator,from,to,ids,amounts,data); + } + + /** + * @dev Override _afterTokenTransfer to make locker is purged + */ + function _afterTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override { + // if it is a Transfer or Burn, we always deal with one token, that is startTokenId + if (from != address(0)) { + for (uint256 i = 0; i < ids.length; i++) { + uint256 tokenId=ids[i]; + uint256 amount = amounts[i]; + if(getApprovalForId(tokenId,from,operator)>=amount){ + nftApproval[tokenId][from][operator]=getApprovalForId(tokenId,from,operator)-amount; + if(locker[tokenId][from][operator]>0){ + if(amount>=locker[tokenId][from][operator]){ + lockedAmount[tokenId][from]=lockedAmount[tokenId][from]-locker[tokenId][from][operator]; + locker[tokenId][from][operator]=0; + } else{ + lockedAmount[tokenId][from]=lockedAmount[tokenId][from]-amount; + locker[tokenId][from][operator]=locker[tokenId][from][operator]-amount; + } + } + } + } + } + super._afterTokenTransfer(operator,from,to,ids,amounts,data); + } + + /*/////////////////////////////////////////////////////////////// + ERC165 LOGIC + //////////////////////////////////////////////////////////////*/ + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC1155) returns (bool) { + return + interfaceId == type(IERC7066SFT).interfaceId || + super.supportsInterface(interfaceId); + } +} \ No newline at end of file diff --git a/assets/erc-7721/IERC7721.sol b/assets/erc-7721/IERC7721.sol new file mode 100644 index 0000000000..42cbb4ec68 --- /dev/null +++ b/assets/erc-7721/IERC7721.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.0; + +/// @title Lockable Extension for ERC721 +/// @dev Interface for ERC7066 +/// @author StreamNFT +import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; + +interface IERC7721 is IERC1155{ + + /** + * @dev Emitted when tokenId is locked + */ + event Lock(uint256 indexed tokenId, address account, address _locker, uint256 amount); + + /** + * @dev Emitted when tokenId is unlocked + */ + event Unlock (uint256 indexed tokenId, address account, address _locker, uint256 amount); + + /** + * @dev lock the amount of token and set locker to msg.sender. Verifies if the msg.sender is owner or approved + * reverts otherwise + */ + function lock(uint256 tokenId, address account, uint256 amount) external; + + /** + * @dev lock the amount of token and set locker to _locker. Verifies if the msg.sender is owner + * reverts otherwise + */ + function lock(uint256 tokenId, address account, address _locker, uint256 amount) external; + + /** + * @dev unlock the token. Verifies the msg.sender is locker + * reverts otherwise + */ + function unlock(uint256 tokenId, address account, uint256 amount) external; + + /** + * @dev Tranfer and lock the token if the msg.sender is owner or approved. + * Lock the token and set locker to caller + * Optionally approve caller if bool setApprove flag is true + */ + function transferAndLock(address from, address to, uint256 tokenId, uint256 amount, bool setApprove) external; + + /** + * @dev Returns the locked amount for the tokenId on account by operator + */ + function getLocked(uint256 tokenId, address account, address operator) external view returns (uint256); + + /** + * @dev Set approval on specific tokenId for token approved and operator on account + */ + function setApprovalForId(uint256 tokenId, address operator, uint256 amount) external; + + /** + * @dev Get amount for token approved and operator on account + */ + function getApprovalForId(uint256 tokenId, address account, address operator) external; + +} \ No newline at end of file From c7d454b836be568408861c64910b19c4125ab2a1 Mon Sep 17 00:00:00 2001 From: Renan Souza Date: Tue, 9 Jul 2024 11:28:43 -0300 Subject: [PATCH 064/126] Add ERC: ABI specification for REVERT reason string Merged by EIP-Bot. --- ERCS/erc-838.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 ERCS/erc-838.md diff --git a/ERCS/erc-838.md b/ERCS/erc-838.md new file mode 100644 index 0000000000..2e6369dcef --- /dev/null +++ b/ERCS/erc-838.md @@ -0,0 +1,83 @@ +--- +eip: 838 +title: ABI specification for REVERT reason string +description: A proposal to extend the ABI specification to include typed errors in the REVERT reason string. +author: Federico Bond (@federicobond), Renan Rodrigues de Souza (@RenanSouza2) +discussions-to: https://ethereum-magicians.org/t/eip-838-what-is-the-current-status/14671 +status: Draft +type: Standards Track +category: ERC +created: 2020-08-20 +--- + +## Abstract + +This proposal specifies how to encode potential error conditions in the JSON ABI of a smart contract. A high-level language could then provide a syntax for declaring and throwing these errors. The compiler will encode these errors in the reason parameter of the REVERT opcode in a way that can be easily reconstructed by libraries such as web3. + + +## Motivation + +It's important to provide clear feedback to users (and developers) about what went wrong with their Ethereum transactions. The REVERT opcode is a step in the right direction, as it allows smart contract developers to encode a message describing the failure in the reason parameter. There is an implementation under review in Solidity that accepts a string, thus providing a low-level interface to this parameter. However, standardizing a method for passing errors from this parameter back to clients will bring many benefits to both users and developers. + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. + +## Specification + +To conform to this specification, compilers producing JSON ABIs SHOULD include error declarations alongside functions and events. Each error object MUST contain the keys name (string) and arguments (same types as the functionโ€™s inputs list). The value of type MUST be "error". + +Example: + +``` +{ "type": "error", "name": "InsufficientBalance", "arguments": [ { "name": "amount", "type": "uint256" } ] } +``` + +A selector for this error can be computed from its signature (InsufficientBalance() for the example above) in the same way that it's currently done for public functions and events. This selector MUST be included in the reason string so that clients can perform a lookup. Any arguments for the error are RLP encoded in the same way as return values from functions. The exact format in which both the selector and the arguments are encoded is to be defined. The Solidity implementation mentioned above leaves room for expansion by prefixing the free-form string with uint256(0). + +A high-level language like Solidity can then implement a syntax like this: + +``` +contract MyToken { + error InsufficientFunds(uint256 amount); + + function transfer(address _to, uint256 _amount) { + if (balances[msg.sender] <= _amount) + throw InsufficientFunds(_amount); + ... + } + ... +} +``` + +### Possible extensions + + +1. A NatSpec comment above the error declaration can be used to provide a default error message. Arguments to the error can be interpolated in the message string with familiar NatSpec syntax. + +``` +/// @notice You don't have enough funds to transfer `amount`. +error InsufficientFunds(uint256 amount); +``` + +2. A function may declare to its callers which errors it can throw. A list of these errors must be included in the JSON ABI item for that function, under the `errors` key. Example: + +``` +function transfer(address _to, uint256 _amount) throws(InsufficientFunds); +``` + +Special consideration should be given to error overloading if we want to support a similar syntax in the future, as errors with same name but different arguments will produce a different selector. + +## Rationale + +Needs discussion. + +## Backwards Compatibility + +Apps and tools that have not implemented this spec can ignore the encoded reason string when it's not prefixed by zero. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From c2b712a6475c6c5db427947903ff9b93f80ff449 Mon Sep 17 00:00:00 2001 From: sam say <80607135+Edoumou@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:31:23 +0200 Subject: [PATCH 065/126] Update ERC-7586: Move to Review Merged by EIP-Bot. --- ERCS/erc-7586.md | 56 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/ERCS/erc-7586.md b/ERCS/erc-7586.md index 06549c6f3f..5f1fdfb2d6 100644 --- a/ERCS/erc-7586.md +++ b/ERCS/erc-7586.md @@ -4,7 +4,7 @@ title: Interest Rate Swaps description: Interest rate swaps derivative contracts author: Samuel Gwlanold Edoumou (@Edoumou) discussions-to: https://ethereum-magicians.org/t/interest-rate-swaps/17777 -status: Draft +status: Review type: Standards Track category: ERC created: 2023-12-31 @@ -29,7 +29,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S ![alt text](../assets/eip-7586/irs.jpeg "IRS diagram") -Every contract compliant with this ERC MUST implement the following interface. The contract MUST inherit from [`ERC20`](./eip-20) to tokenize the swap cash flows. +Every contract compliant with this ERC MUST implement the following interface. The contract MUST inherit from [ERC-20](./eip-20.md) to tokenize the swap cash flows. ```solidity pragma solidity ^0.8.0; @@ -57,12 +57,12 @@ interface IERC7586 /** is ERC20, ERC165 */ { /** * @notice Returns the IRS `payer` account address. The party who agreed to pay fixed interest */ - function fixedInterestPayer() external view returns(address); + function fixedRatePayer() external view returns(address); /** * @notice Returns the IRS `receiver` account address. The party who agreed to pay floating interest */ - function floatingInterestPayer() external view returns(address); + function floatingRatePayer() external view returns(address); /** * @notice Returns the number of decimals the swap rate and spread use - e.g. `4` means to divide the rates by `10000` @@ -73,23 +73,34 @@ interface IERC7586 /** is ERC20, ERC165 */ { function ratesDecimals() external view returns(uint8); /** - * @notice Returns the fixed interest rate + * @notice Returns the fixed interest rate. All rates MUST be multiplied by 10^(ratesDecimals) */ function swapRate() external view returns(uint256); /** - * @notice Returns the floating rate spread, i.e. the fixed part of the floating interest rate - * + * @notice Returns the floating rate spread, i.e. the fixed part of the floating interest rate. All rates MUST be multiplied by 10^(ratesDecimals) * floatingRate = benchmark + spread */ function spread() external view returns(uint256); /** - * @notice Returns the contract address of the asset to be transferred when swapping IRS. Depending on what the two parties agreed upon, this could be a currency, etc. - * Example: If the two parties agreed to swap interest rates in USDC, then this function should return the USDC contract address. - * This address SHOULD be used in the `swap` function to transfer the interest difference to either the `payer` or the `receiver`. Example: IERC(assetContract).transfer + * @notice Returns the day count basis + * For example, 0 can denote actual/actual, 1 can denote actual/360, and so on + */ + function dayCountBasis() external view returns(uint8); + + /** + * @notice Returns the contract address of the currency for which the notional amount is denominated (Example: USDC contract address). + * Returns the zero address if the notional is expressed in FIAT currency like USD + */ + function notionalCurrency() external view returns(address); + + /** + * @notice Returns an array of acceptable contract address of the assets to be transferred when swapping IRS + * The two counterparties may wish to get the payment in different currencies. + * Ex: if the payer wants to receive the payment in USDC and the receiver in DAI, then the function should return [USDC, DAI] or [DAI, USDC] */ - function assetContract() external view returns(address); + function paymentAssets() external view returns(address[] memory); /** * @notice Returns the notional amount in unit of asset to be transferred when swapping IRS. This amount serves as the basis for calculating the interest payments, and may not be exchanged @@ -98,17 +109,25 @@ interface IERC7586 /** is ERC20, ERC165 */ { function notionalAmount() external view returns(uint256); /** - * @notice Returns the interest payment frequency + * @notice Returns the number of times payments must be realized in 1 year */ function paymentFrequency() external view returns(uint256); /** - * @notice Returns an array of specific dates on which the interest payments are exchanged. Each date MUST be a Unix timestamp like the one returned by block.timestamp + * @notice Returns an array of specific dates on which the fix interest payments are exchanged. Each date MUST be a Unix timestamp like the one returned by block.timestamp + * The length of the array returned by this function MUST equal the total number of swaps that should be realized + * + * OPTIONAL + */ + function fixPaymentDates() external view returns(uint256[] memory); + + /** + * @notice Returns an array of specific dates on which the floating interest payments are exchanged. Each date MUST be a Unix timestamp like the one returned by block.timestamp * The length of the array returned by this function MUST equal the total number of swaps that should be realized * * OPTIONAL */ - function paymentDates() external view returns(uint256[] memory); + function floatingPaymentDates() external view returns(uint256[] memory); /** * @notice Returns the starting date of the swap contract. This is a Unix Timestamp like the one returned by block.timestamp @@ -121,22 +140,23 @@ interface IERC7586 /** is ERC20, ERC165 */ { function maturityDate() external view returns(uint256); /** - * @notice Returns the benchmark in basis point unit + * @notice Returns the benchmark (the reference rate). All rates MUST be multiplied by 10^(ratesDecimals) * Example: value of one the following rates: CF BIRC, EURIBOR, HIBOR, SHIBOR, SOFR, SONIA, TONAR, etc. + * Or set manually */ function benchmark() external view returns(uint256); /** - * @notice Returns the oracle contract address for the benchmark rate, or the zero address when the two parties agreed to set the benchmark manually. + * @notice Returns the oracle contract address for acceptable reference rates (benchmark), or the zero address when the two parties agreed to set the benchmark manually. * This contract SHOULD be used to fetch real time benchmark rate * Example: Contract address for `CF BIRC` * * OPTIONAL. The two parties MAY agree to set the benchmark manually */ - function oracleContractForBenchmark() external view returns(address); + function oracleContractsForBenchmark() external view returns(address); /** - * @notice Makes swap calculation and transfers the interest difference to either the `payer` or the `receiver` + * @notice Makes swap calculation and transfers the payment to counterparties */ function swap() external returns(bool); From e83c0862bce4ae2b53db5ea4ce26799b1e3cfe20 Mon Sep 17 00:00:00 2001 From: Vidor <1101164+V1d0r@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:16:46 +0100 Subject: [PATCH 066/126] Update ERC-7578: Fix github authors Merged by EIP-Bot. --- ERCS/erc-7578.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7578.md b/ERCS/erc-7578.md index 8adbefe602..fa520d71d6 100644 --- a/ERCS/erc-7578.md +++ b/ERCS/erc-7578.md @@ -2,7 +2,7 @@ eip: 7578 title: Physical Asset Redemption description: Provides the holder of physical asset backed NFTs readily available real-world information on the underlying physical asset. -author: Lee Vidor (@V1d0r) , David Tan , Lee Smith , Gabriel Stoica (@gabrielstoica) +author: Lee Vidor (@V1d0r), David Tan , Lee Smith , Gabriel Stoica (@gabrielstoica) discussions-to: https://ethereum-magicians.org/t/erc-7578-physical-asset-redemption/17556 status: Review type: Standards Track From 94c2c8920add369304736858acdeaed437c5a0b8 Mon Sep 17 00:00:00 2001 From: dRadiant <103012174+dRadiant@users.noreply.github.com> Date: Thu, 11 Jul 2024 03:35:32 -0400 Subject: [PATCH 067/126] Update ERC-5173: Update ERC-5173 Merged by EIP-Bot. --- ERCS/erc-5173.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ERCS/erc-5173.md b/ERCS/erc-5173.md index cd97e51bbc..61eb9e4547 100644 --- a/ERCS/erc-5173.md +++ b/ERCS/erc-5173.md @@ -13,21 +13,21 @@ requires: 165, 721 ## Abstract -This EIP introduces the NFT Future Rewards (nFR) extension for [EIP-721](./eip-721.md) tokens (NFTs). nFR allows owners to benefit from future price appreciation even after selling their tokens. This establishes a Provenance Value Amplification (PVA) framework where creators, buyers, and sellers collaborate to collectively increase value. This innovative approach disrupts the current zero-sum trading paradigm by creating a fairer and more rewarding system for all participants. +This ERC introduces the NFT Future Rewards (nFR) extension for [ERC-721](./eip-721.md) tokens (NFTs). nFR allows owners to benefit from future price appreciation even after selling their tokens, without the need for market prediction. This establishes a Provenance Value Amplification (PVA) framework where creators, buyers, and sellers collaborate to collectively increase value. This innovative approach disrupts the current zero-sum trading paradigm by creating a fairer and more rewarding system for all participants. -[ERC-5173](./eip-5173.md) fosters a sustainable and collaborative trading environment by aligning the interests of service providers and users. Compliant token owners enjoy price increases during holding and continue receiving nFRs after selling. By eliminating competition and promoting shared prosperity, nFR fosters strong bonds within the NFT ecosystem. The framework ensures equitable profit distribution across all historical owners, including the original minter. +[ERC-5173](./eip-5173.md) fosters a sustainable and collaborative trading environment by aligning the interests of service providers and users. Compliant token owners enjoy price increases during holding and continue receiving nFRs after selling. By eliminating competition and promoting shared prosperity, nFR fosters strong bonds within the NFT and crypto token ecosystems. The framework ensures equitable profit distribution across all historical owners, including the original minter. ## Motivation The current trading landscape is often marred by unfair practices like spoofing, insider trading, and wash trading. These activities disadvantage average traders caught in cycles of fear and greed. However, the rise of NFTs and their inherent transaction tracking capability presents an opportunity to disrupt this unequal value distribution. -ERC-5173 introduces a standardized profit-sharing model across the entire ownership history of an NFT, benefiting all market participants. It creates a Flow of provenance where buyers and owners are rewarded for their contributions to price discovery. This model fosters aligned interests and establishes a mutually beneficial economic structure for both buyers and sellers. +ERC-5173 introduces a standardized profit-sharing model across the entire ownership history of an NFT, benefiting all market participants. It creates a "Flow of Provenance" where buyers and owners are rewarded for their contributions to price discovery. This model fosters aligned interests and establishes a mutually beneficial economic structure for both buyers and sellers. -NFTs can accurately reflect the contributions of their owners to their value. By recording every price change of each ERC-5173 token, we can establish a Future Rewards program that fairly compensates owners. This program aims to level the playing field and provide average traders with a better chance at success. +NFTs can accurately reflect the contributions of their owners to their value. By recording every price change of each ERC-5173 token, we can establish a Future Rewards program that fairly compensates owners. This program aims to level the playing field and provide average traders with a better chance at success, without the need for complex market predictions. -In addition to promoting this novel gift economic model, the nFR framework discourages illicit activities that circumvent artist and marketplace rules. This fosters a transparent and trustworthy trading ecosystem. +In addition to promoting this novel "gift economy" model, the nFR framework discourages illicit activities that circumvent artist and marketplace rules. This fosters a transparent and trustworthy trading ecosystem. -Applied to the exchange of wrapped [EIP-20](./eip-20.md) tokens, this value-amplification construct has the potential to transform the asset transaction sector by integrating identities within the time and sales (T&S) data. This inclusive attribute affords a holistic perspective of each trade, infusing an additional layer of depth into the trading experience. +Applied to the exchange of wrapped [ERC-20](./eip-20.md) tokens, this value-amplification construct has the potential to transform the asset transaction sector by integrating identities within the time and sales (T&S) data. This inclusive attribute affords a holistic perspective of each trade, infusing an additional layer of depth into the trading experience. ## Specification From 23e35d6e7563b448069e3c1248970daf50ee5af8 Mon Sep 17 00:00:00 2001 From: Victor Liew Jia Hao Date: Tue, 16 Jul 2024 11:40:38 +0800 Subject: [PATCH 068/126] Update ERC-7291: Update minor text, in preparation for new function parameter updates Merged by EIP-Bot. --- ERCS/erc-7291.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7291.md b/ERCS/erc-7291.md index 18d46ca41b..8aaa7220de 100644 --- a/ERCS/erc-7291.md +++ b/ERCS/erc-7291.md @@ -619,7 +619,7 @@ Why is there a need for destination? ### Why was a push transaction model chosen? -- This standard sticks to the push transaction model where the transfer of PBM is initiated on the senders side. Modern wallets can support the required PBM logic by embedding the unwrapping logic within the ERC-1155 `safeTransfer` function. +- This standard sticks to the push transaction model where the transfer of PBM is initiated on the sender's side. Modern wallets can support the required PBM logic by embedding the unwrapping logic within the ERC-1155 `safeTransfer` function. ### Customisability From 59f751b4856e500c6dc2c22bb6e79b7f16317393 Mon Sep 17 00:00:00 2001 From: Elias Rad <146735585+nnsW3@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:16:22 +0300 Subject: [PATCH 069/126] Update ERC-1078: Fix docs (#540) * fix erc-1078.md * Update erc-1129.md --- ERCS/erc-1078.md | 2 +- ERCS/erc-1129.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-1078.md b/ERCS/erc-1078.md index ee1a697c35..ca2e2792c1 100644 --- a/ERCS/erc-1078.md +++ b/ERCS/erc-1078.md @@ -43,7 +43,7 @@ If the user doesnโ€™t have an identity, the app should provide the option to cre All those steps can be designed to be set up in a single ethereum transaction. Since this step is not free, the app reserves the right to charge for registering users, or require the user to be verified in a sybil resistant manner of the appโ€™s choosing (captcha, device ID registration, proof of work, etc) -The user shouldnโ€™t be forced to wait for transaction confirmation times. Instead, have an indicator somewhere on the app the shows the progress and then allow the user to interact with your app normally. Itโ€™s unlikely that theyโ€™ll need the identity in the first few minutes and if something goes wrong (username gets registered at the same time), you can then ask the user for an action. +The user shouldnโ€™t be forced to wait for transaction confirmation times. Instead, have an indicator somewhere on the app that shows the progress and then allow the user to interact with your app normally. Itโ€™s unlikely that theyโ€™ll need the identity in the first few minutes and if something goes wrong (username gets registered at the same time), you can then ask the user for an action. **Implementation note:** in order to save gas, some of these steps can be done in advance. The app can automatically deploy a small number of contracts when the gas price is low, and set up all their main variables to be 0xFFFFFF...FFFFF. These should be considered โ€˜vacantโ€™ and when the user registers one, they will get a gas discount for freeing up space on the chain. This has the added benefit of allowing the user a choice in contract address/icon. diff --git a/ERCS/erc-1129.md b/ERCS/erc-1129.md index ca6acdcd75..d3b1579d1f 100644 --- a/ERCS/erc-1129.md +++ b/ERCS/erc-1129.md @@ -49,9 +49,9 @@ struct Announcement{ ### Methods -#### the number of ammouncements +#### the number of announcements -Returns the number of announcement currently active. +Returns the number of announcements currently active. OPTIONAL - this method can be used to provide quicker information for the UI, but could also be retrieved from `numberOfMessages` variable. From df5fd2479cf1332c36da90a7552106f88fe04172 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 19 Jul 2024 17:31:43 +0200 Subject: [PATCH 070/126] Update ERC-7579: Fix typo in ERC-7579 Merged by EIP-Bot. --- ERCS/erc-7579.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7579.md b/ERCS/erc-7579.md index f7f997ee35..199130bf71 100644 --- a/ERCS/erc-7579.md +++ b/ERCS/erc-7579.md @@ -111,7 +111,7 @@ The account MUST encode the execution data the following ways: - For single calls, the `target`, `value` and `callData` are packed in this order (ie `abi.encodePacked` in Solidity). - For delegatecalls, the `target` and `callData` are packed in this order (ie `abi.encodePacked` in Solidity). -- For batch calls, the `targets`, `values` and `callDatas` are put into an array of `Exeuction` structs that includes these fields in this order (ie `Execution(address target, uint256 value, bytes memory callData)`). Then, this array is encoded with padding (ie `abi.encode` in Solidity). +- For batch calls, the `targets`, `values` and `callDatas` are put into an array of `Execution` structs that includes these fields in this order (ie `Execution(address target, uint256 value, bytes memory callData)`). Then, this array is encoded with padding (ie `abi.encode` in Solidity). #### Account configurations From 7256bb6a169d14f324304509544d9a8ff884ab71 Mon Sep 17 00:00:00 2001 From: galimba Date: Mon, 22 Jul 2024 15:38:44 +0200 Subject: [PATCH 071/126] Update ERC-7208: fix to ODC interface Merged by EIP-Bot. --- ERCS/erc-7208.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/ERCS/erc-7208.md b/ERCS/erc-7208.md index c5cc463ca6..3dd6fca0d5 100644 --- a/ERCS/erc-7208.md +++ b/ERCS/erc-7208.md @@ -87,23 +87,6 @@ interface IODC { */ function allowDataManager(DataPoint dp, address dm, bool approved) external; - /** - * @notice Verifies if DataObject is allowed to add Hooks to the DataPoint - * @param dp Identifier of the DataPoint - * @param dobj Address of DataObject - * @return if write access is allowed - */ - function isApprovedDataObject(DataPoint dp, address dobj) external view returns(bool); - - /** - * @notice Defines if DataObject is allowed to add Hooks to the DataPoint - * @param dp Identifier of the DataPoint - * @param dobj Address of DataObject - * @param approved if DataManager should be approved for the DataPoint - * @dev Function should be restricted to datapoint maintainer only - */ - function allowDataObject(DataPoint dp, address dobj, bool approved) external; - /** * @notice Reads stored data * @param dobj Identifier of DataObject From 84323d0d16c218974b6b76721247862fcde54e02 Mon Sep 17 00:00:00 2001 From: Harpocrates <116655068+harpocrates555@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:20:28 -0400 Subject: [PATCH 072/126] Add ERC: Collateralized NFT Merged by EIP-Bot. --- ERCS/erc-7595.md | 491 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 491 insertions(+) create mode 100644 ERCS/erc-7595.md diff --git a/ERCS/erc-7595.md b/ERCS/erc-7595.md new file mode 100644 index 0000000000..c56748caa4 --- /dev/null +++ b/ERCS/erc-7595.md @@ -0,0 +1,491 @@ +--- +eip: 7595 +title: Collateralized NFT +description: ERC-721 Extension to enable collateralization with ERC-20 based tokens. +author: 571nKY (@571nKY), Cosmos (@Cosmos4k), f4t50 (@f4t50), Harpocrates (@harpocrates555) +discussions-to: https://ethereum-magicians.org/t/collateralized-nft-standard/18097 +status: Draft +type: Standards Track +category: ERC +created: 2023-03-13 +requires: 20, 721 +--- + +## Abstract + +This proposal recommends an extension of [ERC-721](./eip-721.md) to allow for collateralization using a list of [ERC-20](./eip-20.md) based tokens. The proprietor of this ERC collection could hold both the native coin and [ERC-20](./eip-20.md) based tokens, with the `ownerOf` tokenId being able to unlock the associated portion of the underlying [ERC-20](./eip-20.md) balance. + +## Motivation + +The emerging trend of NFT finance focuses on the NFT floor price to enable the market value of the NFT serve as a collateral in lending protocols. The NFT floor price is susceptible to the supply-demand dynamics of the NFT market, characterized by higher volatility compared to the broader crypto market. Furthermore, potential price manipulation in specific NFT collections can artificially inflate NFT market prices, impacting the floor price considered by lending protocols. Relying solely on the NFT floor price based on market value is both unpredictable and unreliable. + +This ERC addresses various challenges encountered by the crypto community with [ERC-721](./eip-721.md) based collections and assets. This ERC brings forth advantages such as sustainable NFT royalties supported by tangible assets, an on-chain verifiable floor price, and the introduction of additional monetization avenues for NFT collection creators. + +### Presets + +* The Basic Preset allows for the evaluation of an on-chain verifiable price floor for a specified NFT asset. + +* The Dynamic Preset facilitates on-chain modification of tokenURI based on predefined collateral rules for a specified NFT asset. + +* With the Royalty Preset, NFT collection creators can receive royalty payments for each transaction involving asset owners and Externally Owned Accounts (EOA), as well as transactions with smart contracts. + +* The VRF Preset enables the distribution of collateral among multiple NFT asset holders using the Verifiable Random Function (VRF) by Chainlink. + +### Extension to Existing ERC-721 Based Collections + +For numerous [ERC-721](./eip-721.md) based collections that cannot be redeployed, we propose the implementation of an abstraction layer embodied by a smart contract. This smart contract would replicate all the functionalities of this ERC standard and grant access to collateral through mapping. + +## Specification +### ERC standard for new NFT collections + +```solidity + +interface IERC721Envious is IERC721 { + event Collateralized(uint256 indexed tokenId, uint256 amount, address tokenAddress); + event Uncollateralized(uint256 indexed tokenId, uint256 amount, address tokenAddress); + event Dispersed(address indexed tokenAddress, uint256 amount); + event Harvested(address indexed tokenAddress, uint256 amount, uint256 scaledAmount); + + /** + * @dev An array with two elements. Each of them represents percentage from collateral + * to be taken as a commission. First element represents collateralization commission. + * Second element represents uncollateralization commission. There should be 3 + * decimal buffer for each of them, e.g. 1000 = 1%. + * + * @param uint 256 index of value in array. + */ + function commissions(uint256 index) external view returns (uint256); + + /** + * @dev 'Black hole' is any address that guarantees that tokens sent to it will not be + * retrieved from it. Note: some tokens revert on transfer to zero address. + * + * @return address address of black hole. + */ + function blackHole() external view returns (address); + + /** + * @dev Token that will be used to harvest collected commissions. + * + * @return address address of token. + */ + function communityToken() external view returns (address); + + /** + * @dev Pool of available tokens for harvesting. + * + * @param uint256 index in array. + * @return address address of token. + */ + function communityPool(uint256 index) external view returns (address); + + /** + * @dev Token balance available for harvesting. + * + * @param address address of token. + * @return uint256 token balance. + */ + function communityBalance(address tokenAddress) external view returns (uint256); + + /** + * @dev Array of tokens that have been dispersed. + * + * @param uint256 index in array. + * @return address address of dispersed token. + */ + function disperseTokens(uint256 index) external view returns (address); + + /** + * @dev Amount of tokens that has been dispersed. + * + * @param address address of token. + * @return uint256 token balance. + */ + function disperseBalance(address tokenAddress) external view returns (uint256); + + /** + * @dev Amount of tokens that was already taken from the disperse. + * + * @param address address of token. + * @return uint256 total amount of tokens already taken. + */ + function disperseTotalTaken(address tokenAddress) external view returns (uint256); + + /** + * @dev Amount of disperse already taken by each tokenId. + * + * @param tokenId unique identifier of unit. + * @param address address of token. + * @return uint256 amount of tokens already taken. + */ + function disperseTaken(uint256 tokenId, address tokenAddress) external view returns (uint256); + + /** + * @dev Mapping of `tokenId`s to token addresses that have collateralized before. + * + * @param tokenId unique identifier of unit. + * @param index in array. + * @return address address of token. + */ + function collateralTokens(uint256 tokenId, uint256 index) external view returns (address); + + /** + * @dev Token balances that are stored under `tokenId`. + * + * @param tokenId unique identifier of unit. + * @param address address of token. + * @return uint256 token balance. + */ + function collateralBalances(uint256 tokenId, address tokenAddress) external view returns (uint256); + + /** + * @dev Calculator function for harvesting. + * + * @param amount of `communityToken`s to spend + * @param address address of token to be harvested + * @return amount to harvest based on inputs + */ + function getAmount(uint256 amount, address tokenAddress) external view returns (uint256); + + /** + * @dev Collect commission fees gathered in exchange for `communityToken`. + * + * @param amounts[] array of amounts to collateralize + * @param address[] array of token addresses + */ + function harvest(uint256[] memory amounts, address[] memory tokenAddresses) external; + + /** + * @dev Collateralize NFT with different tokens and amounts. + * + * @param tokenId unique identifier for specific NFT + * @param amounts[] array of amounts to collateralize + * @param address[] array of token addresses + */ + function collateralize( + uint256 tokenId, + uint256[] memory amounts, + address[] memory tokenAddresses + ) external payable; + + /** + * @dev Withdraw underlying collateral. + * + * Requirements: + * - only owner of NFT + * + * @param tokenId unique identifier for specific NFT + * @param amounts[] array of amounts to collateralize + * @param address[] array of token addresses + */ + function uncollateralize( + uint256 tokenId, + uint256[] memory amounts, + address[] memory tokenAddresses + ) external; + + /** + * @dev Split collateral among all existent tokens. + * + * @param amounts[] to be dispersed among all NFT owners + * @param address[] address of token to be dispersed + */ + function disperse(uint256[] memory amounts, address[] memory tokenAddresses) external payable; +} +``` + +### Abstraction layer for already deployed NFT collections + +```solidity + +interface IEnviousHouse { + event Collateralized( + address indexed collection, + uint256 indexed tokenId, + uint256 amount, + address tokenAddress + ); + + event Uncollateralized( + address indexed collection, + uint256 indexed tokenId, + uint256 amount, + address tokenAddress + ); + + event Dispersed( + address indexed collection, + address indexed tokenAddress, + uint256 amount + ); + + event Harvested( + address indexed collection, + address indexed tokenAddress, + uint256 amount, + uint256 scaledAmount + ); + + /** + * @dev totalCollections function returns the total count of registered collections. + * + * @return uint256 number of registered collections. + */ + function totalCollections() external view returns (uint256); + + /** + * @dev 'Black hole' is any address that guarantees that tokens sent to it will not be + * retrieved from it. Note: some tokens revert on transfer to zero address. + * + * @param address collection address. + * @return address address of black hole. + */ + function blackHole(address collection) external view returns (address); + + /** + * @dev collections function returns the collection address based on the collection index input. + * + * @param uint256 index of a registered collection. + * @return address address collection. + */ + function collections(uint256 index) external view returns (address); + + /** + * @dev collectionIds function returns the collection index based on the collection address input. + * + * @param address collection address. + * @return uint256 collection index. + */ + function collectionIds(address collection) external view returns (uint256); + + /** + * @dev specificCollections function returns whether a particular collection follows the ERC721 standard or not. + * + * @param address collection address. + * @return bool specific collection or not. + */ + function specificCollections(address collection) external view returns (bool); + + /** + * @dev An array with two elements. Each of them represents percentage from collateral + * to be taken as a commission. First element represents collateralization commission. + * Second element represents uncollateralization commission. There should be 3 + * decimal buffer for each of them, e.g. 1000 = 1%. + * + * @param address collection address. + * @param uint256 index of value in array. + * @return uint256 collected commission. + */ + function commissions(address collection, uint256 index) external view returns (uint256); + + /** + * @dev Token that will be used to harvest collected commissions. + * + * @param address collection address. + * @return address address of token. + */ + function communityToken(address collection) external view returns (address); + + /** + * @dev Pool of available tokens for harvesting. + * + * @param address collection address. + * @param uint256 index in array. + * @return address address of token. + */ + function communityPool(address collection, uint256 index) external view returns (address); + + /** + * @dev Token balance available for harvesting. + * + * @param address collection address. + * @param address address of token. + * @return uint256 token balance. + */ + function communityBalance(address collection, address tokenAddress) external view returns (uint256); + + /** + * @dev Array of tokens that have been dispersed. + * + * @param address collection address. + * @param uint256 index in array. + * @return address address of dispersed token. + */ + function disperseTokens(address collection, uint256 index) external view returns (address); + + /** + * @dev Amount of tokens that has been dispersed. + * + * @param address collection address. + * @param address address of token. + * @return uint256 token balance. + */ + function disperseBalance(address collection, address tokenAddress) external view returns (uint256); + + /** + * @dev Amount of tokens that was already taken from the disperse. + * + * @param address collection address. + * @param address address of token. + * @return uint256 total amount of tokens already taken. + */ + function disperseTotalTaken(address collection, address tokenAddress) external view returns (uint256); + + /** + * @dev Amount of disperse already taken by each tokenId. + * + * @param address collection address. + * @param tokenId unique identifier of unit. + * @param address address of token. + * @return uint256 amount of tokens already taken. + */ + function disperseTaken(address collection, uint256 tokenId, address tokenAddress) external view returns (uint256); + + /** + * @dev Mapping of `tokenId`s to token addresses that have collateralized before. + * + * @param address collection address. + * @param tokenId unique identifier of unit. + * @param index in array. + * @return address address of token. + */ + function collateralTokens(address collection, uint256 tokenId, uint256 index) external view returns (address); + + /** + * @dev Token balances that are stored under `tokenId`. + * + * @param address collection address. + * @param tokenId unique identifier of unit. + * @param address address of token. + * @return uint256 token balance. + */ + function collateralBalances(address collection, uint256 tokenId, address tokenAddress) external view returns (uint256); + + /** + * @dev Calculator function for harvesting. + * + * @param address collection address. + * @param amount of `communityToken`s to spend. + * @param address address of token to be harvested. + * @return amount to harvest based on inputs. + */ + function getAmount(address collection, uint256 amount, address tokenAddress) external view returns (uint256); + + /** + * @dev setSpecificCollection function enables the addition of any collection that is not compatible with the ERC721 standard to the list of exceptions. + * + * @param address collection address. + */ + function setSpecificCollection(address collection) external; + + /** + * @dev registerCollection function grants Envious functionality to any ERC721-compatible collection and streamlines + * the distribution of an initial minimum disbursement to all NFT holders. + * + * @param address collection address. + * @param address address of `communityToken`. + * @param uint256 collateralization fee, incoming / 1e5 * 100%. + * @param uint256 uncollateralization fee, incoming / 1e5 * 100%. + */ + function registerCollection( + address collection, + address token, + uint256 incoming, + uint256 outcoming + ) external payable; + + /** + * @dev Collect commission fees gathered in exchange for `communityToken`. + * + * @param address collection address. + * @param amounts[] array of amounts to collateralize. + * @param address[] array of token addresses. + */ + function harvest( + address collection, + uint256[] memory amounts, + address[] memory tokenAddresses + ) external; + + /** + * @dev Collateralize NFT with different tokens and amounts. + * + * @param address collection address. + * @param tokenId unique identifier for specific NFT. + * @param amounts[] array of amounts to collateralize. + * @param address[] array of token addresses. + */ + function collateralize( + address collection, + uint256 tokenId, + uint256[] memory amounts, + address[] memory tokenAddresses + ) external payable; + + /** + * @dev Withdraw underlying collateral. + * + * Requirements: + * - only owner of NFT + * + * @param address collection address. + * @param tokenId unique identifier for specific NFT. + * @param amounts[] array of amounts to collateralize. + * @param address[] array of token addresses. + */ + function uncollateralize( + address collection, + uint256 tokenId, + uint256[] memory amounts, + address[] memory tokenAddresses + ) external; + + /** + * @dev Split collateral among all existent tokens. + * + * @param address collection address. + * @param amounts[] to be dispersed among all NFT owners. + * @param address[] address of token to be dispersed. + */ + function disperse( + address collection, + uint256[] memory amounts, + address[] memory tokenAddresses + ) external payable; +} +``` + +## Rationale +### โ€œEnviousโ€ Term Choice +We propose adopting the term "Envious" to describe any NFT collection minted using this ERC standard or any [ERC-721](./eip-721.md) based NFT collection that utilized the EnviousHouse abstraction layer. + +### NFT Collateralization with Multiple Tokens +Some Web3 projects primarily collateralize a specific NFT asset with one [ERC-20](./eip-20.md) based token, resulting in increased gas fees and complications in User Experience (UX). + +This ERC has been crafted to enable the collateralization of a designated NFT asset with multiple [ERC-20](./eip-20.md) based tokens within a single transaction. + +### NFT Collateralization with the Native Coin +Each [ERC-20](./eip-20.md) based token possesses a distinct address. However, a native coin does not carry an address. To address this, we propose utilizing a null address (`0x0000000000000000000000000000000000000000`) as an identifier for the native coin during collateralization, as it eliminates the possibility of collisions with smart contract addresses. + +### Disperse Functionality +We have implemented the capability to collateralize all assets within a particular NFT collection in a single transaction. The complete collateral amount is deposited into a smart contract, enabling each user to claim their respective share of the collateral when they add or redeem collateral for that specific asset. + +### Harvest Functionality +Each Envious NFT collection provides an option to incorporate a community [ERC-20](./eip-20.md) based token, which can be exchanged for commissions accrued from collateralization and uncollateralization activities. + +### BlackHole Instance +Some [ERC-20](./eip-20.md) based token implementations forbid transfers to the null address, it is necessary to have a reliable burning mechanism in the harvest transactions. `blackHole` smart contract removes [ERC-20](./eip-20.md) communityTokens from the circulating supply in exchange for commission fees withdrawn. + +`blackHole` has been designed to prevent the transfer of any tokens from itself and can only perform read operations. It is intended to be used with the Envious extension in implementations related to commission harvesting. + +## Backwards Compatibility + +EnviousHouse abstraction layer is suggested for already deployed [ERC-721](./eip-721.md) based NFT collections. + +## Security Considerations + +Envious may share security concerns similar to those found in [ERC-721](./eip-721.md), such as hidden logic within functions like burn, add resource, accept resource, etc. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From a247eddf895286331eab23990fd9905d80dd8d42 Mon Sep 17 00:00:00 2001 From: rickey <12867336+HelloRickey@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:32:51 +0800 Subject: [PATCH 073/126] Update ERC-7654: Add payable for POST and PUT methods Merged by EIP-Bot. --- ERCS/erc-7654.md | 4 ++-- assets/erc-7654/RequestMethodTypes.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ERCS/erc-7654.md b/ERCS/erc-7654.md index edfd53de14..8e2eb4621f 100644 --- a/ERCS/erc-7654.md +++ b/ERCS/erc-7654.md @@ -97,7 +97,7 @@ interface IRequestMethodTypes{ * @param _methodReq is the method type. * @return The response to the post request. */ - function post(string memory _methodName,bytes memory _methodReq)external returns(bytes memory); + function post(string memory _methodName,bytes memory _methodReq)external payable returns(bytes memory); /** * Request the contract to update a record. @@ -105,7 +105,7 @@ interface IRequestMethodTypes{ * @param _methodReq is the method type. * @return The response to the put request. */ - function put(string memory _methodName,bytes memory _methodReq)external returns(bytes memory); + function put(string memory _methodName,bytes memory _methodReq)external payable returns(bytes memory); /** * Supported request method types. diff --git a/assets/erc-7654/RequestMethodTypes.sol b/assets/erc-7654/RequestMethodTypes.sol index b3c3221402..0dd2b9c204 100644 --- a/assets/erc-7654/RequestMethodTypes.sol +++ b/assets/erc-7654/RequestMethodTypes.sol @@ -59,7 +59,7 @@ contract RequestMethodTypes is IRequestMethodTypes{ } } - function post(string memory _methodName,bytes memory _methodReq)public returns(bytes memory){ + function post(string memory _methodName,bytes memory _methodReq)public payable returns(bytes memory){ if(compareStrings(_methodName,"createUser")){ (string memory name,uint256 age)=abi.decode(_methodReq, (string,uint256)); users[msg.sender]=Profiles(name,age); @@ -68,7 +68,7 @@ contract RequestMethodTypes is IRequestMethodTypes{ return abi.encode(""); } - function put(string memory _methodName,bytes memory _methodReq)public returns(bytes memory){ + function put(string memory _methodName,bytes memory _methodReq)public payable returns(bytes memory){ if(compareStrings(_methodName,"updateUserName")){ (address userAddress,string memory name)=abi.decode(_methodReq, (address,string)); require(userAddress==msg.sender); From 695d8dd6bc2806dff70ab066656d9a95602fdb76 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:11:47 +0200 Subject: [PATCH 074/126] Add ERC: Authorize Operator (#536) * Move ERC7575 to Last Call * Clarifications * Fix reference implementation, add pipe explanation * Fix typo * Move ERC7540 to Last Call * requester => controller * chore: add author * fix: undo status change * comments * Authorize operator ERC * Update * feat: add motivation and rationale * Add * Update filename * Add discussions link * Add domain separator * Add authorizations lookup * Fix reference implementation * Fix eipw --------- Co-authored-by: Timepunk <45543880+0xTimepunk@users.noreply.github.com> Co-authored-by: Joey Santoro --- ERCS/erc-7741.md | 211 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 ERCS/erc-7741.md diff --git a/ERCS/erc-7741.md b/ERCS/erc-7741.md new file mode 100644 index 0000000000..7c7c6f57d7 --- /dev/null +++ b/ERCS/erc-7741.md @@ -0,0 +1,211 @@ +--- +eip: 7741 +title: Authorize Operator +description: Set Operator via EIP-712 secp256k1 signatures +author: Jeroen Offerijns (@hieronx), Joรฃo Martins (@0xTimepunk) +discussions-to: https://ethereum-magicians.org/t/erc-7741-authorize-operator/20531 +status: Draft +type: Standards Track +category: ERC +created: 2024-06-03 +requires: 712, 1271 +--- + +## Abstract + +A set of functions to enable meta-transactions and atomic interactions with contracts implementing an operator model, via signatures conforming to the [EIP-712](./eip-712.md) typed message signing specification. + +## Motivation + +The primary motivation for this standard is to enhance the flexibility, security, and efficiency of operator management. By leveraging EIP-712 signatures, this standard allows users to authorize operators without the need for on-chain transactions, reducing gas costs and improving user experience. This is particularly beneficial whenever frequent operator changes and cross-chain interactions are required. + +Additionally, this standard aims to: + +1. **Enable Meta-Transactions**: Allow users to delegate the execution of transactions to operators, enabling meta-transactions where the user does not need to hold native tokens to pay for gas fees on each chain. +2. **Improve Security**: Utilize the EIP-712 standard for typed data signing, which provides a more secure and user-friendly way to sign messages compared to raw data signing. +3. **Facilitate Interoperability**: Provide a standardized interface for operator management that can be adopted across various vault protocols, promoting interoperability and reducing integration complexity for developers. +4. **Streamline Cross-Chain Operations**: Simplify the process of managing operators across different chains, making it easier for protocols to maintain consistent operator permissions and interactions in a multi-chain environment. + +By addressing these needs, the `Authorize Operator` standard aims to streamline the process of managing operators in decentralized vault protocols, making it easier for users and developers to interact with smart contracts in a secure, cost-effective, and interoperable manner across multiple blockchain networks. + +## Specification + +### Operator-compatible contracts + +This signed authorization scheme applies to any contracts implementing the following interface: + +```solidity + interface IOperator { + event OperatorSet(address indexed owner, address indexed operator, bool approved); + + function setOperator(address operator, bool approved) external returns (bool); + function isOperator(address owner, address operator) external returns (bool status); + } +``` + +[EIP-6909](./eip-6909.md) and [EIP-7540](./eip-7540.md) already implement this interface. + +The naming of the arguments is interchangeable, e.g. [EIP-6909](./eip-6909.md) uses `spender` instead of `operator`. + +### Methods + +#### `authorizeOperator` + +Grants or revokes permissions for `operator` to manage Requests on behalf of the `msg.sender`, using an [EIP-712](./eip-712.md) signature. + +MUST revert if the `deadline` has passed. + +MUST invalidate the nonce of the signature to prevent message replay. + +MUST revert if the `signature` is not a valid [EIP-712](./eip-712.md) signature, with the given input parameters. + +MUST set the operator status to the `approved` value. + +MUST log the `OperatorSet` event. + +MUST return `true`. + +```yaml +- name: authorizeOperator + type: function + stateMutability: nonpayable + + inputs: + - name: owner + type: address + - name: operator + type: address + - name: approved + type: bool + - name: deadline + type: uint256 + - name: nonce + type: bytes32 + - name: signature + type: bytes + + outputs: + - name: success + type: bool +``` + +#### `invalidateNonce` + +Revokes the given `nonce` for `msg.sender` as the `owner`. + +```yaml +- name: invalidateNonce + type: function + stateMutability: nonpayable + + inputs: + - name: nonce + type: bytes32 +``` + +#### `authorizations` + +Returns whether the given `nonce` has been used for the `controller`. + +```yaml +- name: authorizations + type: function + stateMutability: nonpayable + + inputs: + - name: controller + type: address + - name: nonce + type: bytes32 + outputs: + - name: used + type: bool +``` + +#### `DOMAIN_SEPARATOR` + +Returns the `DOMAIN_SEPARATOR` as defined according to EIP-712. The `DOMAIN_SEPARATOR` should be unique to the contract and chain to prevent replay attacks from other domains, and satisfy the requirements of EIP-712, but is otherwise unconstrained. + +```yaml +- name: DOMAIN_SEPARATOR + type: function + stateMutability: nonpayable + + outputs: + - type: bytes32 +``` + +### [ERC-165](./eip-165.md) support + +Smart contracts implementing this standard MUST implement the [ERC-165](./eip-165.md) `supportsInterface` function. + +Contracts MUST return the constant value `true` if `0x7a7911eb` is passed through the `interfaceID` argument. + +## Rationale + +### Similarity to [ERC-2612](./eip-2612.md) + +The specification is intentionally designed to closely match [ERC-2612](./eip-2612.md). This should simplify new integrations of the standard. + +The main difference is using `bytes32` vs `uint256`, which enables unordered nonces. + +## Reference Implementation + +```solidity + // This code snippet is incomplete pseudocode used for example only and is no way intended to be used in production or guaranteed to be secure + + bytes32 public constant AUTHORIZE_OPERATOR_TYPEHASH = + keccak256("AuthorizeOperator(address controller,address operator,bool approved,uint256 deadline,bytes32 nonce)"); + + mapping(address owner => mapping(bytes32 nonce => bool used)) authorizations; + + function DOMAIN_SEPARATOR() public view returns (bytes32) { + // EIP-712 implementation + } + + function isValidSignature(address signer, bytes32 digest, bytes memory signature) internal view returns (bool valid) { + // ERC-1271 implementation + } + + function authorizeOperator( + address controller, + address operator, + bool approved, + uint256 deadline, + bytes32 nonce, + bytes memory signature + ) external returns (bool success) { + require(block.timestamp <= deadline, "ERC7540Vault/expired"); + require(controller != address(0), "ERC7540Vault/invalid-controller"); + require(!authorizations[controller][nonce], "ERC7540Vault/authorization-used"); + + authorizations[controller][nonce] = true; + + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR(), + keccak256(abi.encode(AUTHORIZE_OPERATOR_TYPEHASH, controller, operator, approved, deadline, nonce)) + ) + ); + + require(SignatureLib.isValidSignature(controller, digest, signature), "ERC7540Vault/invalid-authorization"); + + isOperator[controller][operator] = approved; + emit OperatorSet(controller, operator, approved); + + success = true; + } + + function invalidateNonce(bytes32 nonce) external { + authorizations[msg.sender][nonce] = true; + } +``` + +## Security Considerations + +Operators have significant control over users and the signed message can lead to undesired outcomes. The expiration date should be set as short as feasible to reduce the chance of an unused signature leaking at a later point. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From fbdc10f87ebe572f7129b6263430d59910d7edf6 Mon Sep 17 00:00:00 2001 From: ligi Date: Tue, 23 Jul 2024 16:44:49 +0200 Subject: [PATCH 075/126] Update ERC-3770: Move to Draft Merged by EIP-Bot. --- ERCS/erc-3770.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-3770.md b/ERCS/erc-3770.md index 41334680d0..fbed54b7c5 100644 --- a/ERCS/erc-3770.md +++ b/ERCS/erc-3770.md @@ -4,7 +4,7 @@ title: Chain-specific addresses description: Prepending chain-specific addresses with a human-readable chain identifier author: Lukas Schor (@lukasschor), Richard Meissner (@rmeissner), Pedro Gomes (@pedrouid), ligi discussions-to: https://ethereum-magicians.org/t/chain-specific-addresses/6449 -status: Stagnant +status: Draft type: Standards Track category: ERC created: 2021-08-26 @@ -58,7 +58,7 @@ Ethereum addresses without the chain specifier will continue to require addition ## Security Considerations -The Ethereum List curators must consider how similar looking chain short names can be used to confuse users. +Similar looking chain short names can be used to confuse users. ## Copyright From 5ab0970a130ff07ce4dacee570a0c5b25eb90c35 Mon Sep 17 00:00:00 2001 From: "J.D. Bertron" Date: Tue, 23 Jul 2024 08:52:42 -0600 Subject: [PATCH 076/126] Update ERC-5630: Move to Draft Merged by EIP-Bot. --- ERCS/erc-5630.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-5630.md b/ERCS/erc-5630.md index 70003c83c2..45cea4e9d3 100644 --- a/ERCS/erc-5630.md +++ b/ERCS/erc-5630.md @@ -4,7 +4,7 @@ title: New approach for encryption / decryption description: defines a specification for encryption and decryption using Ethereum wallets. author: Firn Protocol (@firnprotocol), Fried L. Trout, Weiji Guo (@weijiguo) discussions-to: https://ethereum-magicians.org/t/eip-5630-encryption-and-decryption/10761 -status: Stagnant +status: Draft type: Standards Track category: ERC created: 2022-09-07 From c05baa7d0b1a1961ec7f943a00b8358736ce1e7a Mon Sep 17 00:00:00 2001 From: Peter Kohl-Landgraf <32535675+pekola@users.noreply.github.com> Date: Tue, 30 Jul 2024 15:49:08 +0200 Subject: [PATCH 077/126] Website: Update ERC-6123 - Streamlined Reference Implementation, added Unit Tests Merged by EIP-Bot. --- ERCS/erc-6123.md | 17 +- assets/erc-6123/contracts/ERC20Settlement.sol | 37 +- assets/erc-6123/contracts/ISDC.sol | 61 ++-- assets/erc-6123/contracts/SDC.sol | 168 ++++++---- .../erc-6123/contracts/SDCPledgedBalance.sol | 201 +++++------ assets/erc-6123/doc/sequence.puml | 4 +- assets/erc-6123/package.json | 2 +- assets/erc-6123/test/SDCTests.js | 316 +++++++++++++----- 8 files changed, 500 insertions(+), 306 deletions(-) diff --git a/ERCS/erc-6123.md b/ERCS/erc-6123.md index 6625fe55fa..591bd4dcef 100644 --- a/ERCS/erc-6123.md +++ b/ERCS/erc-6123.md @@ -65,7 +65,7 @@ The following methods specify a Smart Derivative Contract's trade initiation and A party can initiate a trade by providing the party address to trade with, trade data, trade position, payment amount for the trade and initial settlement data. Only registered counterparties are allowed to use that function. ```solidity -function inceptTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; +function inceptTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external; ``` #### Trade Initiation Phase: `confirmTrade` @@ -73,7 +73,7 @@ function inceptTrade(address _withParty, string memory _tradeData, int _position A counterparty can confirm a trade by providing its trade specification data, which then gets matched against the data stored from `inceptTrade` call. ```solidity -function confirmTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; +function confirmTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external; ``` #### Trade Initiation Phase: `cancelTrade` @@ -81,7 +81,7 @@ function confirmTrade(address _withParty, string memory _tradeData, int _positio The counterparty that called `inceptTrade` has the option to cancel the trade, e.g., in the case where the trade is not confirmed in a timely manner. ```solidity -function cancelTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; +function cancelTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external; ``` #### Trade Settlement Phase: `initiateSettlement` @@ -106,18 +106,19 @@ function performSettlement(int256 settlementAmount, string memory settlementData This method - either called back from the provided settlement token directly or from an eligible address - completes the settlement transfer. This might result in a termination or start of the next settlement phase, depending on the provided success flag. +The transactionData is emitted as part of the corresponding event: `TradeSettled` or `TradeTerminated` ```solidity -function afterTransfer(uint256 transactionHash, bool success) external; +function afterTransfer(bool success, uint256 transactionData) external; ``` #### Trade Termination: `requestTermination` -Allows an eligible party to request a mutual termination with a termination amount she is willing to pay and provide further termination terms (e.g. an XML) +Allows an eligible party to request a mutual termination of the trade with the correspondig `tradeData` with a termination amount she is willing to pay and provide further termination terms (e.g. an XML) ```solidity -function requestTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; +function requestTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; ``` #### Trade Termination: `confirmTradeTermination` @@ -125,7 +126,7 @@ function requestTradeTermination(string memory tradeId, int256 _terminationPayme Allows an eligible party to confirm a previously requested (mutual) trade termination, including termination payment value and termination terms ```solidity -function confirmTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; +function confirmTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; ``` #### Trade Termination: `cancelTradeTermination` @@ -133,7 +134,7 @@ function confirmTradeTermination(string memory tradeId, int256 _terminationPayme The party that initiated `requestTradeTermination` has the option to withdraw the request, e.g., in the case where the termination is not confirmed in a timely manner. ```solidity -function cancelTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; +function cancelTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; ``` ### Trade Events diff --git a/assets/erc-6123/contracts/ERC20Settlement.sol b/assets/erc-6123/contracts/ERC20Settlement.sol index c6fedc9e5f..5aedb06d25 100644 --- a/assets/erc-6123/contracts/ERC20Settlement.sol +++ b/assets/erc-6123/contracts/ERC20Settlement.sol @@ -38,25 +38,14 @@ contract ERC20Settlement is ERC20, IERC20Settlement{ } function checkedTransfer(address to, uint256 value, uint256 transactionID) public onlySDC{ - try this.transfer(to,value) returns (bool transferSuccessFlag) { - ISDC(sdcAddress).afterTransfer(transactionID, transferSuccessFlag); - } - catch{ - ISDC(sdcAddress).afterTransfer(transactionID, false); - } + if ( balanceOf(sdcAddress) < value) + ISDC(sdcAddress).afterTransfer(false, transactionID); + else + ISDC(sdcAddress).afterTransfer(true, transactionID); } - function checkedTransferFrom(address from, address to, uint256 value, uint256 transactionID) external onlySDC { - // TODO: Bug - reason="Error: Transaction reverted: contract call run out of gas and made the transaction revert", method="estimateGas", - if (this.balanceOf(from)< value || this.allowance(from,address(msg.sender)) < value ) - ISDC(sdcAddress).afterTransfer(transactionID, false); - try this.transfer(to,value) returns (bool transferSuccessFlag) { - ISDC(sdcAddress).afterTransfer(transactionID, transferSuccessFlag); - } - catch{ - ISDC(sdcAddress).afterTransfer(transactionID, false); - } - // address owner = _msgSender(); // currently not used + function checkedTransferFrom(address from, address to, uint256 value, uint256 transactionID) external view onlySDC { + revert("not implemented"); } function checkedBatchTransfer(address[] memory to, uint256[] memory values, uint256 transactionID ) public onlySDC{ @@ -65,14 +54,14 @@ contract ERC20Settlement is ERC20, IERC20Settlement{ for(uint256 i = 0; i < values.length; i++) requiredBalance += values[i]; if (balanceOf(msg.sender) < requiredBalance){ - ISDC(sdcAddress).afterTransfer(transactionID, false); + ISDC(sdcAddress).afterTransfer(false, transactionID); return; } else{ for(uint256 i = 0; i < to.length; i++){ - transfer(to[i],values[i]); + _transfer(sdcAddress,to[i],values[i]); } - ISDC(sdcAddress).afterTransfer(transactionID, true); + ISDC(sdcAddress).afterTransfer(true, transactionID); } } @@ -88,15 +77,15 @@ contract ERC20Settlement is ERC20, IERC20Settlement{ totalRequiredBalance += values[j]; } if (balanceOf(fromAddress) < totalRequiredBalance){ - ISDC(sdcAddress).afterTransfer(transactionID, false); - break; + ISDC(sdcAddress).afterTransfer(false, transactionID); + return; } } for(uint256 i = 0; i < to.length; i++){ - transferFrom(from[i],to[i],values[i]); + _transfer(from[i],to[i],values[i]); } - ISDC(sdcAddress).afterTransfer(transactionID, true); + ISDC(sdcAddress).afterTransfer(true, transactionID); } } \ No newline at end of file diff --git a/assets/erc-6123/contracts/ISDC.sol b/assets/erc-6123/contracts/ISDC.sol index 88804d1fc4..e82b4d3a8c 100644 --- a/assets/erc-6123/contracts/ISDC.sol +++ b/assets/erc-6123/contracts/ISDC.sol @@ -93,7 +93,7 @@ interface ISDC { /** * @dev Emitted when an active trade is terminated - * @param cause string holding the cause of the termination + * @param cause string holding data associated with the termination, e.g. transactionData upon a failed transaction */ event TradeTerminated(string cause); @@ -105,7 +105,7 @@ interface ISDC { /** * @dev Emitted when settlement process has been finished */ - event TradeSettled(); + event TradeSettled(string transactionData); /** * @dev Emitted when a settlement gets requested @@ -152,35 +152,35 @@ interface ISDC { /** * @notice Incepts a trade, stores trade data * @dev emits a {TradeIncepted} event - * @param _withParty is the party the inceptor wants to trade with - * @param _tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml - * @param _position is the position the inceptor has in that trade - * @param _paymentAmount is the payment amount which can be positive or negative (viewed from the inceptor) - * @param _initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) + * @param withParty is the party the inceptor wants to trade with + * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param position is the position the inceptor has in that trade + * @param paymentAmount is the payment amount which can be positive or negative (viewed from the inceptor) + * @param initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) */ - function inceptTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; + function inceptTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external; /** * @notice Performs a matching of provided trade data and settlement data of a previous trade inception * @dev emits a {TradeConfirmed} event if trade data match - * @param _withParty is the party the confirmer wants to trade with - * @param _tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml - * @param _position is the position the confirmer has in that trade (negative of the position the inceptor has in the trade) - * @param _paymentAmount is the payment amount which can be positive or negative (viewed from the confirmer, negative of the inceptor's view) - * @param _initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) + * @param withParty is the party the confirmer wants to trade with + * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param position is the position the confirmer has in that trade (negative of the position the inceptor has in the trade) + * @param paymentAmount is the payment amount which can be positive or negative (viewed from the confirmer, negative of the inceptor's view) + * @param initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) */ - function confirmTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; + function confirmTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external; /** * @notice Performs a matching of provided trade data and settlement data of a previous trade inception. Required to be called by inceptor. * @dev emits a {TradeCanceled} event if trade data match and msg.sender agrees with the party that incepted the trade. - * @param _withParty is the party the inceptor wants to trade with - * @param _tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml - * @param _position is the position the inceptor has in that trade - * @param _paymentAmount is the payment amount which can be positive or negative (viewed from the inceptor) - * @param _initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) + * @param withParty is the party the inceptor wants to trade with + * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param position is the position the inceptor has in that trade + * @param paymentAmount is the payment amount which can be positive or negative (viewed from the inceptor) + * @param initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) */ - function cancelTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external; + function cancelTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external; /// Settlement Cycle: Settlement @@ -202,9 +202,10 @@ interface ISDC { /** * @notice May get called from outside to to finish a transfer (callback). The trade decides on how to proceed based on success flag * @param success tells the protocol whether transfer was successful + * @param transactionData data associtated with the transfer, will be emitted via the events. * @dev may emit a {TradeSettled} event or a {TradeTerminated} event */ - function afterTransfer(uint256 transactionHash, bool success) external; + function afterTransfer(bool success, uint256 transactionData) external; /// Trade termination @@ -212,25 +213,27 @@ interface ISDC { /** * @notice Called from a counterparty to request a mutual termination * @dev emits a {TradeTerminationRequest} - * @param tradeId the trade identifier which is supposed to be terminated - * @param terminationTerms the termination terms + * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param terminationPayment an agreed termination amount (viewed from the requester) + * @param terminationTerms the termination terms to be stored on chain. */ - function requestTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; + function requestTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; /** * @notice Called from a party to confirm an incepted termination, which might trigger a final settlement before trade gets closed * @dev emits a {TradeTerminationConfirmed} - * @param tradeId the trade identifier of the trade which is supposed to be terminated - * @param terminationTerms the termination terms + * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param terminationPayment an agreed termination amount (viewed from the confirmer, negative of the value provided by the requester) + * @param terminationTerms the termination terms to be stored on chain. */ - function confirmTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; + function confirmTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; /** * @notice Called from a party to confirm an incepted termination, which might trigger a final settlement before trade gets closed * @dev emits a {TradeTerminationConfirmed} - * @param tradeId the trade identifier of the trade which is supposed to be terminated + * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml * @param terminationTerms the termination terms */ - function cancelTradeTermination(string memory tradeId, int256 _terminationPayment, string memory terminationTerms) external; + function cancelTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; } diff --git a/assets/erc-6123/contracts/SDC.sol b/assets/erc-6123/contracts/SDC.sol index 7d61ef9a41..71c3eba737 100644 --- a/assets/erc-6123/contracts/SDC.sol +++ b/assets/erc-6123/contracts/SDC.sol @@ -41,20 +41,24 @@ abstract contract SDC is ISDC { Confirmed, /* - * Valuation Phase + * Valuation Phase: The contract is awaiting a valuation for the next settlement. */ Valuation, /* - * A Token-based Transfer is in Progress + * Token-based Transfer is in Progress. Contracts awaits termination of token transfer (allows async transfers). */ InTransfer, /* - * Settlement is Completed + * Settlement is Completed. */ Settled, + /* + * Termination is in Progress. + */ + InTermination, /* * Terminated. */ @@ -65,29 +69,32 @@ abstract contract SDC is ISDC { * Modifiers serve as guards whether at a specific process state a specific function can be called */ - modifier onlyWhenTradeInactive() { require(tradeState == TradeState.Inactive, "Trade state is not 'Inactive'."); _; } + modifier onlyWhenTradeIncepted() { require(tradeState == TradeState.Incepted, "Trade state is not 'Incepted'."); _; } + modifier onlyWhenSettled() { require(tradeState == TradeState.Settled, "Trade state is not 'Settled'."); _; } + modifier onlyWhenValuation() { require(tradeState == TradeState.Valuation, "Trade state is not 'Valuation'."); _; } - modifier onlyWhenInTransfer() { - require(tradeState == TradeState.InTransfer, "Trade state is not 'InTransfer'."); _; - } - TradeState internal tradeState; + modifier onlyWhenInTermination () { + require(tradeState == TradeState.InTermination, "Trade state is not 'InTermination'."); _; + } modifier onlyCounterparty() { require(msg.sender == party1 || msg.sender == party2, "You are not a counterparty."); _; } + TradeState private tradeState; + address internal party1; address internal party2; address internal receivingParty; @@ -95,12 +102,8 @@ abstract contract SDC is ISDC { string internal tradeID; string internal tradeData; mapping(uint256 => address) internal pendingRequests; // Stores open request hashes for several requests: initiation, update and termination - bool internal mutuallyTerminated = false; int256 terminationPayment; - - int256[] internal settlementAmounts; - string[] internal settlementData; - + int256 upfrontPayment; /* * SettlementToken holds: @@ -116,12 +119,15 @@ abstract contract SDC is ISDC { address _party2, address _settlementToken ) { + terminationPayment = 0; + upfrontPayment = 0; party1 = _party1; party2 = _party2; settlementToken = ERC20Settlement(_settlementToken); settlementToken.setSDCAddress(address(this)); tradeState = TradeState.Inactive; } + /* * generates a hash from tradeData and generates a map entry in openRequests * emits a TradeIncepted @@ -134,6 +140,7 @@ abstract contract SDC is ISDC { uint256 transactionHash = uint256(keccak256(abi.encode(msg.sender,_withParty,_tradeData,_position, _paymentAmount,_initialSettlementData))); pendingRequests[transactionHash] = msg.sender; receivingParty = _position == 1 ? msg.sender : _withParty; + upfrontPayment = _position == 1 ? _paymentAmount : -_paymentAmount; // upfrontPayment is saved with view on the receiving party tradeID = Strings.toString(transactionHash); tradeData = _tradeData; // Set trade data to enable querying already in inception state emit TradeIncepted(msg.sender, tradeID, _tradeData); @@ -152,18 +159,9 @@ abstract contract SDC is ISDC { delete pendingRequests[transactionHash]; // Delete Pending Request tradeState = TradeState.Confirmed; emit TradeConfirmed(msg.sender, tradeID); - address upfrontPayer; - if (_position==1 && _paymentAmount < 0) // payment amount negative means from a long position : party has to pay - upfrontPayer = msg.sender; - else if (_position==1 && _paymentAmount > 0) - upfrontPayer = _withParty; - else if (_position==-1 && _paymentAmount < 0) // payment amount negative means from a short position : party has to pay - upfrontPayer = msg.sender; - else - upfrontPayer = _withParty; - settlementData.push(_initialSettlementData); - uint256 absPaymentAmount = uint256(abs(_paymentAmount)); - processTradeAfterConfirmation(upfrontPayer, absPaymentAmount); + address upfrontPayer = upfrontPayment > 0 ? otherParty(receivingParty) : receivingParty; + uint256 upfrontTransferAmount = uint256(abs(_paymentAmount)); + processTradeAfterConfirmation(upfrontPayer, upfrontTransferAmount,_initialSettlementData); } /* @@ -191,8 +189,8 @@ abstract contract SDC is ISDC { require(keccak256(abi.encodePacked(tradeID)) == keccak256(abi.encodePacked(_tradeId)), "Trade ID mismatch"); uint256 hash = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment, terminationTerms))); pendingRequests[hash] = msg.sender; - - emit TradeTerminationRequest(msg.sender, _tradeId, _terminationPayment, terminationTerms); + terminationPayment = _terminationPayment; // termination payment will be provided in view of receiving party + emit TradeTerminationRequest(msg.sender, _tradeId, terminationPayment, terminationTerms); } /* @@ -205,13 +203,13 @@ abstract contract SDC is ISDC { uint256 hashConfirm = uint256(keccak256(abi.encode(_tradeId, "terminate", -_terminationPayment, terminationTerms))); require(pendingRequests[hashConfirm] == pendingRequestParty, "Confirmation of termination failed due to wrong party or missing request"); delete pendingRequests[hashConfirm]; - mutuallyTerminated = true; - terminationPayment = _terminationPayment; - emit TradeTerminationConfirmed(msg.sender, _tradeId, -_terminationPayment, terminationTerms); - /* Trigger final Settlement */ - address initiator = msg.sender; - tradeState = TradeState.Valuation; - emit TradeSettlementRequest(initiator, tradeData, settlementData[settlementData.length - 1]); + emit TradeTerminationConfirmed(msg.sender, _tradeId, terminationPayment, terminationTerms); + /* Trigger Termination Payment Amount */ + address payerAddress = terminationPayment > 0 ? otherParty(receivingParty) : receivingParty; + uint256 absPaymentAmount = uint256(abs(_terminationPayment)); + setTradeState(TradeState.InTermination); + processTradeAfterMutualTermination(payerAddress,absPaymentAmount,terminationTerms); + } /* @@ -227,49 +225,63 @@ abstract contract SDC is ISDC { emit TradeTerminationCanceled(msg.sender, _tradeId, terminationTerms); } - function processTradeAfterConfirmation(address upfrontPayer, uint256 upfrontPayment) virtual internal; - /* - * Utilities - */ + * Booking of the upfrontPayment and implementation specific setups of margin buffers / wallets. + */ + function processTradeAfterConfirmation(address upfrontPayer, uint256 upfrontPayment, string memory initialSettlementData) virtual internal; - /** - * Absolute value of an integer + /* + * Booking of the terminationAmount and implementation specific cleanup of margin buffers / wallets. */ - function abs(int x) internal pure returns (int256) { - return x >= 0 ? x : -x; - } + function processTradeAfterMutualTermination(address terminationFeePayer, uint256 terminationAmount, string memory terminationData) virtual internal; - /** - * Maximum value of two integers + /* + * Management of Trade States */ - function max(int a, int b) internal pure returns (int256) { - return a > b ? a : b; + function inStateIncepted() public view returns (bool) { return tradeState == TradeState.Incepted; } + function inStateConfirmed() public view returns (bool) { return tradeState == TradeState.Confirmed; } + function inStateSettled() public view returns (bool) { return tradeState == TradeState.Settled; } + function inStateTransfer() public view returns (bool) { return tradeState == TradeState.InTransfer; } + function inStateTermination() public view returns (bool) { return tradeState == TradeState.InTermination; } + function inStateTerminated() public view returns (bool) { return tradeState == TradeState.Terminated; } + + function getTradeState() public view returns (TradeState) { + return tradeState; } - /** - * Minimum value of two integers - */ - function min(int a, int b) internal pure returns (int256) { - return a < b ? a : b; + function setTradeState(TradeState newState) internal { + if ( newState == TradeState.Incepted && tradeState != TradeState.Inactive) + revert("Provided Trade state is not allowed"); + if ( newState == TradeState.Confirmed && tradeState != TradeState.Incepted) + revert("Provided Trade state is not allowed"); + if ( newState == TradeState.InTransfer && !(tradeState == TradeState.Confirmed || tradeState == TradeState.Valuation) ) + revert("Provided Trade state is not allowed"); + if ( newState == TradeState.Valuation && tradeState != TradeState.Settled) + revert("Provided Trade state is not allowed"); + if ( newState == TradeState.InTermination && !(tradeState == TradeState.InTransfer || tradeState == TradeState.Settled ) ) + revert("Provided Trade state is not allowed"); + tradeState = newState; } + /* + * Upfront and termination payments. + */ - function getTokenAddress() public view returns(address) { - return address(settlementToken); + function getReceivingParty() public view returns (address) { + return receivingParty; } - function getTradeState() public view returns (TradeState) { - return tradeState; + function getUpfrontPayment() public view returns (int) { + return upfrontPayment; } - /** - * Other party - */ - function otherParty(address party) internal view returns (address) { - return (party == party1 ? party2 : party1); + function getTerminationPayment() public view returns (int) { + return terminationPayment; } + /* + * Trade Specification (ID, Token, Data) + */ function getTradeID() public view returns (string memory) { return tradeID; @@ -279,9 +291,43 @@ abstract contract SDC is ISDC { tradeID= _tradeID; } + function getTokenAddress() public view returns(address) { + return address(settlementToken); + } + function getTradeData() public view returns (string memory) { return tradeData; } + /* + * Utilities (internal) + */ + + /** + * Other party + */ + function otherParty(address party) internal view returns (address) { + return (party == party1 ? party2 : party1); + } + /** + * Maximum value of two integers + */ + function max(int a, int b) internal pure returns (int256) { + return a > b ? a : b; + } + + /** + * Minimum value of two integers + */ + function min(int a, int b) internal pure returns (int256) { + return a < b ? a : b; + } + + /** + * Absolute value of an integer + */ + function abs(int x) internal pure returns (int256) { + return x >= 0 ? x : -x; + } } \ No newline at end of file diff --git a/assets/erc-6123/contracts/SDCPledgedBalance.sol b/assets/erc-6123/contracts/SDCPledgedBalance.sol index 17bd7edfe0..357ba86f1a 100644 --- a/assets/erc-6123/contracts/SDCPledgedBalance.sol +++ b/assets/erc-6123/contracts/SDCPledgedBalance.sol @@ -37,50 +37,29 @@ import "./ERC20Settlement.sol"; contract SDCPledgedBalance is SDC { + struct MarginRequirement { uint256 buffer; uint256 terminationFee; } - mapping(address => MarginRequirement) private marginRequirements; // Storage of M and P per counterparty address + int256[] private settlementAmounts; + string[] private settlementData; + constructor( address _party1, address _party2, address _settlementToken, - uint256 _initialBuffer, // m - uint256 _initalTerminationFee // p + uint256 _initialBuffer, // m + uint256 _initalTerminationFee // p ) SDC(_party1,_party2,_settlementToken) { marginRequirements[party1] = MarginRequirement(_initialBuffer, _initalTerminationFee); marginRequirements[party2] = MarginRequirement(_initialBuffer, _initalTerminationFee); } - function processTradeAfterConfirmation(address upfrontPayer, uint256 upfrontPayment) override internal{ - uint256 marginRequirementParty1 = uint(marginRequirements[party1].buffer + marginRequirements[party1].terminationFee ); - uint256 marginRequirementParty2 = uint(marginRequirements[party2].buffer + marginRequirements[party2].terminationFee ); - uint256 requiredBalanceParty1 = marginRequirementParty1 + (upfrontPayer==party1 ? upfrontPayment : 0); - uint256 requiredBalanceParty2 = marginRequirementParty2 + (upfrontPayer==party2 ? upfrontPayment : 0); - bool isAvailableParty1 = (settlementToken.balanceOf(party1) >= requiredBalanceParty1) && (settlementToken.allowance(party1, address(this)) >= requiredBalanceParty1); - bool isAvailableParty2 = (settlementToken.balanceOf(party2) >= requiredBalanceParty2) && (settlementToken.allowance(party2, address(this)) >= requiredBalanceParty2); - if (isAvailableParty1 && isAvailableParty2){ // Pre-Conditions: M + P needs to be locked (i.e. pledged) - address[] memory from = new address[](3); - address[] memory to = new address[](3); - uint256[] memory amounts = new uint256[](3); - from[0] = party1; to[0] = address(this); amounts[0] = marginRequirementParty1; - from[1] = party2; to[1] = address(this); amounts[1] = marginRequirementParty2; - from[2] = upfrontPayer; to[2] = otherParty(upfrontPayer); amounts[2] = upfrontPayment; - uint256 transactionID = uint256(keccak256(abi.encodePacked(from,to,amounts))); - tradeState = TradeState.InTransfer; - settlementToken.checkedBatchTransferFrom(from,to,amounts,transactionID); // Atomic Transfer - } - else { - tradeState = TradeState.Inactive; - emit TradeTerminated("Insufficient Balance or Allowance"); - } - } - /* * Settlement can be initiated when margin accounts are locked, a valuation request event is emitted containing tradeData and valuationViewParty * Changes Process State to Valuation&Settlement @@ -88,7 +67,7 @@ contract SDCPledgedBalance is SDC { */ function initiateSettlement() external override onlyCounterparty onlyWhenSettled { address initiator = msg.sender; - tradeState = TradeState.Valuation; + setTradeState(TradeState.Valuation); emit TradeSettlementRequest(initiator, tradeData, settlementData[settlementData.length - 1]); } @@ -98,92 +77,124 @@ contract SDCPledgedBalance is SDC { * Checks Settlement amount according to valuationViewParty: If SettlementAmount is > 0, valuationViewParty receives * can be called only when ProcessState = ValuationAndSettlement */ - function performSettlement(int256 settlementAmount, string memory _settlementData) onlyWhenValuation external override { - - if (mutuallyTerminated){ - settlementAmount = settlementAmount + terminationPayment; - } - + (address settlementPayer,uint256 transferAmount) = determineTransferAmountAndPayerAddress(settlementAmount); + int cappedSettlementAmount = settlementPayer == receivingParty ? -int256(transferAmount) : int256(transferAmount); settlementData.push(_settlementData); - settlementAmounts.push(settlementAmount); + settlementAmounts.push(cappedSettlementAmount); // save the capped settlement amount + uint256 transactionID = uint256(keccak256(abi.encodePacked(settlementPayer,otherParty(settlementPayer), transferAmount, block.timestamp))); + address[] memory from = new address[](1); + address[] memory to = new address[](1); + uint256[] memory amounts = new uint256[](1); + from[0] = settlementPayer; to[0] = otherParty(settlementPayer); amounts[0] = transferAmount; + emit TradeSettlementPhase(); + setTradeState(TradeState.InTransfer); + settlementToken.checkedBatchTransferFrom(from,to,amounts,transactionID); + } - uint256 transferAmount; - address settlementPayer; - (settlementPayer, transferAmount) = determineTransferAmountAndPayerAddress(settlementAmount); - - if (settlementToken.balanceOf(settlementPayer) >= transferAmount && - settlementToken.allowance(settlementPayer,address(this)) >= transferAmount) { /* Good case: Balances are sufficient and token has enough approval */ - uint256 transactionID = uint256(keccak256(abi.encodePacked(settlementPayer,otherParty(settlementPayer), transferAmount))); - emit TradeSettlementPhase(); - tradeState = TradeState.InTransfer; - address[] memory from = new address[](1); - address[] memory to = new address[](1); - uint256[] memory amounts = new uint256[](1); - from[0] = settlementPayer; to[0] = otherParty(settlementPayer); amounts[0] = transferAmount; - tradeState = TradeState.InTransfer; - settlementToken.checkedBatchTransferFrom(from,to,amounts,transactionID); + /* + * afterTransfer processes SDC depending on success of the respective payment and depending on the current trade state + * Good Case: state will be settled, failed settlement will trigger the pledge balance transfer and termination + */ + function afterTransfer(bool success, uint256 transactionHash) external override { + if ( inStateConfirmed()){ + if (success){ + setTradeState(TradeState.Settled); + emit TradeActivated(getTradeID()); + } + else{ + setTradeState(TradeState.Terminated); + emit TradeTerminated("Upfront Transfer Failure"); + } } - else { /* Bad Case: Process termination by booking from own balance */ - tradeState = TradeState.InTransfer; - _processAfterTransfer(false); + else if ( inStateTransfer() ){ + if (success){ + setTradeState(TradeState.Settled); + emit TradeSettled("Settlement Settled - Pledge Transfer"); + } + else{ // Settlement & Pledge Case: transferAmount is transferred from SDC balance (i.e. pledged balance). + int256 settlementAmount = settlementAmounts[settlementAmounts.length-1]; + setTradeState(TradeState.InTermination); + processTerminationWithPledge(settlementAmount); + emit TradeTerminated("Settlement Failed - Pledge Transfer"); + } } + else if( inStateTermination() ){ + if (success){ + setTradeState(TradeState.Terminated); + emit TradeTerminated("Trade terminated sucessfully"); + } + else{ + emit TradeTerminated("Mutual Termination failed - Pledge Transfer"); + processTerminationWithPledge(getTerminationPayment()); + } + } + else + revert("Trade State does not allow to call 'afterTransfer'"); } + /* + * internal function which determines the capped settlement amount and poyer address + */ function determineTransferAmountAndPayerAddress(int256 settlementAmount) internal view returns(address, uint256) { address settlementReceiver = settlementAmount > 0 ? receivingParty : otherParty(receivingParty); address settlementPayer = otherParty(settlementReceiver); uint256 transferAmount; if (settlementAmount > 0) - transferAmount = uint256(abs(min( settlementAmount, int(marginRequirements[settlementPayer].buffer)))); + transferAmount = uint256(abs(min( settlementAmount, int256(marginRequirements[settlementPayer].buffer)))); else - transferAmount = uint256(abs(max( settlementAmount, -int(marginRequirements[settlementReceiver].buffer)))); + transferAmount = uint256(abs(max( settlementAmount, -int256(marginRequirements[settlementReceiver].buffer)))); return (settlementPayer,transferAmount); } - function afterTransfer(uint256 /* transactionHash */, bool success) external override onlyWhenInTransfer { - // Note: parameter transactionHash currenty unused - emit TradeSettled(); - _processAfterTransfer(success); + /* + * internal function which pepares the settlement tranfer after confirmation. + * Batched Transfer consists of Upfront Payment and Initial Prefunding to SDC Address + */ + + function processTradeAfterConfirmation(address upfrontPayer, uint256 upfrontPayment, string memory initialSettlementData) override internal{ + settlementAmounts.push(0); + settlementData.push(initialSettlementData); + address[] memory from = new address[](3); + address[] memory to = new address[](3); + uint256[] memory amounts = new uint256[](3); + from[0] = party1; to[0] = address(this); amounts[0] = uint(marginRequirements[party1].buffer + marginRequirements[party1].terminationFee ); + from[1] = party2; to[1] = address(this); amounts[1] = uint(marginRequirements[party2].buffer + marginRequirements[party2].terminationFee ); + from[2] = upfrontPayer; to[2] = otherParty(upfrontPayer); amounts[2] = upfrontPayment; + uint256 transactionID = uint256(keccak256(abi.encodePacked(from,to,amounts))); + settlementToken.checkedBatchTransferFrom(from,to,amounts,transactionID); // Batched Transfer } - function _processAfterTransfer(bool success) internal{ - if(success){ - emit TradeSettled(); - if (tradeState == TradeState.Terminated || mutuallyTerminated){ - tradeState = TradeState.Inactive; - } - else{ - tradeState = TradeState.Settled; - } - } - else{ // TRANSFER HAS FAILED - if (settlementData.length == 1){ // Case after confirmTrade where Transfer of upfront has failed - tradeState = TradeState.Inactive; - emit TradeTerminated("Initial Upfront Transfer fail - Trade Inactive"); - } - else{ - // Settlement & Pledge Case: transferAmount is transferred from SDC balance (i.e. pledged balance). - int256 settlementAmount = settlementAmounts[settlementAmounts.length-1]; - uint256 transferAmount; - address settlementPayer; - (settlementPayer, transferAmount) = determineTransferAmountAndPayerAddress(settlementAmount); - address settlementReceiver = otherParty(settlementPayer); - settlementToken.approve(settlementPayer,uint256(marginRequirements[settlementPayer].buffer - transferAmount)); // Release Buffers - settlementToken.approve(settlementReceiver,uint256(marginRequirements[settlementReceiver].buffer)); // Release Buffers - - // Do Pledge Transfer from own balances including termination fee - tradeState = TradeState.Terminated; - emit TradeTerminated("Trade terminated due to regular settlement failure"); - address[] memory to = new address[](2); - uint256[] memory amounts = new uint256[](2); - to[0] = settlementReceiver; amounts[0] = uint256(transferAmount); - to[1] = settlementReceiver; amounts[1] = uint256(marginRequirements[settlementPayer].terminationFee); - uint256 transactionID = uint256(keccak256(abi.encodePacked(to,amounts))); - settlementToken.checkedBatchTransfer(to,amounts,transactionID); - } - } + /* + * internal function which processes mutual termination, transfers termination payment and releases pledged balances from sdc address + */ + function processTradeAfterMutualTermination(address terminationFeePayer, uint256 terminationAmount, string memory terminationData) override internal{ + settlementAmounts.push(0); // termination payment is saved separately + settlementData.push(terminationData); + address[] memory from = new address[](3); + address[] memory to = new address[](3); + uint256[] memory amounts = new uint256[](3); + from[0] = address(this); to[0] = party1; amounts[0] = uint(marginRequirements[party1].buffer + marginRequirements[party1].terminationFee ); // Release buffers + from[1] = address(this); to[1] = party2; amounts[1] = uint(marginRequirements[party2].buffer + marginRequirements[party2].terminationFee ); // Release buffers + from[2] = terminationFeePayer; to[2] = otherParty(terminationFeePayer); amounts[2] = terminationAmount; + uint256 transactionID = uint256(keccak256(abi.encodePacked(from,to,amounts))); + settlementToken.checkedBatchTransferFrom(from,to,amounts,transactionID); // Batched Transfer } + + /* function which perfoms the "Pledged Booking" in case of failed settlement, transferring open settlement amount as well as termination fee from sdc's own balance + */ + function processTerminationWithPledge(int256 settlementAmount) internal{ + (address settlementPayer, uint256 transferAmount) = determineTransferAmountAndPayerAddress(settlementAmount); + address settlementReceiver = otherParty(settlementPayer); + address[] memory to = new address[](3); + uint256[] memory amounts = new uint256[](3); + to[0] = settlementReceiver; amounts[0] = transferAmount+marginRequirements[settlementPayer].terminationFee; // Settlement from Own Balance + to[1] = settlementReceiver; amounts[1] = marginRequirements[settlementReceiver].terminationFee + marginRequirements[settlementReceiver].buffer; // Release + to[2] = settlementPayer; amounts[2] = marginRequirements[settlementPayer].buffer-transferAmount; // Release of Buffer + uint256 transactionID = uint256(keccak256(abi.encodePacked(to,amounts))); + settlementToken.checkedBatchTransfer(to,amounts,transactionID); + } + } diff --git a/assets/erc-6123/doc/sequence.puml b/assets/erc-6123/doc/sequence.puml index 5ff19b5c7a..ea04005b0e 100644 --- a/assets/erc-6123/doc/sequence.puml +++ b/assets/erc-6123/doc/sequence.puml @@ -27,8 +27,6 @@ CP1 ->SettlementToken: allocate balances CP2 ->SettlementToken: allocate balances CP1 ->SDC: tx 'deploy' a SDC with token address activate SDC -CP1 ->SettlementToken: tx set 'allowance' for SDC address -CP2 ->SettlementToken: tx set 'allowance' for SDC address CP1 ->SDC: tx 'inceptTrade' SDC-->EventHandler: emit TradeIncepted @@ -71,7 +69,7 @@ else success else fail SDC->SettlementToken: tx 'transfer' Settlement Amount from SDC Balance to Receiving Party SDC->SettlementToken: tx 'transfer' Termination Fee from SDC Balance to Receiving Party - SDC->SettlementToken: tx 'approve' - Unlock remaing Party Balances + SDC->SettlementToken: tx 'transfer' - Release remainigBalances to parties == TradeState 'Terminated' == end diff --git a/assets/erc-6123/package.json b/assets/erc-6123/package.json index 24d9974fb7..19e85871df 100644 --- a/assets/erc-6123/package.json +++ b/assets/erc-6123/package.json @@ -1,6 +1,6 @@ { "name": "@finmath.net/sdc", - "version": "0.3.1", + "version": "0.4.0", "description": "Solidity Smart Derivative Contracts", "author": "Christian Fries, Peter Kohl-Landgraf, Alexandros Korpis", "license": "ISC", diff --git a/assets/erc-6123/test/SDCTests.js b/assets/erc-6123/test/SDCTests.js index b21ff704c4..dececef6c7 100644 --- a/assets/erc-6123/test/SDCTests.js +++ b/assets/erc-6123/test/SDCTests.js @@ -13,18 +13,19 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { Valuation: 3, InTransfer: 4, Settled: 5, - Terminated: 6 + InTermination: 6, + Terminated: 7 }; const abiCoder = new AbiCoder(); const trade_data = "here are the trade specification { counterparty2 = _counterparty2; ERC20Factory = await ethers.getContractFactory("ERC20Settlement"); SDCFactory = await ethers.getContractFactory("SDCPledgedBalance"); - token = await ERC20Factory.deploy(); - await token.deployed(); + //oken = await ERC20Factory.deploy(); + // await token.deployed(); }); - it("Initial minting and approvals for SDC", async () => { - await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); - await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); - }); - it("Counterparties incept and confirm a trade successfully, upfront is transferred", async () => { + + it("1. Counterparties incept and confirm a trade successfully, Upfront is transferred from CP1 to CP2", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); - await sdc.deployed(); -// console.log("SDC Address: %s", sdc.address); - await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); - await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount+upfront); let trade_id =""; - const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, -upfront, "initialMarketData"); await expect(incept_call).to.emit(sdc, "TradeIncepted"); - const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, -upfront, "initialMarketData"); + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, upfront, "initialMarketData"); await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); let trade_state = await sdc.connect(counterparty1).getTradeState(); await expect(trade_state).equal(TradeState.Settled); + let sdc_balance = await token.connect(counterparty1).balanceOf(sdc.address); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + console.log("Balance for SDC-Address: %s", sdc_balance); + await expect(sdc_balance).equal(2*(terminationFee+marginBufferAmount)); + await expect(cp1_balance).equal(initialLiquidityBalance-upfront-terminationFee-marginBufferAmount); + await expect(cp2_balance).equal(initialLiquidityBalance+upfront-terminationFee-marginBufferAmount); }); - it("Counterparty incepts and cancels trade successfully", async () => { + it("2a. CP1 is receiving party and pays initial Upfront (no buffers)", async () => { + let token = await ERC20Factory.deploy(); + let upfront1 = 150; + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,0,0); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, -upfront1, "initialMarketData"); + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, upfront1, "initialMarketData"); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + await expect(cp1_balance).equal(initialLiquidityBalance-upfront1); + await expect(cp2_balance).equal(initialLiquidityBalance+upfront1); + }); + + it("2b. CP1 is paying party and receives initial Upfront (no buffers)", async () => { + let token = await ERC20Factory.deploy(); + let upfront1 = 150; + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,0,0); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, -1, upfront1, "initialMarketData"); + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, 1, -upfront1, "initialMarketData"); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + await expect(cp1_balance).equal(initialLiquidityBalance+upfront1); + await expect(cp2_balance).equal(initialLiquidityBalance-upfront1); + }); + + it("2c. CP2 is paying party and pays initial Upfront (no buffers)", async () => { + let token = await ERC20Factory.deploy(); + let upfront1 = 150; + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,0,0); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront1, "initialMarketData"); + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, -upfront1, "initialMarketData"); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + await expect(cp1_balance).equal(initialLiquidityBalance+upfront1); + await expect(cp2_balance).equal(initialLiquidityBalance-upfront1); + }); + + it("3. Counterparty incepts and cancels trade successfully", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); - await sdc.deployed(); -// console.log("SDC Address: %s", sdc.address); - await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); - await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount+upfront); let trade_id =""; const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); await expect(incept_call).to.emit(sdc, "TradeIncepted"); @@ -80,87 +125,126 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { await expect(trade_state).equal(TradeState.Inactive); }); - it("Not enough approval to transfer upfront payment", async () => { - let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); - await sdc.deployed(); -// console.log("SDC Address: %s", sdc.address); - await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); - await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount); - const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); - await expect(incept_call).to.emit(sdc, "TradeIncepted"); - const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, -upfront, "initialMarketData"); - await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); - let trade_state = await sdc.connect(counterparty1).getTradeState(); - await expect(trade_state).equal(TradeState.Inactive); - }); + it("4. Not enough balance to transfer upfront payment", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); - it("Trade Matching fails", async () => { - let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); - await sdc.deployed(); -// console.log("SDC Address: %s", sdc.address); - await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); - await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount); - const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); - await expect(incept_call).to.emit(sdc, "TradeIncepted"); - const confirm_call = sdc.connect(counterparty2).confirmTrade(counterparty1.address, "none", -1, -upfront, "initialMarketData23"); - await expect(confirm_call).to.be.revertedWith("Confirmation fails due to inconsistent trade data or wrong party address"); - }); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, 2*initialLiquidityBalance, "initialMarketData"); + await expect(incept_call).to.emit(sdc, "TradeIncepted"); + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, -2*initialLiquidityBalance, "initialMarketData"); + await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); + let trade_state = await sdc.connect(counterparty1).getTradeState(); + await expect(trade_state).equal(TradeState.Terminated); + let sdc_balance = await token.connect(counterparty1).balanceOf(sdc.address); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + await expect(sdc_balance).equal(0); + await expect(cp1_balance).equal(initialLiquidityBalance); + await expect(cp2_balance).equal(initialLiquidityBalance); + }); - it("Trade cancellation fails due to wrong party calling cancel", async () => { - let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); - await sdc.deployed(); -// console.log("SDC Address: %s", sdc.address); - await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); - await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount); - const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); - await expect(incept_call).to.emit(sdc, "TradeIncepted"); - const confirm_call = sdc.connect(counterparty2).cancelTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); - await expect(confirm_call).to.be.revertedWith("Cancellation fails due to inconsistent trade data or wrong party address"); - }); + it("5. Trade Matching fails", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + await expect(incept_call).to.emit(sdc, "TradeIncepted"); + const confirm_call = sdc.connect(counterparty2).confirmTrade(counterparty1.address, "none", -1, -upfront, "initialMarketData23"); + await expect(confirm_call).to.be.revertedWith("Confirmation fails due to inconsistent trade data or wrong party address"); + }); - it("Trade cancellation fails due to wrong arguments", async () => { - let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); - await sdc.deployed(); -// console.log("SDC Address: %s", sdc.address); - await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); - await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount); - const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); - await expect(incept_call).to.emit(sdc, "TradeIncepted"); - const confirm_call = sdc.connect(counterparty1).cancelTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData23"); - await expect(confirm_call).to.be.revertedWith("Cancellation fails due to inconsistent trade data or wrong party address"); - }); + it("6. Trade cancellation fails due to wrong party calling cancel", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + await expect(incept_call).to.emit(sdc, "TradeIncepted"); + const confirm_call = sdc.connect(counterparty2).cancelTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + await expect(confirm_call).to.be.revertedWith("Cancellation fails due to inconsistent trade data or wrong party address"); + }); - it("Counterparties incept and confirm a trade successfully, upfront is transferred, trade is terminated", async () => { + it("7. Trade cancellation fails due to wrong arguments", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); - await sdc.deployed(); -// console.log("SDC Address: %s", sdc.address); - await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); - await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount+upfront); - // Incept trade (and fetch tradeId) + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); + await expect(incept_call).to.emit(sdc, "TradeIncepted"); + const confirm_call = sdc.connect(counterparty1).cancelTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData23"); + await expect(confirm_call).to.be.revertedWith("Cancellation fails due to inconsistent trade data or wrong party address"); + }); + + it("8. Counterparties incept and confirm, upfront is transferred from CP2 to CP1, Trade is terminated with Payment from CP2 to CP1", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); const receipt = await incept_call.wait(); const event = receipt.events.find(event => event.event === 'TradeIncepted'); const trade_id = event.args[1]; - const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, -upfront, "initialMarketData"); await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); - const terminate_call = await sdc.connect(counterparty1).requestTradeTermination(trade_id, terminationPayment, "terminationTerms"); await expect(terminate_call).to.emit(sdc, "TradeTerminationRequest"); - const confirm_terminate_call = await sdc.connect(counterparty2).confirmTradeTermination(trade_id, -terminationPayment, "terminationTerms"); await expect(confirm_terminate_call).to.emit(sdc, "TradeTerminationConfirmed"); let trade_state = await sdc.connect(counterparty1).getTradeState(); - await expect(trade_state).equal(TradeState.Valuation); + await expect(trade_state).equal(TradeState.Terminated); + let sdc_balance = await token.connect(counterparty1).balanceOf(sdc.address); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + await expect(sdc_balance).equal(0); + await expect(cp1_balance).equal(initialLiquidityBalance+upfront+terminationPayment); + await expect(cp2_balance).equal(initialLiquidityBalance-upfront-terminationPayment); }); - it("Successful Settlement", async () => { + it("9a. CP1 is Receiving Party, Trade-Termination is incepted by CP2 which receives the termination payment from CP1", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, 0, "initialMarketData"); + const receipt = await incept_call.wait(); + const event = receipt.events.find(event => event.event === 'TradeIncepted'); + const trade_id = event.args[1]; + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, 0, "initialMarketData"); + const terminate_call = await sdc.connect(counterparty2).requestTradeTermination(trade_id, -terminationPayment, "terminationTerms"); + const confirm_terminate_call = await sdc.connect(counterparty1).confirmTradeTermination(trade_id, +terminationPayment, "terminationTerms"); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + await expect(cp1_balance).equal(initialLiquidityBalance-terminationPayment); + await expect(cp2_balance).equal(initialLiquidityBalance+terminationPayment); + }); + it("9b. CP1 is Receiving Party, Trade-Termination is incepted by CP1 which pays the termination payment to CP2", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, 0, "initialMarketData"); + const receipt = await incept_call.wait(); + const event = receipt.events.find(event => event.event === 'TradeIncepted'); + const trade_id = event.args[1]; + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, 0, "initialMarketData"); + const terminate_call = await sdc.connect(counterparty1).requestTradeTermination(trade_id, -terminationPayment, "terminationTerms"); + const confirm_terminate_call = await sdc.connect(counterparty2).confirmTradeTermination(trade_id, +terminationPayment, "terminationTerms"); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + await expect(cp1_balance).equal(initialLiquidityBalance-terminationPayment); + await expect(cp2_balance).equal(initialLiquidityBalance+terminationPayment); + }); + + it("10. Successful Inception with Upfront transferred from CP2 to CP1 + successful settlement transferred from CP1 to CP2", async () => { + let settlementAmount = -245; + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); - await sdc.deployed(); -// console.log("SDC Address: %s", sdc.address); - await token.connect(counterparty1).approve(sdc.address,terminationFee+10*marginBufferAmount); //Approve for 10*margin amount - await token.connect(counterparty2).approve(sdc.address,terminationFee+10*marginBufferAmount+upfront); let trade_id =""; const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); await expect(incept_call).to.emit(sdc, "TradeIncepted"); @@ -168,11 +252,73 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); const initSettlementPhase = sdc.connect(counterparty2).initiateSettlement(); await expect(initSettlementPhase).to.emit(sdc, "TradeSettlementRequest"); - const balance_call = await token.connect(counterparty2).balanceOf(counterparty2.address); -// console.log("Balance: %s", balance_call); - const performSettlementCall = sdc.connect(counterparty1).performSettlement(1,"settlementData"); + + const performSettlementCall = sdc.connect(counterparty1).performSettlement(settlementAmount,"settlementData"); await expect(performSettlementCall).to.emit(sdc, "TradeSettlementPhase"); let trade_state = await sdc.connect(counterparty1).getTradeState(); await expect(trade_state).equal(TradeState.Settled); + let sdc_balance = await token.connect(counterparty1).balanceOf(sdc.address); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + await expect(sdc_balance).equal(2*(terminationFee+marginBufferAmount)); + await expect(cp1_balance).equal(initialLiquidityBalance-terminationFee-marginBufferAmount+upfront+settlementAmount); + await expect(cp2_balance).equal(initialLiquidityBalance-terminationFee-marginBufferAmount-upfront-settlementAmount); + }); + + it("11. Failed settlement followed by Termination with Pledge Case", async () => { + let settlementAmount = -500; + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + let trade_id =""; + let upfront_max = initialLiquidityBalance - marginBufferAmount - terminationFee; + + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, -upfront_max, "initialMarketData"); + await expect(incept_call).to.emit(sdc, "TradeIncepted"); + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, +upfront_max, "initialMarketData"); + await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); + + const initSettlementPhase = sdc.connect(counterparty2).initiateSettlement(); + await expect(initSettlementPhase).to.emit(sdc, "TradeSettlementRequest"); + + const performSettlementCall = sdc.connect(counterparty1).performSettlement(settlementAmount,"settlementData"); + await expect(performSettlementCall).to.emit(sdc, "TradeSettlementPhase"); + let trade_state = await sdc.connect(counterparty1).getTradeState(); + let sdc_balance = await token.connect(counterparty1).balanceOf(sdc.address); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + + await expect(trade_state).equal(TradeState.Terminated); + await expect(sdc_balance).equal(0); + await expect(cp1_balance).equal(marginBufferAmount+settlementAmount); + await expect(cp2_balance).equal(initialLiquidityBalance+upfront_max-settlementAmount+terminationFee); + + }); + + it("12. Failed Mutual Termination: Payment from CP1 to CP2 results in pledge case with capped termination fee amount being transferred", async () => { + let token = await ERC20Factory.deploy(); + await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); + await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); + let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, 0, "initialMarketData"); + const receipt = await incept_call.wait(); + const event = receipt.events.find(event => event.event === 'TradeIncepted'); + const trade_id = event.args[1]; + const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, 0, "initialMarketData"); + await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); + const terminate_call = await sdc.connect(counterparty1).requestTradeTermination(trade_id, 10000000, "terminationTerms"); + await expect(terminate_call).to.emit(sdc, "TradeTerminationRequest"); + const confirm_terminate_call = await sdc.connect(counterparty2).confirmTradeTermination(trade_id, -10000000, "terminationTerms"); + await expect(confirm_terminate_call).to.emit(sdc, "TradeTerminationConfirmed"); + let trade_state = await sdc.connect(counterparty1).getTradeState(); + await expect(trade_state).equal(TradeState.Terminated); + let sdc_balance = await token.connect(counterparty1).balanceOf(sdc.address); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); + let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); + await expect(sdc_balance).equal(0); + await expect(cp1_balance).equal(initialLiquidityBalance+marginBufferAmount+terminationFee); + await expect(cp2_balance).equal(initialLiquidityBalance-marginBufferAmount-terminationFee); + }); }); \ No newline at end of file From c7f655702fe61c9f05e775ffaac5b1d011c5fcda Mon Sep 17 00:00:00 2001 From: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> Date: Tue, 30 Jul 2024 09:59:11 -0400 Subject: [PATCH 078/126] Update jekyll-label-bot.yml (#544) --- .github/workflows/jekyll-label-bot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jekyll-label-bot.yml b/.github/workflows/jekyll-label-bot.yml index 549c28c9b4..f5450c9a71 100644 --- a/.github/workflows/jekyll-label-bot.yml +++ b/.github/workflows/jekyll-label-bot.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: Pandapip1/jekyll-label-action@d0fd82c3cd118140a50843906845fca8e59a8b9e + - uses: Pandapip1/jekyll-label-action@753297a43dc77d48a8b92e5d66622c452a5247a9 with: token: ${{ secrets.GITHUB_TOKEN }} config-path: config/.jekyll-labels.yml From 10e5f3dd66b76aff10eba1c29dfa467fbc6edee0 Mon Sep 17 00:00:00 2001 From: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:06:49 -0400 Subject: [PATCH 079/126] Update jekyll-label-bot.yml --- .github/workflows/jekyll-label-bot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jekyll-label-bot.yml b/.github/workflows/jekyll-label-bot.yml index f5450c9a71..73307415ca 100644 --- a/.github/workflows/jekyll-label-bot.yml +++ b/.github/workflows/jekyll-label-bot.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: Pandapip1/jekyll-label-action@753297a43dc77d48a8b92e5d66622c452a5247a9 + - uses: Pandapip1/jekyll-label-action@4b7cce7588a8686f5146a8e12aab7269042057ce with: token: ${{ secrets.GITHUB_TOKEN }} config-path: config/.jekyll-labels.yml From 80c2da06e7258e7c0a4bc3ac479675ee3070dbcb Mon Sep 17 00:00:00 2001 From: Christian Fries Date: Thu, 1 Aug 2024 10:21:53 +0200 Subject: [PATCH 080/126] Website: Refactor rename for events associated with settlement. Merged by EIP-Bot. --- ERCS/erc-6123.md | 8 ++--- assets/erc-6123/contracts/ISDC.sol | 33 +++++++++++++------ .../erc-6123/contracts/SDCPledgedBalance.sol | 6 ++-- assets/erc-6123/test/SDCTests.js | 8 ++--- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/ERCS/erc-6123.md b/ERCS/erc-6123.md index 591bd4dcef..16ce2ce58f 100644 --- a/ERCS/erc-6123.md +++ b/ERCS/erc-6123.md @@ -173,20 +173,20 @@ Emitted when a Trade is activated event TradeActivated(string tradeId); ``` -### TradeSettlementRequest +### SettlementRequested Emitted when a settlement is requested. May trigger the settlement phase. ```solidity -event TradeSettlementRequest(address initiator, string tradeData, string lastSettlementData); +event SettlementRequested(address initiator, string tradeData, string lastSettlementData); ``` -### TradeSettlementPhase +### SettlementEvaluated Emitted when the settlement phase is started. ```solidity -event TradeSettlementPhase(); +event SettlementEvaluated(); ``` #### TradeTerminationRequest diff --git a/assets/erc-6123/contracts/ISDC.sol b/assets/erc-6123/contracts/ISDC.sol index e82b4d3a8c..90b299f72d 100644 --- a/assets/erc-6123/contracts/ISDC.sol +++ b/assets/erc-6123/contracts/ISDC.sol @@ -62,7 +62,11 @@ pragma solidity >=0.7.0 <0.9.0; */ interface ISDC { + /*------------------------------------------- EVENTS ---------------------------------------------------------------------------------------*/ + + /* Events related to trade inception */ + /** * @dev Emitted when a new trade is incepted from a eligible counterparty * @param initiator is the address from which trade was incepted @@ -85,6 +89,8 @@ interface ISDC { */ event TradeCanceled(address initiator, string tradeId); + /* Events related to activation and termination */ + /** * @dev Emitted when a confirmed trade is set to active - e.g. when termination fee amounts are provided * @param tradeId the trade identifier of the activated trade @@ -97,15 +103,22 @@ interface ISDC { */ event TradeTerminated(string cause); + /* Events related to the settlement process */ + /** * @dev Emitted when Settlement phase is initiated */ - event TradeSettlementPhase(); + event SettlementEvaluated(); /** * @dev Emitted when settlement process has been finished */ - event TradeSettled(string transactionData); + event SettlementTranfered(string transactionData); + + /** + * @dev Emitted when settlement process has been finished + */ + event SettlementFailed(string transactionData); /** * @dev Emitted when a settlement gets requested @@ -113,7 +126,9 @@ interface ISDC { * @param tradeData holding the stored trade data * @param lastSettlementData holding the settlementdata from previous settlement (next settlement will be the increment of next valuation compared to former valuation) */ - event TradeSettlementRequest(address initiator, string tradeData, string lastSettlementData); + event SettlementRequested(address initiator, string tradeData, string lastSettlementData); + + /* Events related to trade termination */ /** * @dev Emitted when a counterparty proactively requests an early termination of the underlying trade @@ -162,7 +177,7 @@ interface ISDC { /** * @notice Performs a matching of provided trade data and settlement data of a previous trade inception - * @dev emits a {TradeConfirmed} event if trade data match + * @dev emits a {TradeConfirmed} event if trade data match and emits a {TradeActivated} if trade becomes active or {TradeTerminated} if not * @param withParty is the party the confirmer wants to trade with * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml * @param position is the position the confirmer has in that trade (negative of the position the inceptor has in the trade) @@ -186,13 +201,13 @@ interface ISDC { /** * @notice Called to trigger a (maybe external) valuation of the underlying contract and afterwards the according settlement process - * @dev emits a {TradeSettlementRequest} + * @dev emits a {SettlementRequested} */ function initiateSettlement() external; /** * @notice Called to trigger according settlement on chain-balances callback for initiateSettlement() event handler - * @dev perform settlement checks, may initiate transfers and emits {TradeSettlementPhase} + * @dev perform settlement checks, may initiate transfers and emits {SettlementEvaluated} * @param settlementAmount the settlement amount. If settlementAmount > 0 then receivingParty receives this amount from other party. If settlementAmount < 0 then other party receives -settlementAmount from receivingParty. * @param settlementData. the tripple (product, previousSettlementData, settlementData) determines the settlementAmount. */ @@ -203,11 +218,10 @@ interface ISDC { * @notice May get called from outside to to finish a transfer (callback). The trade decides on how to proceed based on success flag * @param success tells the protocol whether transfer was successful * @param transactionData data associtated with the transfer, will be emitted via the events. - * @dev may emit a {TradeSettled} event or a {TradeTerminated} event + * @dev emit a {SettlementTranfered} or a {SettlementFailed} event. May emit a {TradeTerminated} event. */ function afterTransfer(bool success, uint256 transactionData) external; - /// Trade termination /** @@ -230,10 +244,9 @@ interface ISDC { /** * @notice Called from a party to confirm an incepted termination, which might trigger a final settlement before trade gets closed - * @dev emits a {TradeTerminationConfirmed} + * @dev emits a {TradeTerminationCanceled} * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml * @param terminationTerms the termination terms */ function cancelTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; - } diff --git a/assets/erc-6123/contracts/SDCPledgedBalance.sol b/assets/erc-6123/contracts/SDCPledgedBalance.sol index 357ba86f1a..e6a6732ee7 100644 --- a/assets/erc-6123/contracts/SDCPledgedBalance.sol +++ b/assets/erc-6123/contracts/SDCPledgedBalance.sol @@ -68,7 +68,7 @@ contract SDCPledgedBalance is SDC { function initiateSettlement() external override onlyCounterparty onlyWhenSettled { address initiator = msg.sender; setTradeState(TradeState.Valuation); - emit TradeSettlementRequest(initiator, tradeData, settlementData[settlementData.length - 1]); + emit SettlementRequested(initiator, tradeData, settlementData[settlementData.length - 1]); } /* @@ -87,7 +87,7 @@ contract SDCPledgedBalance is SDC { address[] memory to = new address[](1); uint256[] memory amounts = new uint256[](1); from[0] = settlementPayer; to[0] = otherParty(settlementPayer); amounts[0] = transferAmount; - emit TradeSettlementPhase(); + emit SettlementEvaluated(); setTradeState(TradeState.InTransfer); settlementToken.checkedBatchTransferFrom(from,to,amounts,transactionID); } @@ -110,7 +110,7 @@ contract SDCPledgedBalance is SDC { else if ( inStateTransfer() ){ if (success){ setTradeState(TradeState.Settled); - emit TradeSettled("Settlement Settled - Pledge Transfer"); + emit SettlementTranfered("Settlement Settled - Pledge Transfer"); } else{ // Settlement & Pledge Case: transferAmount is transferred from SDC balance (i.e. pledged balance). int256 settlementAmount = settlementAmounts[settlementAmounts.length-1]; diff --git a/assets/erc-6123/test/SDCTests.js b/assets/erc-6123/test/SDCTests.js index dececef6c7..180231a0f3 100644 --- a/assets/erc-6123/test/SDCTests.js +++ b/assets/erc-6123/test/SDCTests.js @@ -251,10 +251,10 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, -upfront, "initialMarketData"); await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); const initSettlementPhase = sdc.connect(counterparty2).initiateSettlement(); - await expect(initSettlementPhase).to.emit(sdc, "TradeSettlementRequest"); + await expect(initSettlementPhase).to.emit(sdc, "SettlementRequested"); const performSettlementCall = sdc.connect(counterparty1).performSettlement(settlementAmount,"settlementData"); - await expect(performSettlementCall).to.emit(sdc, "TradeSettlementPhase"); + await expect(performSettlementCall).to.emit(sdc, "SettlementEvaluated"); let trade_state = await sdc.connect(counterparty1).getTradeState(); await expect(trade_state).equal(TradeState.Settled); let sdc_balance = await token.connect(counterparty1).balanceOf(sdc.address); @@ -281,10 +281,10 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); const initSettlementPhase = sdc.connect(counterparty2).initiateSettlement(); - await expect(initSettlementPhase).to.emit(sdc, "TradeSettlementRequest"); + await expect(initSettlementPhase).to.emit(sdc, "SettlementRequested"); const performSettlementCall = sdc.connect(counterparty1).performSettlement(settlementAmount,"settlementData"); - await expect(performSettlementCall).to.emit(sdc, "TradeSettlementPhase"); + await expect(performSettlementCall).to.emit(sdc, "SettlementEvaluated"); let trade_state = await sdc.connect(counterparty1).getTradeState(); let sdc_balance = await token.connect(counterparty1).balanceOf(sdc.address); let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); From e21527a50060ab8c1bbfb0e4345e779a2504c91b Mon Sep 17 00:00:00 2001 From: Christian Fries Date: Thu, 1 Aug 2024 11:06:15 +0200 Subject: [PATCH 081/126] Update ERC-6123: Refactor rename SDC.sol to SDCSingleTrade.sol Merged by EIP-Bot. --- ERCS/erc-6123.md | 5 +++-- assets/erc-6123/contracts/{SDC.sol => SDCSingleTrade.sol} | 0 ...DCPledgedBalance.sol => SDCSingleTradePledgedBalance.sol} | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) rename assets/erc-6123/contracts/{SDC.sol => SDCSingleTrade.sol} (100%) rename assets/erc-6123/contracts/{SDCPledgedBalance.sol => SDCSingleTradePledgedBalance.sol} (99%) diff --git a/ERCS/erc-6123.md b/ERCS/erc-6123.md index 16ce2ce58f..43986463a8 100644 --- a/ERCS/erc-6123.md +++ b/ERCS/erc-6123.md @@ -238,7 +238,8 @@ The interface design and reference implementation are based on the following con - The interface specification is generic enough to handle the case that parties process one or even multiple financial transactions (on a netted base) - Usually, the valuation of financial trades (e.g. OTC Derivatives) will require advanced valuation methodology to determine the market value. This is why the concept might rely on an external market data source and hosted valuation algorithms - A pull-based valuation-based oracle pattern can be implemented by using the provided callback pattern (methods: `initiateSettlement`, `performSettlement`) -- The reference implementation `SDC.sol` is based on a state-machine pattern where the states also serve as guards (via modifiers) to check which method is allowed to be called at a particular given process and trade state +- The reference implementation `SDCSingleTrade.sol` considers a single trade and is based on a state-machine pattern where the states also serve as guards (via modifiers) to check which method is allowed to be called at a particular given process and trade state +- The interface allows the extension to multiple trades with common (netted) settlement. ### State diagram of trade and process states @@ -254,7 +255,7 @@ Life-cycle unit tests based on the sample implementation and usage of [ERC-20](. ## Reference Implementation -An abstract contract class SDC.sol as well as a full reference implementation SDCPledgedBalance.sol for an OTC-Derivative is provided and is based on the [ERC-20](./eip-20.md) token standard. +An abstract contract class `SDCSingleTrade.sol` for single trade SDCs as well as a full reference implementation SDCPledgedBalance.sol for an OTC-Derivative is provided and is based on the [ERC-20](./eip-20.md) token standard. See folder `/assets/contracts`, more explanation on the implementation is provided inline. ### Trade Data Specification (suggestion) diff --git a/assets/erc-6123/contracts/SDC.sol b/assets/erc-6123/contracts/SDCSingleTrade.sol similarity index 100% rename from assets/erc-6123/contracts/SDC.sol rename to assets/erc-6123/contracts/SDCSingleTrade.sol diff --git a/assets/erc-6123/contracts/SDCPledgedBalance.sol b/assets/erc-6123/contracts/SDCSingleTradePledgedBalance.sol similarity index 99% rename from assets/erc-6123/contracts/SDCPledgedBalance.sol rename to assets/erc-6123/contracts/SDCSingleTradePledgedBalance.sol index e6a6732ee7..0268780714 100644 --- a/assets/erc-6123/contracts/SDCPledgedBalance.sol +++ b/assets/erc-6123/contracts/SDCSingleTradePledgedBalance.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: CC0-1.0 pragma solidity >=0.8.0 <0.9.0; -import "./SDC.sol"; +import "./SDCSingleTrade.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "./ERC20Settlement.sol"; From e9480178aff5750f2acbf7252b1aef903805d45d Mon Sep 17 00:00:00 2001 From: nand2 Date: Thu, 1 Aug 2024 15:12:05 +0200 Subject: [PATCH 082/126] Update ERC-6860: Add the fragment part to the URL definition. Merged by EIP-Bot. --- ERCS/erc-6860.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-6860.md b/ERCS/erc-6860.md index 092e988adf..a79ddac86e 100644 --- a/ERCS/erc-6860.md +++ b/ERCS/erc-6860.md @@ -38,7 +38,7 @@ This specification uses the Augmented Backus-Naur Form (ABNF) notation of [RFC 2 A Web3 URL is an ASCII string in the following form : ``` -web3URL = schema "://" [ userinfo "@" ] contractName [ ":" chainid ] pathQuery +web3URL = schema "://" [ userinfo "@" ] contractName [ ":" chainid ] pathQuery [ "#" fragment ] schema = "w3" / "web3" userinfo = address ``` @@ -69,6 +69,12 @@ pathQuery = mPathQuery ; path+query for manual mode **pathQuery**, made of the path and optional query, will have a different structure whether the resolve mode is "manual" or "auto". +``` +fragment = *VCHAR +``` + +**fragment**, like in HTTP URLs, is a string of characters meant to refer to a resource, and is not transmitted to the smart contract. + ``` web3UrlRef = web3URL / relativeWeb3URL @@ -286,7 +292,7 @@ The protocol will find the address of **vitalik.eth** from ENS on chainid 1 (Mai ### Appendix A: Complete ABNF for Web3 URLs ``` -web3URL = schema "://" [ userinfo "@" ] contractName [ ":" chainid ] pathQuery +web3URL = schema "://" [ userinfo "@" ] contractName [ ":" chainid ] pathQuery [ "#" fragment ] schema = "w3" / "web3" userinfo = address contractName = address @@ -295,6 +301,7 @@ chainid = %x31-39 *DIGIT pathQuery = mPathQuery ; path+query for manual mode / aPathQuery ; path+query for auto mode +fragment = *VCHAR web3UrlRef = web3URL / relativeWeb3URL From 590bf774646f4facd5b3e60a6828045b2d67e7f0 Mon Sep 17 00:00:00 2001 From: galimba Date: Thu, 1 Aug 2024 20:26:28 +0200 Subject: [PATCH 083/126] Update ERC-7208: prep Merged by EIP-Bot. --- ERCS/erc-7208.md | 286 +++--------------- assets/erc-7208/erc-7208-compat.md | 20 +- assets/erc-7208/erc-7208-overview.svg | 2 +- .../erc-7208/erc-7208-technical-overview.svg | 2 +- 4 files changed, 57 insertions(+), 253 deletions(-) diff --git a/ERCS/erc-7208.md b/ERCS/erc-7208.md index 3dd6fca0d5..8b4d1d3f23 100644 --- a/ERCS/erc-7208.md +++ b/ERCS/erc-7208.md @@ -1,7 +1,7 @@ --- eip: 7208 title: On-Chain Data Container -description: Abstracting logic away from storage +description: ERC interoperability by abstracting logic away from storage author: Rachid Ajaja , Alexandros Athanasopulos (@Xaleee), Pavel Rubin (@pash7ka), Sebastian Galimberti Romano (@galimba) discussions-to: https://ethereum-magicians.org/t/erc-7208-on-chain-data-container/14778 status: Draft @@ -15,22 +15,22 @@ requires: 165 -"On-chain Data Containers" (ODCs) are used for indexing and storing data in Smart Contracts called "Data Objects" (DOs). Information stored in Data Objects can be accessed and modified by implementing smart contracts called "Data Managers" (DMs). This ERC defines a series of interfaces for the separation of the storage of data from the implementation of the logic functions that govern such data. We introduce the interfaces for ODCs, the structures associated with DOs for abstracting storage, the DMs to access or modify the data, and finally the interfaces for compatibility Registries that enable Data Portability (horizontal mobility) between different implementations of this ERC. - +"On-chain Data Containers" (ODCs) are a series of interfaces used for indexing and managing data in Smart Contracts called "Data Object" (DO). Information stored in Data Objects can be accessed and modified by implementing smart contracts called "Data Manager" (DM). This ERC defines a series of interfaces for the separation of the storage of data from the implementation of the logic functions that govern such data. We introduce the interfaces for access management through "Data Index" (DI) implementations, the structures associated with "Data Points" (DP) for abstracting storage, the Data Managers to access or modify the data, and finally the "Data Point Registries" (DPR) interfaces for compatibility that enable data portability (horizontal mobility) between different implementations of this ERC. ## Motivation -As the Ethereum ecosystem grows, so does the demand for on-chain functionalities. The market encourages a desire for broader adoption and more complex systems, so there is a constant need for improved efficiency. We have seen times where the market hype has driven an explosion of new standard token proposals. While each standard serves its purpose, most often requires more flexibility to manage interoperability with other standards. +As the Ethereum ecosystem grows, so does the demand for on-chain functionalities. The market encourages a desire for broader adoption through more complex systems and there is a constant need for improved efficiency. We have seen times where the market hype has driven an explosion of new standard token proposals. While each standard serves its purpose, most often requires more flexibility to manage interoperability with other standards. + -While such diversity spurs innovation, different projects will implement their bespoke solutions for interoperability, resulting in a highly fragmented landscape. The lack of a standard mechanism for adapting tokenized across ERC standards is accentuating the interoperability issues. +The diversity of standards spurs innovation. Different projects will implement their bespoke solutions for interoperability. The absence of a unified adapter mechanism driving the interactions between assets issued under different ERC standards is causing interoperability issues. This, in turn, is leading to fragmentation. -We recognize there is no โ€œone size fits allโ€ solution to solve the standardization and interoperability challenges. Most assets - Fungible, Non-Fungible, Digital Twins, Real-world Assets, DePin, etc - have multiple mechanisms for representing them as on-chain tokens using different standard interfaces. However, for those assets to be exchanged, traded, or interacted with, protocols must implement compatibility with those standards before accessing and modifying the on-chain data. Moreover, the immutability of smart contracts plays a role in future-proofing their implementations by supporting new tokenization standards. A collaborative effort must be made to enable interaction between assets tokenized under different standards. The current ERC provides the tools for developing such On-chain Adapters. +We recognize there is no โ€œone size fits allโ€ solution to solve the standardization and interoperability challenges. Most assets - Fungible, Non-Fungible, Digital Twins, Real-world Assets, DePin, etc - have multiple mechanisms for representing them as on-chain tokens through the use of different standard interfaces. However, for those assets to be exchanged, traded, or interacted with, protocols must implement compatibility with those standards before accessing and modifying the on-chain data. Moreover, the immutability of smart contracts plays a role in future-proofing their implementations by supporting new tokenization standards. A collaborative effort must be made to enable interaction between assets tokenized under different standards. The current ERC provides the tools for developing such on-chain adapters. -We aim to abstract the on-chain data handling from both the logical implementation and the ERC interfaces exposing the underlying data. This EIP proposes a series of interfaces for storing and accessing data on-chain, codifying the underlying assets as generic "Data Points" that may be associated with multiple interoperable and even concurrent ERC interfaces. This proposal is designed to work by coexisting with previous and future token standards, providing a flexible, efficient, and coherent mechanism to manage asset interoperability. +We aim to abstract the on-chain data handling from the logical implementation and the ERC interfaces exposing the underlying data. The current ERC proposes a series of interfaces for storing and accessing data on-chain, codifying the underlying assets as generic "Data Points" that may be associated with multiple interoperable and even concurrent ERC interfaces. This proposal is designed to work by coexisting with previous and future token standards, providing a flexible, efficient, and coherent mechanism to manage asset interoperability. - **Data Abstraction**: We propose a standardized interface for enabling developers to separate the data storage code from the underlying token utility logic, reducing the need for supporting and implementing multiple inherited -and often clashing- interfaces to achieve asset compatibility. The data (and therefore the assets) can be stored independently of the logic that governs such data. @@ -46,30 +46,27 @@ We aim to abstract the on-chain data handling from both the logical implementati ### Terms +**Data Index Implementation**: One or many Smart Contracts implementing the Data Index interface, used for data access management through the indexing of Data Objects. -**ODC**: A uniquely identifiable data structure used for indexing Data Objects or storing Data Points. - -**ODC Implementation**: One or many Smart Contracts implementing the ODC access-management logic. - -**Data Point**: A uniquely identifiable unit of information. +**Data Point**: A uniquely identifiable unit of information indexed by a Data Index, managed by a Data Manager through a Data Object, and provided by a Data Point Registry. -**Data Object**: One or many Smart Contracts implementing the low-level storage management of stored Data Points +**Data Object**: One or many Smart Contracts implementing the low-level storage management of stored Data Points. -**Data Manager**: One or many Smart Contracts implementing the high-level logic and end-user interface for managing the Data Points. +**Data Manager**: One or many Smart Contracts implementing the high-level logic and end-user interfaces for managing Data Points. **Data Point Registry**: One or many Smart Contracts that define a space of compatible or interoperable Data Points. -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. -### ODC Interface +### Data Index Interface - * ODC SHOULD manage internal IDs for each data container. - * ODC SHOULD manage the access of Data Managers to Data Objects. - * ODC SHOULD use the IODC interface: + * DataIndex SHOULD manage the access of Data Managers to Data Objects. + * DataIndex SHOULD manage internal IDs for each user. + * DataIndex SHOULD use the IDataIndex interface: ```solidity -interface IODC { +interface IDataIndex { /** * @notice Verifies if DataManager is allowed to write specific DataPoint on specific DataObject * @param dp Identifier of the DataPoint @@ -109,12 +106,15 @@ interface IODC { function write(address dobj, DataPoint dp, bytes4 operation, bytes calldata data) external returns(bytes memory); } ``` +The **Data Index** is a smart contract entrusted with access control. It is a gating mechanism for **Data Managers** to access **Data Objects**. If a **Data Manager** intends to access a **Data Point** (either by `read()`, `write()`, or any other method), the **Data Index** should be used for validating access to the data. + +The mechanism for ID managamenent determines a space of compatibility between implementations. ### Data Object Interface * Data Object SHOULD implement the logic directly related to handling the data stored on Data Points. - * Data Object SHOULD implement the logic for transfering management of its Data Points to a different ODC Implementation. + * Data Object SHOULD implement the logic for transfering management of its Data Points to a different Data Index Implementation. * Data Object SHOULD use the IDataObject interface: ```solidity @@ -138,18 +138,18 @@ interface IDataObject { function write(DataPoint dp, bytes4 operation, bytes calldata data) external returns(bytes memory); /** - * @notice Sets ODC Implementation + * @notice Sets DataIndex Implementation * @param dp Identifier of the DataPoint - * @param newImpl address of the new ODC implementation + * @param newImpl address of the new DataIndex implementation */ - function setOdcImplementation(DataPoint dp, address newImpl) external; + function setDIImplementation(DataPoint dp, address newImpl) external; } ``` +**Data Objects** are entrusted with the management of transactions that affect the storage of **Data Points**. -Data Objects can receive `read()` or `write()` requests when a Data Manager is requesting access to a Data Point. - -The function `setODCImplementation()` SHOULD enable the delegation of the the management function to an IODC implementation. +**Data Objects** can receive `read()`, `write()`, or any other custom requests from a **Data Manager** requesting access to a **Data Point**. +As such, **Data Objects** respond to a gating mechanism given by a single **Data Index**. The function `setDIImplementation()` SHOULD enable the delegation of the the management function to an `IDataIndex` implementation. ### Data Point Structure @@ -176,6 +176,8 @@ The function `setODCImplementation()` SHOULD enable the delegation of the the ma **/ ``` +**Data Points** are the low-level structure abstracting information. **Data Points** are allocated by a **Data Point Registry**, and this information should be stored within its internal structure. Each **Data Point** should have a unique identifier provided by the **Data Point Registry** when instantiated. + ### Data Point Registry Interface @@ -208,7 +210,7 @@ interface IDataPointRegistry { function transferOwnership(DataPoint dp, address newOwner) external; /** - * @notice Grant permission to grant/revoke other roles on the DataPoint inside an ODC Implementation + * @notice Grant permission to grant/revoke other roles on the DataPoint inside a Data Index Implementation * This is useful if DataManagers are deployed during lifecycle of the application. * @param dp DataPoint * @param account New admin @@ -217,7 +219,7 @@ interface IDataPointRegistry { function grantAdminRole(DataPoint dp, address account) external returns (bool); /** - * @notice Revoke permission to grant/revoke other roles on the DataPoint inside an ODC Implementation + * @notice Revoke permission to grant/revoke other roles on the DataPoint inside a Data Index Implementation * @param dp DataPoint * @param account Old admin * @dev If an owner revokes Admin role from himself, he can add it again @@ -226,6 +228,8 @@ interface IDataPointRegistry { function revokeAdminRole(DataPoint dp, address account) external returns (bool); } ``` +The **Data Point Registry** is a smart contract entrusted with **Data Point** access control. **Data Managers** may request the allocation of **Data Points** to the **Data Point Registry**. Access-control to those **Data Points** is also managed by the **Data Point Registry**. + ### Data Manager Contract @@ -236,7 +240,7 @@ interface IDataPointRegistry { * Data Manager MAY use multiple Data Points * Data Manager MAY implement the logic for requesting Data Points from a Data Point Registry. -Data Managers are independent smart contracts that implement the business logic. They can either `read()` from a DataObject address, and `write()` through an ODC Implementation managing the delegated storage of the Data Points. +**Data Managers** are independent smart contracts that implement the business logic or "high-level" data management. They can either `read()` from a **Data Object** address and `write()` through a **Data Index** Implementation managing the delegated storage of the **Data Points**. ## Rationale @@ -245,238 +249,38 @@ Data Managers are independent smart contracts that implement the business logic. The decision to encode Data Points as bytes32 data containers is primarily driven by flexibility and future-proofing. Using bytes32 allows for a wide range of data encodings. This provides the developer with many options to accommodate diverse use cases. Furthermore, as Ethereum and its standards continue to evolve, encoding as bytes32 ensures that the Standard Adapters can support future data types or structures without requiring significant changes to the standard adapter itself. The Data Point encoding should have a prefix so that the Data Object can efficiently identify compatibility issues when accessing the data storage. Additionally, the prefix should be used to find the Data Point Registry and verify admin access of the Data Point. The use of a suffix for identifying the Data Point Registry is also required, for the Data Object to quickly discard badly formed transactions that aim to use a Data Point from an unmatching Data Point Registry. -Data Manager implementations decide which Data Points they will be using. Their allocation is managed through a Data Point Registry, and the access to the Data Point is managed by passing through the ODC Implementation. +Data Manager implementations decide which Data Points they will be using. Their allocation is managed through a Data Point Registry, and the access to the Data Point is managed by passing through the Data Index Implementation. Data Objects being independent separate Smart Contracts that implement the same `read`/`write` interface for communicating with Data Managers is a decision mainly driven by the scalability of the system. Offering a simple interface for this 2-layer structure enables different applications to have their addresses for storage of data as well as assets. It is up to each implementation to manage access to this Data Point storage space. This enables a wide array of complex, dynamic, and interactive use cases to be implemented with multiple ERCs as well as other smart contracts. Data Objects offer flexibility in storing mutable on-chain data that can be modified as per the requirements of each specific use case. This enables the Data Managers to hold mutable states in delegated storage and reflect changes over time, providing a dynamic layer to the otherwise static nature of storage through most other standardized interfaces. -As the Data Points can be set to respond to a specific ODC implementation, Data Managers can decide to migrate the complete storage of a Data Object from one ODC implementation to another. -By leveraging multiple implementations of the IODC interface, this standard delivers a powerful framework that amplifies the potential of all ERCs (present and future). +As the Data Points can be set to respond to a specific Data Index implementation, Data Managers can decide to migrate the complete storage of a Data Object from one Data Index implementation to another. +By leveraging multiple implementations of the `IDataIndex` interface, this standard delivers a powerful framework that amplifies the potential of all ERCs (present and future). ## Backwards Compatibility -This ERC is intended to augment the functionality of existing token standards without introducing breaking changes. As such, it does not present any backwards compatibility issues. Already deployed ERCs can be wrapped as Data Points or owned in Vaults as Data Objects, and later exposed through any implementation of Data Managers. - -See Reference Implementation. - +This ERC is intended to augment the functionality of existing token standards without introducing breaking changes. As such, it does not present any backward compatibility issues. Already deployed tokens under other ERCs can be wrapped as Data Points and managed by Data Objects, and later exposed through any implementation of Data Managers. Each interoperability integration will require a compatibility analysis, depending on the use case. ## Reference Implementation -We present an example implementation of Asset Vaults, a deterministic Asset Vault Factory, and a Data Object specialized in managing Asset Vaults. The following implementation assumes the existance of both IDataPointRegistry and IODC implementations. - -The Vault Data Object may hold multiple assets locked under management, which in turn may be exposed through a Data Manager interface implementing a logic equivalent to the fractionalization of the Vault. - - -**Example Vault Interface** +We present an **educational example** implementation showcasing two types of tokens (Fungible and Semi-Fungible) sharing the same storage. The abstraction of the storage from the logic is achieved through the use of **Data Objects**. A factory is used for deploying fungible token contracts that share storage with each semi-fungible NFT representing a collection of fractions. Note that if a `transfer()` is called by either interface (Fungible or Semi-Fungible), both interfaces are emitting an event. -Asset Vaults are an example smart contract designed to implement the minimimalistic approach at asset management. Any user can deploy Asset Vaults through the factory. The role of an Asset Vault smart contract is to manage assets on behalf of the owner. We recommend Asset Vaults be `Ownable2Step` for security reasons. +**This example has not been audited and should not be used in production environments.** -```solidity -pragma solidity ^0.8.0; -interface IVault { - /** - * @notice Executes a state-changing call on a target - * @param target Contract to call - * @param data Data sent to the target, including function selector - * @param value Native coin value sent with the call - * @dev Access to this function SHOULD be protected - */ - function execute(address target, bytes calldata data, uint256 value) external returns (bytes memory); - /** - * @notice Executes a static call (non state-changing) on a target - * @param target Contract to call - * @param data Data sent to the target, including function selector - */ - function executeStatic(address target, bytes calldata data) external view returns (bytes memory); - ... -} -``` - -Vault Factories are example smart contracts that facilitate the deployment of Asset Vaults. - -**Example Vault Factory Interface** -```solidity -pragma solidity ^0.8.0; - -import "IVault.sol"; - -interface IVaultFactory { - function deploy(bytes calldata data) external returns (IVault); - function deployDeterministic(bytes32 salt, bytes calldata data) external returns (IVault); - function computeVaultAddress(address deployer, bytes32 salt, bytes calldata data) external view returns (address); - ... -} -``` - -A Vault Data Object is an implementation of Base Data Object for managing assets through Asset Vaults. As an implementation of IDataObject, this smart contract must implement `read()` and `write()` functions related to handling the data stored on Data Points under management. The logic of `read()` can be implemented with the use of `dispatchRead()` and the `write()` logic with `dispatchWrite()`. In this example, the Vault Data Object also implements some signature verification logic. - -**Example Vault Data Object contract** -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "IVaultFactory.sol"; -import "BaseDataObject.sol"; - -contract VaultDataObject is IERC1271, BaseDataObject { - using EnumerableSet for EnumerableSet.AddressSet; - - bytes4 public constant VAULT_FOR_SALT_SELECTOR = bytes4(keccak256("vaultForSalt(bytes32)")); //vaultForSalt(bytes32) returns(address) - bytes4 public constant ALL_VAULTS_SELECTOR = bytes4(keccak256("allVaults()")); //allVaults() returns(address[] memory) - - bytes4 public constant DEPLOY_VAULT_SELECTOR = bytes4(keccak256("deployVault(address,bytes)")); //deployVault(address factory, bytes calldata data) - bytes4 public constant DEPLOY_DETERMINISTIC_VAULT_SELECTOR = bytes4(keccak256("deployDeterministicVault(address,bytes32,bytes)")); //deployDeterministicVault(address factory, bytes32 salt, bytes calldata data) - bytes4 public constant GRANT_SIGNATURE_VALIDATION_SELECTOR = bytes4(keccak256("grantSignatureValidation(address,bytes32,address)")); //grantSignatureValidation(address vault, bytes32 hash, address signer) - bytes4 public constant REVOKE_SIGNATURE_VALIDATION_SELECTOR = bytes4(keccak256("revokeSignatureValidation(address,bytes32,address)")); //revokeSignatureValidation(address vault, bytes32 hash, address signer) - bytes4 public constant VAULT_EXECUTE_SELECTOR = bytes4(keccak256("vaultExecute(address,address,bytes,uint256)")); //vaultExecute(address vault, address target, bytes calldata data, uint256 value) - bytes4 private constant ERC1271_INVALID_SIGNATURE = 0xffffffff; - - error UnknownVault(); - error UnknownSalt(); - error UnknownDataPoint(); - error DeloyedVaultAlreadyRegistered(); - - event SignatureValidationAdded(address vault, bytes32 hash, address signer); - event SignatureValidationRevoked(address vault, bytes32 hash, address signer); - - struct SignatureVerificationData { - mapping(address vault => mapping(address signer => bool valid)) validSigners; - } - - struct DpData { - EnumerableSet.AddressSet deployedVaults; - mapping(bytes32 salt => address vault) deployedDeterministicVaults; - mapping(bytes32 hash => SignatureVerificationData) validSignatures; - ... - } - - mapping(uint256 dpIdx => DpData) private dpDataStorage; - mapping(address vault => DataPoint) internal vaultsToDataPoints; - - /** - * @inheritdoc IERC1271 - */ - function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue) { - ... - } - - function computeDeterministicVaultAddress(DataPoint dp, address factory, bytes32 salt, bytes calldata data) external view returns (address) { - ... - } - - function datapointOfVault(address vault) public view returns (DataPoint) { - DataPoint dp = vaultsToDataPoints[vault]; - if (DataPoint.unwrap(dp) == bytes32(0)) revert UnknownVault(); - return dp; - } - - function dispatchRead(DataPoint dp, bytes4 operation, bytes calldata data) internal view virtual override returns (bytes memory) { - if (operation == VAULT_FOR_SALT_SELECTOR) { - bytes32 salt = abi.decode(data, (bytes32)); - return abi.encode(_vaultForSalt(dp, salt)); - } else if (operation == ALL_VAULTS_SELECTOR) { - return abi.encode(_allVaults(dp)); - } else { - revert UnknownReadOperation(operation); - } - } - - function dispatchWrite(DataPoint dp, bytes4 operation, bytes calldata data) internal virtual override returns (bytes memory) { - if (operation == DEPLOY_VAULT_SELECTOR) { - (address factory, bytes memory factoryData) = abi.decode(data, (address, bytes)); - return abi.encode(_deployVault(dp, factory, factoryData)); - } else if (operation == DEPLOY_DETERMINISTIC_VAULT_SELECTOR) { - (address factory, bytes32 salt, bytes memory factoryData) = abi.decode(data, (address, bytes32, bytes)); - return abi.encode(_deployDeterministicVault(dp, factory, salt, factoryData)); - } else if (operation == GRANT_SIGNATURE_VALIDATION_SELECTOR) { - (address vault, bytes32 hash, address signer) = abi.decode(data, (address, bytes32, address)); - _grantSignatureValidation(dp, vault, hash, signer); - return ""; - } else if (operation == REVOKE_SIGNATURE_VALIDATION_SELECTOR) { - (address vault, bytes32 hash, address signer) = abi.decode(data, (address, bytes32, address)); - _revokeSignatureValidation(dp, vault, hash, signer); - return ""; - } else if (operation == VAULT_EXECUTE_SELECTOR) { - (address vault, address target, bytes memory vaultCallData, uint256 value) = abi.decode(data, (address, address, bytes, uint256)); - return _vaultExecute(dp, vault, target, vaultCallData, value); - } else { - revert UnknownWriteOperation(operation); - } - } - ... -} -``` - -Finally, we expose a user-facing example Data Manager contract for managing the Data Object. The Data Manager will be implementing `read()` functions for checking the users' `balanceOf()` directly from the Data Object, and `write()` functions through the IODC implementation for the `transfer()` logic. Since multiple Data Managers can make use of the same Data Point, it is possible for several concurrent Data Manager implementations to share a Data Point where they delegate their storage. - - -**Example Fungible Token Data Manager implementation** - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -contract ERC20DataManager is IERC20, IERC20Metadata, IERC20Errors, Ownable, ERC20Approvals, ERC20Transfers, ERC20Burnable, ERC20Mintable, ERC20Metadata { - bytes4 internal BALANCE_OF_SELECTOR = bytes4(keccak256("balanceOf(address)")); - bytes4 internal TOTAL_SUPPLY_SELECTOR = bytes4(keccak256("totalSupply()")); - bytes4 internal MINT_SELECTOR = bytes4(keccak256("mint(address,uint256)")); - bytes4 internal BURN_SELECTOR = bytes4(keccak256("burn(address,uint256)")); - bytes4 internal TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,address,uint256)")); - - DataPoint internal immutable datapoint; - IDataObject public immutable fungibleDO; - IODC public odc; - mapping(address account => mapping(address spender => uint256)) private _allowances; - - constructor( - bytes32 _datapoint, - address _odc, - address _fungibleDO, - string memory name_, - string memory symbol_ - ) Ownable(msg.sender) ERC20Metadata(name_, symbol_) { - datapoint = DataPoint.wrap(_datapoint); - odc = IODC(_odc); - fungibleDO = IDataObject(_fungibleDO); - } - - function totalSupply() external view override returns (uint256) { - return abi.decode(fungibleDO.read(datapoint, TOTAL_SUPPLY_SELECTOR, ""), (uint256)); - } - - function balanceOf(address account) external view override returns (uint256) { - return abi.decode(fungibleDO.read(datapoint, BALANCE_OF_SELECTOR, abi.encode(account)), (uint256)); - } - - function _checkMinter() internal view override { - _checkOwner(); - } - - function _writeTransfer(address from, address to, uint256 amount) internal override { - if (from == address(0)) { - odc.write(address(fungibleDO), datapoint, MINT_SELECTOR, abi.encode(to, amount)); - } else if (to == address(0)) { - odc.write(address(fungibleDO), datapoint, BURN_SELECTOR, abi.encode(from, amount)); - } else { - odc.write(address(fungibleDO), datapoint, TRANSFER_SELECTOR, abi.encode(from, to, amount)); - } - } -} -``` ## Security Considerations -The access control is separated in three layers: +The access control is separated into three layers: -* **Layer 1**: The Data Point Registry allocates for Data Managers and manages ownerhsip (admin/write rights) of Data Points. -* **Layer 2**: The ODC smart contract implements Access Control by managing Approvals of Data Managers to Data Points. It uses the Data Point Registry to verify who can grant/revoke this access. -* **Layer 3**: The Data Manager exposes functions that can perform `write` operations on the Data Point by calling the ODC implementation. +* **Layer 1**: The Data Point Registry allocates for Data Managers and manages ownership (admin/write rights) of Data Points. +* **Layer 2**: The Data Index smart contract implements Access Control by managing Approvals of Data Managers to Data Points. It uses the Data Point Registry to verify who can grant/revoke this access. +* **Layer 3**: The Data Manager exposes functions that can perform `write` operations on the Data Point by calling the Data Index implementation. -No further security considerations are derived specificly from this ERC. +No further security considerations are derived specifically from this ERC. ## Copyright diff --git a/assets/erc-7208/erc-7208-compat.md b/assets/erc-7208/erc-7208-compat.md index 571ef9f912..8a9a5fe696 100644 --- a/assets/erc-7208/erc-7208-compat.md +++ b/assets/erc-7208/erc-7208-compat.md @@ -1,20 +1,20 @@ -## Appendix: Compatibility Analysis +## Appendix: Interoperability Analysis -We provide a cherrypicked list of possible points of contact between ERC-7208 (on-chain data container) and other tokenization standards and proposals. +We provide a cherrypicked list of possible points of contact between ERC-7208 (on-chain data containers) and other tokenization standards and proposals. -**ERC-1400 (Security Token Standard)**: In aggregate provides a suite of standard interfaces for issuing / redeeming security tokens, managing their ownership and transfer restrictions and providing transparency to token holders on how different subsets of their token balance behave with respect to transfer restrictions, rights and obligations. ERC-7208 can enhance ERC-1400 by offering more dynamic and flexible data management. Data Objects enable the storage and modification of on-chain data related to security tokens, such as compliance information or ownership details. In the case of assets that are already issued under ERC-1400, they can be wrapped into a Vault Data Object and exposed through any Data Manager interface (including ERC-3643 and others). Alternative, if the asset is issued with native Data Point storage, the integration could lead to more efficient and transparent security token offerings. The modular and adaptable nature of ERC-7208 enable transparent enhancements to the internal logic of individual ERC-1400 tokens. +**ERC-1400 (Security Token Standard)**: This ERC provides a suite of standard interfaces for issuing / redeeming security tokens, managing their ownership and transfer restrictions and providing transparency to token holders on how different subsets of their token balance behave with respect to transfer restrictions, rights and obligations. ERC-7208 can enhance ERC-1400 by offering more dynamic and flexible data management architecture. **Data Objects** enable the storage and modification of on-chain data related to security tokens, such as compliance information or ownership details. In the case of assets that are already issued under ERC-1400, they can be wrapped into a **Vault Data Object** and exposed through any **Data Manager** interface (including ERC-3643 and others). Alternative, if the asset is issued with native **Data Point** storage, the integration could lead to more efficient and transparent security token offerings. The modular and adaptable nature of ERC-7208 enable transparent enhancements to the internal logic of individual ERC-1400 tokens. -**EIP-2309 (Consecutive batch minting)**: ERC-7208 is compatible with EIP-2309, allowing for the batch minting process to be enriched with additional data. ODCs could store information related to each batch, such as metadata or batch-specific attributes, without disrupting the minting process. This information may be stored on Data Points, internally within the ODC Implementation, or locally at the Data Manager exposing the EIP-2309 interface. +**EIP-2309 (Consecutive batch minting)**: ERC-7208 is compatible with EIP-2309, allowing for the batch minting process to be enriched with additional data. **Data Objects** could store information related to each batch, such as metadata or batch-specific attributes, without disrupting the minting process. This information may be stored on **Data Points**, and interacted with through a **Data Manager** exposing the EIP-2309 interface. -**EIP-2981 (Royalties)**: ERC-7208 can complement EIP-2981 as a Data Manager by providing a flexible way to handle royalties. Data Objects can store and manage the low level storage of royalty information dynamically and independently from the Data Manager's implemented interface. This enables a complex royalty structure that can change over time or based on certain conditions, like embedding compliance checks on the functions modifying the storage and simultaneously exposing multiple interfaces for accessing the storage. For instance, by leveraging ERC-7208, an individual royalty based NFT can be traded in a compliant manner, concurrently under both an ERC-721 interface as well as an ERC-20 through the use of Data Managers. +**EIP-2981 (Royalties)**: ERC-7208 can complement EIP-2981 as a **Data Manager** by providing a flexible way to handle royalties. **Data Objects** can store and manage the low level storage of royalty information dynamically and independently from the interface used by the end user. This enables a complex royalty structure that can change over time or based on arbitrary conditions, like embedding compliance checks and simultaneously exposing multiple interfaces for accessing the underlying asset. For instance, by leveraging ERC-7208, an individual royalty based NFT can be traded in a compliant manner, concurrently under both an ERC-721 interface as well as an ERC-20 through the use of **Data Managers**. -**ERC-3643 (Security Tokens)**: ERC-3643 defines a *Security Token interface for Regulated Exchanges* based on ERC-20 token standard. The ERC-7208 can be used for wrapping buckets of tokens (irrespective of their ERC) and adapting their logic to the ERC-3643. Additionally, a Data Object storing native ERC-3643 tokens can be used for improving the compliance logic and enabling the trading of underlying securities simultaneously through multiple interfaces that respond to different regulatory frameworks. Moreover, the separation of the storage enables the logic to implement functionalities that were not initially a part of the original ERC, such as identity-based recovery of assets, role-based access control, the introduction of cross-chain support, etc. +**ERC-3643 (Security Tokens)**: ERC-3643 defines a *Security Token interface for Regulated Exchanges* based on ERC-20 token standard. The ERC-7208 can be used for wrapping buckets of tokens (irrespective of their ERC) and adapting their logic to the ERC-3643. Additionally, a **Data Object** storing native ERC-3643 tokens can be used for improving the compliance logic and enabling the trading of underlying securities simultaneously through multiple interfaces that respond to different regulatory frameworks. Moreover, the separation of the storage enables the logic to implement functionalities that were not initially a part of the original ERC, such as identity-based recovery of assets, role-based access control, the introduction of cross-chain support, etc. -**ERC-4337 (Account Abstraction)**: ERC-7208 can provide a standardized method to store and manage the complex data structures required by abstracted accounts. This can include user preferences, access control lists, recovery options, and other customizable account features. The mutable states of abstracted accounts can be efficiently handled using Data Objects. This, in turn, improves the adaptability and security of abstracted accounts. Additionally, an ERC-7208 implementation supporting meta-transactions and Data Points separated by chain-id can be developed to fully abstract account management across blockchains. +**ERC-4337 (Account Abstraction)**: ERC-7208 can provide a standardized method to store and manage the complex data structures required by abstracted accounts. This can include user preferences, access control lists, recovery options, and other customizable account features. The mutable states of abstracted accounts can be efficiently handled using **Data Objects**. This, in turn, improves the adaptability and security of abstracted accounts. Additionally, an ERC-7208 implementation supporting meta-transactions and **Data Points** separated by chain-id can be developed to fully abstract account management across blockchains. -**EIP-4626 (Tokenized Vaults)**: Tokenized Vaults inherit from a single ERC-20 and ERC-2612 for approvals via EIP-712 secp256k1 signatures. ODCs can enhance EIP-4626 by providing a more dynamic data layer for tokenized vaults. Data Objects and ODCs can store information about the assets in the vault, conditions for access, or other relevant data, enabling more nuanced interactions with tokenized vaults. Additionally, the Data Object can store more than a single ERC-20, greatly increasing the capabilities of Tokenized Vaults. +**EIP-4626 (Tokenized Vaults)**: Tokenized Vaults inherit from a single ERC-20 and ERC-2612 for approvals via EIP-712 secp256k1 signatures. ERC-7208 can enhance EIP-4626 by providing a more dynamic data layer for tokenized vaults. **Data Objects** store information about the assets in the vault, conditions for access, or other relevant data, enabling more nuanced interactions with tokenized vaults. Additionally, the **Data Point** can store more than a single ERC-20, greatly increasing the capabilities of Tokenized Vaults. -**ERC-4907 (Shared Ownership)**: The integration of ERC-4907 as a Data Manager with ERC-7208 Data Object storage can enhance the rental experience by allowing for additional rental-related data directly on-chain, such as rental terms, user permissions, and other customizable settings which would be self-contained within Data Points and therefore automatically updated as metadata. ERC-4907's rental mechanism complements ERC-7208's ability to manage mutable on-chain data. By combining these two, NFTs can not only be rented out for specific periods but also have their traits or states dynamically managed and updated during the rental period. This combination enhances security and compliance in NFT transactions, particularly for Real World Asset Tokenization. Rental agreements, regulatory compliance, intelectual property and user rights can be embedded within Data Objects to ensure that the NFT usage adheres to predefined rules. +**ERC-4907 (Shared Ownership)**: The integration of ERC-4907 as a **Data Manager** with ERC-7208 **Data Point** storage can enhance the rental experience by allowing for additional rental-related data directly on-chain, such as rental terms, user permissions, and other customizable settings which would be self-contained within **Data Points** and therefore automatically updated as metadata. ERC-4907's rental mechanism complements ERC-7208's ability to manage mutable on-chain data. By combining these two, NFTs can not only be rented out for specific periods but also have their traits or states dynamically managed and updated during the rental period. This combination enhances security and compliance in NFT transactions, particularly for *Real World Asset Tokenization*. Rental agreements, regulatory compliance, intelectual property and user rights can be embedded within Data Objects to ensure that the NFT usage adheres to predefined rules. -**ERC-7540 (Asynchronous ERC-4626 Tokenized Vaults)**: ERC-7540 vaults's are focused on asynchronous deposit and redemption. Integrating ERC-7540, either by Wrapping into a Data Object or by exposing a 7208 Data Manager, will facilitate more complex financial products. DeFi products like undercollateralized loans, insurance products, or tokenized stocks often require operations to be handled in a non-instantaneous manner. However, the nature of these products requires adhering to regulatory compliance and identity management solutions. This can easily be achieved by implementing the use of on-chain adapters that enhance the logic while keeping the data secure. +**ERC-7540 (Asynchronous ERC-4626 Tokenized Vaults)**: ERC-7540 vaults's are focused on asynchronous deposit and redemption. Integrating ERC-7540, either by Wrapping into a **Data Object** or by exposing an ERC-7208 **Data Manager**, will facilitate more complex financial products. DeFi products like undercollateralized loans, insurance products, or tokenized stocks often require operations to be handled in a non-instantaneous manner. However, the nature of these products requires adhering to regulatory compliance and identity management solutions. This can easily be achieved by implementing the use of on-chain adapters that enhance the logic while keeping the data secure. diff --git a/assets/erc-7208/erc-7208-overview.svg b/assets/erc-7208/erc-7208-overview.svg index 13cc55680e..bd9fa53b8f 100644 --- a/assets/erc-7208/erc-7208-overview.svg +++ b/assets/erc-7208/erc-7208-overview.svg @@ -1,4 +1,4 @@ -
DataObject
DataObject
DataManager
On-chain Data Container
DataManager
DataManager
DataObject
Buckets of assets
and asset data
Access Management
Business Logic and
user interface for
interacting with the data
\ No newline at end of file +
DataObject
DataObject
DataManager
Data Index
ย &
Data Point Registry
DataManager
DataManager
DataObject
Asset data Storage
Access Management
Business Logic and
user interface for
interacting with the data
(expose any ERC interface)
\ No newline at end of file diff --git a/assets/erc-7208/erc-7208-technical-overview.svg b/assets/erc-7208/erc-7208-technical-overview.svg index d1ab5cc3cc..cef28c2fa2 100644 --- a/assets/erc-7208/erc-7208-technical-overview.svg +++ b/assets/erc-7208/erc-7208-technical-overview.svg @@ -1,4 +1,4 @@ -
Access Management
ODC_ID => DM_ID => DP_ID
...
...
ODC Smart Contract
ODC ID
Management
DataManager (DM)
Implements business logic
Access DM
Manage ODC
User
Manage DM <=> DataObject relationship
DM Mainteiner
Storage
DataPoint_DM1_1
odc_id1
odc_id2
DataPoint_DM1_2
odc_id1
odc_id2
DataPoint_DM2_1
odc_id1
odc_id2
DataPoint_DM2_2
odc_id1
odc_id2
DataPoint Logic
Performs logic directly related
to storage management
Multiple DataObject (DO)
Contracts
DataObject (DO)
Contract
ODC implementation
(ERC-7208)

DataManager (DM)
Implements business logic
DataManager (DM)
Implements business logic
DataManager (DM)
Implements usecase logic
Data is stored in DataPoints
DataObject exposes low level
Data Management interface
ODC implementation
manages access
and indexes DataObjects
DataPoint Registry

Should provide information if an account can grant access to DP for DMs and DOs

function isAdmin(DataPoint, account) returns (bool)
Verify DP Maintainer
is Admin of DataPoint
Allocate DataPoint
DataManagers Implement
business logic
(user-facing interfaces)
DP Registry implementation
separates the space of
compatible DataPoints
\ No newline at end of file +
Access Management
ID => DM_ID => DP_ID
...
...
DataIndex (DI)
Smart Contract
IDย Management
DataManager (DM)
Implements business logic
Access DM
Provides ID
User
Manage DM <=> DataObject relationship
DM Mainteiner
Storage
DataPoint_DM1_1
id_1
id_2
DataPoint_DM1_2
id_1
id_2
DataPoint_DM2_1
id_1
id_2
DataPoint_DM2_2
id_1
id_2
DataPoint Logic
Performs logic directly related
to storage management
Multiple DataObject (DO)
Contracts
DataObject (DO)
Contract
ERC-7208
Technical Overview
DataManager (DM)
Implements business logic
DataManager (DM)
Implements business logic
DataManager (DM)
Implements usecase logic
Data is stored in DataPoints
DataObject exposes low level
Data Management interface
DataIndex implementation
manages access
and indexes DataObjects
DataPoint Registry

Should provide information if an account can grant access to DP for DMs and DOs

function isAdmin(DataPoint, account) returns (bool)
Verify DP Maintainer
is Admin of DataPoint
Allocate DataPoint
DataManagers Implement
business logic
(user-facing interfaces)
DP Registry implementation
separates the space of
compatible DataPoints
\ No newline at end of file From 10f0120c2db3f3374035e85915a472e284ac5421 Mon Sep 17 00:00:00 2001 From: galimba Date: Thu, 1 Aug 2024 20:41:53 +0200 Subject: [PATCH 084/126] Update ERC-7208: pushing example implementation Merged by EIP-Bot. --- assets/erc-7208/contracts/DataIndex.sol | 95 ++++ .../erc-7208/contracts/DataPointRegistry.sol | 78 +++ ...icERC1155WithERC20FractionsDataManager.sol | 452 ++++++++++++++++++ .../MinimalisticERC20FractionDataManager.sol | 157 ++++++ ...alisticERC20FractionDataManagerFactory.sol | 12 + .../MinimalisticFungibleFractionsDO.sol | 380 +++++++++++++++ .../contracts/interfaces/IDataIndex.sol | 49 ++ .../contracts/interfaces/IDataObject.sol | 37 ++ .../interfaces/IDataPointRegistry.sol | 91 ++++ .../IFractionTransferEventEmitter.sol | 20 + .../IFungibleFractionsOperations.sol | 103 ++++ .../contracts/interfaces/IIDManager.sol | 27 ++ .../erc-7208/contracts/utils/ChainidTools.sol | 48 ++ .../erc-7208/contracts/utils/DataPoints.sol | 67 +++ .../contracts/utils/OmnichainAddresses.sol | 77 +++ 15 files changed, 1693 insertions(+) create mode 100644 assets/erc-7208/contracts/DataIndex.sol create mode 100644 assets/erc-7208/contracts/DataPointRegistry.sol create mode 100644 assets/erc-7208/contracts/datamanagers/MinimalisticERC1155WithERC20FractionsDataManager.sol create mode 100644 assets/erc-7208/contracts/datamanagers/MinimalisticERC20FractionDataManager.sol create mode 100644 assets/erc-7208/contracts/datamanagers/MinimalisticERC20FractionDataManagerFactory.sol create mode 100644 assets/erc-7208/contracts/dataobjects/MinimalisticFungibleFractionsDO.sol create mode 100644 assets/erc-7208/contracts/interfaces/IDataIndex.sol create mode 100644 assets/erc-7208/contracts/interfaces/IDataObject.sol create mode 100644 assets/erc-7208/contracts/interfaces/IDataPointRegistry.sol create mode 100644 assets/erc-7208/contracts/interfaces/IFractionTransferEventEmitter.sol create mode 100644 assets/erc-7208/contracts/interfaces/IFungibleFractionsOperations.sol create mode 100644 assets/erc-7208/contracts/interfaces/IIDManager.sol create mode 100644 assets/erc-7208/contracts/utils/ChainidTools.sol create mode 100644 assets/erc-7208/contracts/utils/DataPoints.sol create mode 100644 assets/erc-7208/contracts/utils/OmnichainAddresses.sol diff --git a/assets/erc-7208/contracts/DataIndex.sol b/assets/erc-7208/contracts/DataIndex.sol new file mode 100644 index 0000000000..f1cb864cbe --- /dev/null +++ b/assets/erc-7208/contracts/DataIndex.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/AccessControl.sol"; +import "./interfaces/IDataIndex.sol"; +import "./interfaces/IDataObject.sol"; +import "./interfaces/IIDManager.sol"; +import "./interfaces/IDataPointRegistry.sol"; + +/** + * @title Data Index contract + * @notice Minimalistic implementation of a Data Index contract + */ +contract DataIndex is IDataIndex, IIDManager, AccessControl { + /// @dev Error thrown when the sender is not an admin of the DataPoint + error InvalidDataPointAdmin(DataPoint dp, address sender); + + /// @dev Error thrown when the DataManager is not approved to interact with the DataPoint + error DataManagerNotApproved(DataPoint dp, address dm); + + /// @dev Error thrown when the dataIndex identifier is incorrect + error IncorrectIdentifier(bytes32 diid); + + /** + * @notice Event emitted when DataManager is approved for DataPoint + * @param dp Identifier of the DataPoint + * @param dm Address of DataManager + * @param approved if DataManager is approved + */ + event DataPointDMApprovalChanged(DataPoint dp, address dm, bool approved); + + /// @dev Mapping of DataPoint to DataManagers allowed to write to this DP (in any DataObject) + mapping(DataPoint => mapping(address dm => bool allowed)) dmApprovals; + + /** + * @notice Restricts access to the function, allowing only DataPoint admins + * @param dp DataPoint to check ownership of + */ + modifier onlyDPOwner(DataPoint dp) { + (uint32 chainId, address registry, ) = DataPoints.decode(dp); + ChainidTools.requireCurrentChain(chainId); + bool isAdmin = IDataPointRegistry(registry).isAdmin(dp, msg.sender); + if (!isAdmin) revert InvalidDataPointAdmin(dp, msg.sender); + _; + } + + /** + * @notice Allows access only to DataManagers which was previously approved + * @param dp DataPoint to check DataManager approval for + */ + modifier onlyApprovedDM(DataPoint dp) { + bool approved = dmApprovals[dp][msg.sender]; + if (!approved) revert DataManagerNotApproved(dp, msg.sender); + _; + } + + /// @dev Sets the default admin role + constructor() { + _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + ///@inheritdoc IDataIndex + function isApprovedDataManager(DataPoint dp, address dm) external view returns (bool) { + return dmApprovals[dp][dm]; + } + + ///@inheritdoc IDataIndex + function allowDataManager(DataPoint dp, address dm, bool approved) external onlyDPOwner(dp) { + dmApprovals[dp][dm] = approved; + emit DataPointDMApprovalChanged(dp, dm, approved); + } + + ///@inheritdoc IDataIndex + function read(address dobj, DataPoint dp, bytes4 operation, bytes calldata data) external view returns (bytes memory) { + return IDataObject(dobj).read(dp, operation, data); + } + + ///@inheritdoc IDataIndex + function write(address dobj, DataPoint dp, bytes4 operation, bytes calldata data) external onlyApprovedDM(dp) returns (bytes memory) { + return IDataObject(dobj).write(dp, operation, data); + } + + ///@inheritdoc IIDManager + function diid(address account, DataPoint) external pure returns (bytes32) { + return bytes32(uint256(uint160(account))); + } + + ///@inheritdoc IIDManager + function ownerOf(bytes32 _diid) external view returns (uint32, address) { + if (_diid & 0xFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000 != 0) revert IncorrectIdentifier(_diid); // Require first 12 bytes empty, leaving only 20 bytes of address non-empty + address account = address(uint160(uint256(_diid))); + if (account == address(0)) revert IncorrectIdentifier(_diid); + return (ChainidTools.chainid(), account); + } +} diff --git a/assets/erc-7208/contracts/DataPointRegistry.sol b/assets/erc-7208/contracts/DataPointRegistry.sol new file mode 100644 index 0000000000..e090083e93 --- /dev/null +++ b/assets/erc-7208/contracts/DataPointRegistry.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {DataPoints, DataPoint} from "./utils/DataPoints.sol"; +import {IDataPointRegistry} from "./interfaces/IDataPointRegistry.sol"; + +/** + * @title DataPointRegistry contract + * @notice Contract for managing the creation, transfer and access control of DataPoints + */ +contract DataPointRegistry is IDataPointRegistry { + /** + * @notice DataPoint access data + * @param owner Owner of the DataPoint + * @param isAdmin Mapping of isAdmin status for each account + */ + struct DPAccessData { + address owner; + mapping(address => bool) isAdmin; + } + + /// @dev Counter for DataPoint allocation + uint256 private counter; + + /// @dev Access data for each DataPoint + mapping(DataPoint => DPAccessData) private accessData; + + /// @inheritdoc IDataPointRegistry + function isAdmin(DataPoint dp, address account) public view returns (bool) { + return accessData[dp].isAdmin[account]; + } + + /// @inheritdoc IDataPointRegistry + function allocate(address owner) external payable returns (DataPoint) { + if (msg.value > 0) revert NativeCoinDepositIsNotAccepted(); + uint256 newCounter = ++counter; + if (newCounter > type(uint32).max) revert CounterOverflow(); + DataPoint dp = DataPoints.encode(address(this), uint32(newCounter)); + DPAccessData storage dpd = accessData[dp]; + dpd.owner = owner; + dpd.isAdmin[owner] = true; + emit DataPointAllocated(dp, owner); + return dp; + } + + /// @inheritdoc IDataPointRegistry + function transferOwnership(DataPoint dp, address newOwner) external { + DPAccessData storage dpd = accessData[dp]; + address currentOwner = dpd.owner; + if (msg.sender != currentOwner) revert InvalidDataPointOwner(dp, msg.sender); + dpd.owner = newOwner; + emit DataPointOwnershipTransferred(dp, currentOwner, newOwner); + } + + /// @inheritdoc IDataPointRegistry + function grantAdminRole(DataPoint dp, address account) external returns (bool) { + DPAccessData storage dpd = accessData[dp]; + if (msg.sender != dpd.owner) revert InvalidDataPointOwner(dp, msg.sender); + if (!isAdmin(dp, account)) { + dpd.isAdmin[account] = true; + emit DataPointAdminGranted(dp, account); + return true; + } + return false; + } + + /// @inheritdoc IDataPointRegistry + function revokeAdminRole(DataPoint dp, address account) external returns (bool) { + DPAccessData storage dpd = accessData[dp]; + if (msg.sender != dpd.owner) revert InvalidDataPointOwner(dp, msg.sender); + if (isAdmin(dp, account)) { + dpd.isAdmin[account] = false; + emit DataPointAdminRevoked(dp, account); + return true; + } + return false; + } +} diff --git a/assets/erc-7208/contracts/datamanagers/MinimalisticERC1155WithERC20FractionsDataManager.sol b/assets/erc-7208/contracts/datamanagers/MinimalisticERC1155WithERC20FractionsDataManager.sol new file mode 100644 index 0000000000..cf8cb6daa7 --- /dev/null +++ b/assets/erc-7208/contracts/datamanagers/MinimalisticERC1155WithERC20FractionsDataManager.sol @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/Arrays.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol"; +import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import {IERC1155Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; +import "../interfaces/IDataIndex.sol"; +import "../interfaces/IDataObject.sol"; +import "./MinimalisticERC20FractionDataManagerFactory.sol"; +import "./MinimalisticERC20FractionDataManager.sol"; + +/** + * Deployment process + * 1. Allocate DataPoint via IDataPointRegistry.allocate() + * 2. Deploy ERC1155WithERC20FractionsDataManager (or an extending contract) + * 3. Grant Admin role on the DataPoint to the deployed contract + */ +contract MinimalisticERC1155WithERC20FractionsDataManager is IFractionTransferEventEmitter, IERC1155, IERC1155Errors, ERC165, Ownable { + using Arrays for uint256[]; + + error IncorrectId(uint256 id); + event ERC20FractionDataManagerDeployed(uint256 id, address dm); + + string private _name; + string private _symbol; + string private _defaultURI = ""; + DataPoint internal immutable datapoint; + IDataObject public immutable fungibleFractionsDO; + IDataIndex public dataIndex; + mapping(address account => mapping(address operator => bool)) private _operatorApprovals; + mapping(uint256 id => address erc20dm) public fractionManagersById; + mapping(address erc20dm => uint256 id) public fractionManagersByAddress; + MinimalisticERC20FractionDataManagerFactory erc20FractionsDMFactory; + + modifier onlyMinter() { + _checkMinter(); + _; + } + + constructor( + bytes32 _dp, + address _dataIndex, + address _fungibleFractionsDO, + address _erc20FractionsDMFactory, + string memory name_, + string memory symbol_ + ) Ownable(msg.sender) { + _name = name_; + _symbol = symbol_; + datapoint = DataPoint.wrap(_dp); + dataIndex = IDataIndex(_dataIndex); + fungibleFractionsDO = IDataObject(_fungibleFractionsDO); + erc20FractionsDMFactory = MinimalisticERC20FractionDataManagerFactory(_erc20FractionsDMFactory); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { + return interfaceId == type(IERC1155).interfaceId || interfaceId == type(IERC1155MetadataURI).interfaceId || super.supportsInterface(interfaceId); + } + + function setDefaultURI(string calldata defaultURI) external onlyOwner { + _setDefaultURI(defaultURI); + } + + function totalSupply() public view returns (uint256) { + return abi.decode(fungibleFractionsDO.read(datapoint, IFungibleFractionsOperations.totalSupplyAll.selector, ""), (uint256)); + } + + function totalSupply(uint256 id) public view returns (uint256) { + return abi.decode(fungibleFractionsDO.read(datapoint, IFungibleFractionsOperations.totalSupply.selector, abi.encode(id)), (uint256)); + } + + function balanceOf(address account, uint256 id) public view returns (uint256) { + return abi.decode(fungibleFractionsDO.read(datapoint, IFungibleFractionsOperations.balanceOf.selector, abi.encode(account, id)), (uint256)); + } + + function balanceOfBatch(address[] memory accounts, uint256[] memory ids) public view returns (uint256[] memory) { + return + abi.decode( + fungibleFractionsDO.read(datapoint, IFungibleFractionsOperations.balanceOfBatchAccounts.selector, abi.encode(accounts, ids)), + (uint256[]) + ); + } + + function uri(uint256) public view virtual returns (string memory) { + return _defaultURI; + } + + function name() public view returns (string memory) { + return _name; + } + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev See {IERC1155-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC1155-isApprovedForAll}. + */ + function isApprovedForAll(address account, address operator) public view virtual returns (bool) { + return _operatorApprovals[account][operator]; + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Emits an {ApprovalForAll} event. + * + * Requirements: + * + * - `operator` cannot be the zero address. + */ + function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { + if (operator == address(0)) { + revert ERC1155InvalidOperator(address(0)); + } + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev See {IERC1155-safeTransferFrom}. + */ + function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) public virtual { + address sender = _msgSender(); + if (from != sender && !isApprovedForAll(from, sender)) { + revert ERC1155MissingApprovalForAll(sender, from); + } + _safeTransferFrom(from, to, id, value, data); + } + + /** + * @dev See {IERC1155-safeBatchTransferFrom}. + */ + function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external virtual { + address sender = _msgSender(); + if (from != sender && !isApprovedForAll(from, sender)) { + revert ERC1155MissingApprovalForAll(sender, from); + } + _safeBatchTransferFrom(from, to, ids, values, data); + } + + /** + * @dev Transfers a `value` tokens of token type `id` from `from` to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `from` must have a balance of tokens of type `id` of at least `value` amount. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function _safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) internal { + if (to == address(0)) { + revert ERC1155InvalidReceiver(address(0)); + } + if (from == address(0)) { + revert ERC1155InvalidSender(address(0)); + } + _updateWithAcceptanceCheck(from, to, id, value, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + * - `ids` and `values` must have the same length. + */ + function _safeBatchTransferFrom(address from, address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal { + if (ids.length != values.length) { + revert ERC1155InvalidArrayLength(ids.length, values.length); + // DO NOT remove this check without refactoring ERC1155WithERC20FractionsDataManager._update() which relies on it! + } + + if (to == address(0)) { + revert ERC1155InvalidReceiver(address(0)); + } + if (from == address(0)) { + revert ERC1155InvalidSender(address(0)); + } + if (ids.length == 1 && values.length == 1) { + uint256 id = ids.unsafeMemoryAccess(0); + uint256 value = values.unsafeMemoryAccess(0); + _updateWithAcceptanceCheck(from, to, id, value, data); + } else { + _updateWithAcceptanceCheck(from, to, ids, values, data); + } + } + + /** + * @dev Version of {_update} that performs the token acceptance check by calling + * {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it + * contains code (eg. is a smart contract at the moment of execution). + * + * IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any + * update to the contract state after this function would break the check-effect-interaction pattern. Consider + * overriding {_update} instead. + */ + function _updateWithAcceptanceCheck(address from, address to, uint256 id, uint256 value, bytes memory data) internal virtual { + _update(from, to, id, value); + if (to != address(0)) { + address operator = _msgSender(); + _doSafeTransferAcceptanceCheck(operator, from, to, id, value, data); + } + } + + /** + * @dev Version of {_update} that performs the token acceptance check by calling + * {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it + * contains code (eg. is a smart contract at the moment of execution). + * + * IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any + * update to the contract state after this function would break the check-effect-interaction pattern. Consider + * overriding {_update} instead. + */ + function _updateWithAcceptanceCheck(address from, address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal virtual { + _update(from, to, ids, values); + if (to != address(0)) { + address operator = _msgSender(); + _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data); + } + } + + /** + * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from` + * (or `to`) is the zero address. + * + * Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise. + * + * Requirements: + * + * - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received} + * or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value. + * - `ids` and `values` must have the same length. + * + * NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead. + */ + function _updateInternal(address from, address to, uint256 id, uint256 value) internal virtual { + address operator = _msgSender(); + + _writeTransfer(from, to, id, value); + + emit TransferSingle(operator, from, to, id, value); + } + + /** + * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from` + * (or `to`) is the zero address. + * + * Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise. + * + * Requirements: + * + * - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received} + * or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value. + * - `ids` and `values` must have the same length. + * + * NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead. + * NOTE: Array length check is not performed in this function and must be performed in the caller + */ + function _updateInternal(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual { + address operator = _msgSender(); + + _writeTransferBatch(from, to, ids, values); + emit TransferBatch(operator, from, to, ids, values); + } + + /** + * @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address + * if it contains code at the moment of execution. + */ + function _doSafeTransferAcceptanceCheck(address operator, address from, address to, uint256 id, uint256 value, bytes memory data) private { + if (to.code.length > 0) { + try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) { + if (response != IERC1155Receiver.onERC1155Received.selector) { + // Tokens rejected + revert ERC1155InvalidReceiver(to); + } + } catch (bytes memory reason) { + if (reason.length == 0) { + // non-ERC1155Receiver implementer + revert ERC1155InvalidReceiver(to); + } else { + /// @solidity memory-safe-assembly + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } + } + + /** + * @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address + * if it contains code at the moment of execution. + */ + function _doSafeBatchTransferAcceptanceCheck( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory values, + bytes memory data + ) private { + if (to.code.length > 0) { + try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (bytes4 response) { + if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { + // Tokens rejected + revert ERC1155InvalidReceiver(to); + } + } catch (bytes memory reason) { + if (reason.length == 0) { + // non-ERC1155Receiver implementer + revert ERC1155InvalidReceiver(to); + } else { + /// @solidity memory-safe-assembly + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } + } + + function _setDefaultURI(string memory defaultURI) internal virtual { + _defaultURI = defaultURI; + } + + function _checkMinter() internal view { + _checkOwner(); + } + + function _writeTransfer(address from, address to, uint256 id, uint256 value) internal virtual { + if (from == address(0)) { + dataIndex.write(address(fungibleFractionsDO), datapoint, IFungibleFractionsOperations.mint.selector, abi.encode(to, id, value)); + } else if (to == address(0)) { + dataIndex.write(address(fungibleFractionsDO), datapoint, IFungibleFractionsOperations.burn.selector, abi.encode(from, id, value)); + } else { + dataIndex.write(address(fungibleFractionsDO), datapoint, IFungibleFractionsOperations.transferFrom.selector, abi.encode(from, to, id, value)); + } + } + + function _writeTransferBatch(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual { + if (from == address(0)) { + dataIndex.write(address(fungibleFractionsDO), datapoint, IFungibleFractionsOperations.batchMint.selector, abi.encode(to, ids, values)); + } else if (to == address(0)) { + dataIndex.write(address(fungibleFractionsDO), datapoint, IFungibleFractionsOperations.batchBurn.selector, abi.encode(from, ids, values)); + } else { + dataIndex.write(address(fungibleFractionsDO), datapoint, IFungibleFractionsOperations.batchTransferFrom.selector, abi.encode(from, to, ids, values)); + } + } + + function mint(address to, uint256 id, uint256 value, bytes memory data) public virtual onlyMinter { + _mint(to, id, value, data); + } + + function batchMint(address, uint256[] memory, uint256[] memory, bytes memory) public pure { + revert("Batch mint not supported"); // it will be very expensive anyway + } + + function fractionTransferredNotify(address from, address to, uint256 value) external { + uint256 id = fractionManagersByAddress[_msgSender()]; + if (id == 0) revert WrongTransferNotificationSource(); + emit TransferSingle(_msgSender(), from, to, id, value); + } + + function _mint(address to, uint256 id, uint256 value, bytes memory data) internal virtual { + if (id == 0) revert IncorrectId(id); + _deployERC20DMIfNotDeployed(id, data); + _updateWithAcceptanceCheck(address(0), to, id, value, data); + } + + function _update(address from, address to, uint256 id, uint256 value) internal { + _updateInternal(from, to, id, value); + _erc20TransferNotify(id, from, to, value); + } + + function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal { + _updateInternal(from, to, ids, values); + for (uint256 i; i < ids.length; i++) { + uint256 id = ids.unsafeMemoryAccess(i); + uint256 value = ids.unsafeMemoryAccess(i); // We have an array length check in ERC1155Transfers._safeBatchTransferFrom() + _erc20TransferNotify(id, from, to, value); + } + } + + function _deployERC20DMIfNotDeployed(uint256 id, bytes memory data) internal { + if (fractionManagersById[id] != address(0)) return; // Already deployed + (string memory name_, string memory symbol_) = _prepareNameAndSymbol(data, id); + _deployERC20DM(id, name_, symbol_); + } + + function _afterDeployERC20DM(address deployedDM) internal virtual {} + + function _prepareNameAndSymbol(bytes memory data, uint256 id) private view returns (string memory, string memory) { + string memory name_; + string memory symbol_; + if (data.length != 0) { + (name_, symbol_) = abi.decode(data, (string, string)); + } else { + name_ = string.concat(name(), " ", Strings.toString(id)); + symbol_ = string.concat(symbol(), "-", Strings.toString(id)); + } + return (name_, symbol_); + } + + function _deployERC20DM(uint256 id, string memory name_, string memory symbol_) private { + address erc20dm = erc20FractionsDMFactory.deploy(id); + MinimalisticERC20FractionDataManager(erc20dm).initialize( + DataPoint.unwrap(datapoint), + address(dataIndex), + address(fungibleFractionsDO), + address(this), + id, + name_, + symbol_ + ); + + fractionManagersById[id] = erc20dm; + fractionManagersByAddress[erc20dm] = id; + + dataIndex.allowDataManager(datapoint, erc20dm, true); + + _afterDeployERC20DM(erc20dm); + + emit ERC20FractionDataManagerDeployed(id, erc20dm); + } + + function _erc20TransferNotify(uint256 id, address from, address to, uint256 value) private { + IFractionTransferEventEmitter(fractionManagersById[id]).fractionTransferredNotify(from, to, value); + } +} diff --git a/assets/erc-7208/contracts/datamanagers/MinimalisticERC20FractionDataManager.sol b/assets/erc-7208/contracts/datamanagers/MinimalisticERC20FractionDataManager.sol new file mode 100644 index 0000000000..93aa92d016 --- /dev/null +++ b/assets/erc-7208/contracts/datamanagers/MinimalisticERC20FractionDataManager.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "../interfaces/IDataIndex.sol"; +import "../interfaces/IDataObject.sol"; +import "../interfaces/IFungibleFractionsOperations.sol"; +import "../interfaces/IFractionTransferEventEmitter.sol"; + +contract MinimalisticERC20FractionDataManager is IFractionTransferEventEmitter, IERC20, IERC20Errors, OwnableUpgradeable { + uint8 private constant DECIMALS = 0; + + DataPoint internal datapoint; + IDataObject public fungibleFractionsDO; + IDataIndex public dataIndex; + address public erc1155dm; + uint256 public erc1155ID; + + string private _name; + string private _symbol; + + mapping(address account => mapping(address spender => uint256)) private _allowances; + + modifier onlyTransferNotifier() { + if (_msgSender() != erc1155dm) revert WrongTransferNotificationSource(); + _; + } + + function initialize( + bytes32 _datapoint, + address _dataIndex, + address _fungibleFractionsDO, + address _erc1155dm, + uint256 _erc1155ID, + string memory name_, + string memory symbol_ + ) external initializer { + __Ownable_init_unchained(_msgSender()); + __MinimalisticERC20FractionDataManager_init_unchained(_datapoint, _dataIndex, _fungibleFractionsDO, _erc1155dm, _erc1155ID, name_, symbol_); + } + + function __MinimalisticERC20FractionDataManager_init_unchained( + bytes32 _datapoint, + address _dataIndex, + address _fungibleFractionsDO, + address _erc1155dm, + uint256 _erc1155ID, + string memory name_, + string memory symbol_ + ) internal onlyInitializing { + datapoint = DataPoint.wrap(_datapoint); + dataIndex = IDataIndex(_dataIndex); + fungibleFractionsDO = IDataObject(_fungibleFractionsDO); + erc1155dm = _erc1155dm; + erc1155ID = _erc1155ID; + _name = name_; + _symbol = symbol_; + } + + function decimals() external pure returns (uint8) { + return DECIMALS; + } + + function name() external view returns (string memory) { + return _name; + } + + function symbol() external view returns (string memory) { + return _symbol; + } + + function totalSupply() external view override returns (uint256) { + return abi.decode(fungibleFractionsDO.read(datapoint, IFungibleFractionsOperations.totalSupply.selector, abi.encode(erc1155ID)), (uint256)); + } + + function balanceOf(address account) external view override returns (uint256) { + return abi.decode(fungibleFractionsDO.read(datapoint, IFungibleFractionsOperations.balanceOf.selector, abi.encode(account, erc1155ID)), (uint256)); + } + + function fractionTransferredNotify(address from, address to, uint256 amount) external onlyTransferNotifier { + emit Transfer(from, to, amount); + } + + function allowance(address owner, address spender) public view returns (uint256) { + return _allowances[owner][spender]; + } + + function approve(address spender, uint256 value) public returns (bool) { + if (_msgSender() == address(0)) { + revert ERC20InvalidApprover(address(0)); + } + if (spender == address(0)) { + revert ERC20InvalidSpender(address(0)); + } + _allowances[_msgSender()][spender] = value; + emit Approval(_msgSender(), spender, value); + return true; + } + + function transfer(address to, uint256 amount) external virtual override returns (bool) { + if (to == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _beforeTokenTransfer(_msgSender(), to, amount); + + _writeTransfer(_msgSender(), to, amount); + + emit Transfer(_msgSender(), to, amount); + return true; + } + + function transferFrom(address from, address to, uint256 amount) external virtual override returns (bool) { + _spendAllowance(from, _msgSender(), amount); + _beforeTokenTransfer(from, to, amount); + + if (from == address(0)) { + revert ERC20InvalidSender(address(0)); + } + if (to == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + + _writeTransfer(from, to, amount); + + emit Transfer(from, to, amount); + return true; + } + + function _spendAllowance(address owner, address spender, uint256 amount) internal { + uint256 currentAllowance = _allowances[owner][spender]; + if (currentAllowance != type(uint256).max) { + if (currentAllowance < amount) { + revert ERC20InsufficientAllowance(spender, currentAllowance, amount); + } + unchecked { + _allowances[owner][spender] = currentAllowance - amount; + } + } + } + + function _writeTransfer(address from, address to, uint256 amount) internal { + if (from == address(0)) { + dataIndex.write(address(fungibleFractionsDO), datapoint, IFungibleFractionsOperations.mint.selector, abi.encode(to, erc1155ID, amount)); + } else if (to == address(0)) { + dataIndex.write(address(fungibleFractionsDO), datapoint, IFungibleFractionsOperations.burn.selector, abi.encode(from, erc1155ID, amount)); + } else { + dataIndex.write(address(fungibleFractionsDO), datapoint, IFungibleFractionsOperations.transferFrom.selector, abi.encode(from, to, erc1155ID, amount)); + } + IFractionTransferEventEmitter(erc1155dm).fractionTransferredNotify(from, to, amount); + } + + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} +} diff --git a/assets/erc-7208/contracts/datamanagers/MinimalisticERC20FractionDataManagerFactory.sol b/assets/erc-7208/contracts/datamanagers/MinimalisticERC20FractionDataManagerFactory.sol new file mode 100644 index 0000000000..b030746d24 --- /dev/null +++ b/assets/erc-7208/contracts/datamanagers/MinimalisticERC20FractionDataManagerFactory.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/utils/Create2.sol"; +import "./MinimalisticERC20FractionDataManager.sol"; + +contract MinimalisticERC20FractionDataManagerFactory { + function deploy(uint256 id) external returns (address) { + bytes32 salt = keccak256(abi.encodePacked(msg.sender, id)); + return Create2.deploy(0, salt, type(MinimalisticERC20FractionDataManager).creationCode); + } +} diff --git a/assets/erc-7208/contracts/dataobjects/MinimalisticFungibleFractionsDO.sol b/assets/erc-7208/contracts/dataobjects/MinimalisticFungibleFractionsDO.sol new file mode 100644 index 0000000000..a15ccf6b04 --- /dev/null +++ b/assets/erc-7208/contracts/dataobjects/MinimalisticFungibleFractionsDO.sol @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "@openzeppelin/contracts/utils/Arrays.sol"; +import "../interfaces/IIDManager.sol"; +import "../interfaces/IFungibleFractionsOperations.sol"; +import "../interfaces/IDataPointRegistry.sol"; +import "../interfaces/IDataIndex.sol"; +import "../interfaces/IDataObject.sol"; +import "../utils/OmnichainAddresses.sol"; + +/** + * @title Minimalistic Fungible Fractions Data Object + * @notice DataObject with base funtionality of Fungible Fractions (Can be used for ERC1155-Compatible DataManagers) + * @dev This contract exposes base functionality of Fungible Fraction tokens, including + * balanceOf, totalSupply, exists, transferFrom, mint, burn and their batch variants. + * + * NOTE: This contract is expected to be used by a DataManager contract, which could + * implement a fungible token interface and provide more advanced features like approvals, + * access control, metadata management, etc. As may be an ERC1155 token. + * + * This contract only emit basic events, it is expected that the DataManager contract will + * emit the events for the token operations + */ +contract MinimalisticFungibleFractionsDO is IDataObject { + using Arrays for uint256[]; + using Arrays for address[]; + using EnumerableSet for EnumerableSet.UintSet; + + /** + * @notice Error thrown when the msg.sender is not the expected caller + * @param dp The DataPoint identifier + * @param sender The msg.sender address + */ + error InvalidCaller(DataPoint dp, address sender); + + /** + * @notice Error thrown when the DataPoint is not initialized with a DataIndex implementation + * @param dp The DataPoint identifier + */ + error UninitializedDataPoint(DataPoint dp); + + /// @dev Error thrown when the operation arguments are wrong + error WrongOperationArguments(); + + /** + * @notice Error thrown when the read operation is unknown + * @param selector The operation selector + */ + error UnknownReadOperation(bytes4 selector); + + /** + * @notice Error thrown when the write operation is unknown + * @param selector The operation selector + */ + error UnknownWriteOperation(bytes4 selector); + + /** + * @notice Error thrown when the balance is insufficient + * @param diid The DataIndex identifier + * @param id The id of the token + * @param balance The current balance + * @param value The requested amount + */ + error InsufficientBalance(bytes32 diid, uint256 id, uint256 balance, uint256 value); + + /** + * @notice Error thrown when the total supply is insufficient + * @param id The id of the token + * @param totalSupply The current total supply + * @param value The requested amount + * @dev This should never happen because we've already checked "from" balance + */ + error InsufficientTotalSupply(uint256 id, uint256 totalSupply, uint256 value); + + /// @dev Error thrown when the params length mismatch + error ArrayLengthMismatch(); + + /** + * @notice Event emitted when the DataIndex implementation is set + * @param dp The DataPoint identifier + * @param dataIndexImplementation The DataIndex implementation address + */ + event DataIndexImplementationSet(DataPoint dp, address dataIndexImplementation); + + /** + * @notice Data structure for storing Fungible Fractions data + * @param totalSupplyAll Total supply of all tokens + * @param totalSupply Mapping of token id to total supply + * @dev Data related to the DataPoint as a whole + */ + struct DpData { + uint256 totalSupplyAll; + mapping(uint256 id => uint256 totalSupplyOfId) totalSupply; + } + + /** + * @notice Data structure for storing Fungible Fractions data of a user + * @param ids Enumerable set of object (ERC1155 token) ids + * @param balances Mapping of object (ERC1155 token) id to balance of the user owning diid + * @dev Data related to a specific user of a DataPoint (user identified by his DataIndex id) + */ + struct DiidData { + EnumerableSet.UintSet ids; + mapping(uint256 id => uint256 value) balances; + } + + /** + * @notice Data structure to store DataPoint data + * @param dataIndexImplementation The DataIndex implementation set for the DataPoint + * @param dpData The DataPoint data + * @param dataIndexData Mapping of diid to user data + */ + struct DataPointStorage { + IDataIndex dataIndexImplementation; + DpData dpData; + mapping(bytes32 diid => DiidData) diidData; + } + + /// @dev Mapping of DataPoint to DataPointStorage + mapping(DataPoint => DataPointStorage) private dpStorages; + + /** + * @notice Modifier to check if the caller is the DataIndex implementation set for the DataPoint + * @param dp The DataPoint identifier + */ + modifier onlyDataIndex(DataPoint dp) { + DataPointStorage storage dps = _dataPointStorage(dp); + if (address(dps.dataIndexImplementation) != msg.sender) revert InvalidCaller(dp, msg.sender); + _; + } + + /// @inheritdoc IDataObject + function setDIImplementation(DataPoint dp, IDataIndex newImpl) external { + DataPointStorage storage dps = dpStorages[dp]; + if (address(dps.dataIndexImplementation) == address(0)) { + // Registering new DataPoint + // Should be called by DataPoint Admin + if (!_isDataPointAdmin(dp, msg.sender)) revert InvalidCaller(dp, msg.sender); + } else { + // Updating the DataPoint + // Should be called by current DataIndex or DataPoint Admin + if ((address(dps.dataIndexImplementation) != msg.sender) && !_isDataPointAdmin(dp, msg.sender)) revert InvalidCaller(dp, msg.sender); + } + dps.dataIndexImplementation = newImpl; + emit DataIndexImplementationSet(dp, address(newImpl)); + } + + // =========== Dispatch functions ============ + /// @inheritdoc IDataObject + function read(DataPoint dp, bytes4 operation, bytes calldata data) external view returns (bytes memory) { + return _dispatchRead(dp, operation, data); + } + + /// @inheritdoc IDataObject + function write(DataPoint dp, bytes4 operation, bytes calldata data) external onlyDataIndex(dp) returns (bytes memory) { + return _dispatchWrite(dp, operation, data); + } + + function _dispatchRead(DataPoint dp, bytes4 operation, bytes calldata data) internal view virtual returns (bytes memory) { + if (operation == IFungibleFractionsOperations.balanceOf.selector) { + (address account, uint256 id) = abi.decode(data, (address, uint256)); + return abi.encode(_balanceOf(dp, account, id)); + } else if (operation == IFungibleFractionsOperations.balanceOfBatchAccounts.selector) { + (address[] memory accounts, uint256[] memory ids) = abi.decode(data, (address[], uint256[])); + return abi.encode(_balanceOfBatchAccounts(dp, accounts, ids)); + } else if (operation == IFungibleFractionsOperations.totalSupply.selector) { + return abi.encode(_totalSupply(dp, abi.decode(data, (uint256)))); + } else if (operation == IFungibleFractionsOperations.totalSupplyAll.selector) { + if (data.length != 0) revert WrongOperationArguments(); + return abi.encode(_totalSupplyAll(dp)); + } else if (operation == IFungibleFractionsOperations.exists.selector) { + return abi.encode(_exists(dp, abi.decode(data, (uint256)))); + } else { + revert UnknownReadOperation(operation); + } + } + + function _dispatchWrite(DataPoint dp, bytes4 operation, bytes calldata data) internal virtual returns (bytes memory) { + if (operation == IFungibleFractionsOperations.transferFrom.selector) { + (address from, address to, uint256 id, uint256 value) = abi.decode(data, (address, address, uint256, uint256)); + _transferFrom(dp, from, to, id, value); + return ""; + } else if (operation == IFungibleFractionsOperations.mint.selector) { + (address to, uint256 id, uint256 value) = abi.decode(data, (address, uint256, uint256)); + _mint(dp, to, id, value); + return ""; + } else if (operation == IFungibleFractionsOperations.burn.selector) { + (address from, uint256 id, uint256 value) = abi.decode(data, (address, uint256, uint256)); + _burn(dp, from, id, value); + return ""; + } else if (operation == IFungibleFractionsOperations.batchTransferFrom.selector) { + (address from, address to, uint256[] memory ids, uint256[] memory values) = abi.decode(data, (address, address, uint256[], uint256[])); + _batchTransferFrom(dp, from, to, ids, values); + return ""; + } else { + revert UnknownWriteOperation(operation); + } + } + + // =========== Logic implementation ============ + + function _balanceOf(DataPoint dp, address account, uint256 id) internal view returns (uint256) { + bytes32 diid = _tryDiid(dp, account); + if (diid == 0) return 0; + (bool success, DiidData storage od) = _tryDiidData(dp, diid); + return success ? od.balances[id] : 0; + } + + function _balanceOfBatchAccounts(DataPoint dp, address[] memory accounts, uint256[] memory ids) internal view returns (uint256[] memory balances) { + if (accounts.length != ids.length) revert ArrayLengthMismatch(); + balances = new uint256[](accounts.length); + for (uint256 i; i < accounts.length; i++) { + uint256 id = ids.unsafeMemoryAccess(i); + address account = accounts.unsafeMemoryAccess(i); + bytes32 diid = _tryDiid(dp, account); + if (diid == 0) { + balances[i] = 0; + } else { + (bool success, DiidData storage od) = _tryDiidData(dp, diid); + balances[i] = success ? od.balances[id] : 0; + } + } + } + + function _totalSupply(DataPoint dp, uint256 id) internal view returns (uint256) { + (bool success, DpData storage dd) = _tryDpData(dp); + return success ? dd.totalSupply[id] : 0; + } + + function _totalSupplyAll(DataPoint dp) internal view returns (uint256) { + (bool success, DpData storage dd) = _tryDpData(dp); + return success ? dd.totalSupplyAll : 0; + } + + function _exists(DataPoint dp, uint256 id) internal view returns (bool) { + (bool success, DpData storage dd) = _tryDpData(dp); + return success ? (dd.totalSupply[id] > 0) : false; + } + + function _transferFrom(DataPoint dp, address from, address to, uint256 id, uint256 value) internal virtual { + bytes32 diidFrom = _diid(dp, from); + bytes32 diidTo = _diid(dp, to); + DiidData storage diiddFrom = _diidData(dp, diidFrom); + DiidData storage diiddTo = _diidData(dp, diidTo); + _decreaseBalance(diiddFrom, id, value, dp, diidFrom); + _increaseBalance(diiddTo, id, value, dp, diidTo); + } + + function _mint(DataPoint dp, address to, uint256 id, uint256 value) internal virtual { + bytes32 diidTo = _diid(dp, to); + DiidData storage diiddTo = _diidData(dp, diidTo); + _increaseBalance(diiddTo, id, value, dp, diidTo); + + DpData storage dpd = _dpData(dp); + dpd.totalSupply[id] += value; + dpd.totalSupplyAll += value; + } + + function _burn(DataPoint dp, address from, uint256 id, uint256 value) internal virtual { + bytes32 diidFrom = _diid(dp, from); + DiidData storage diiddFrom = _diidData(dp, diidFrom); + _decreaseBalance(diiddFrom, id, value, dp, diidFrom); + DpData storage dpd = _dpData(dp); + uint256 totalSupply = dpd.totalSupply[id]; + if (totalSupply < value) revert InsufficientTotalSupply(id, totalSupply, value); + unchecked { + totalSupply -= value; + } + dpd.totalSupply[id] = totalSupply; + uint256 totalSupplyAll = dpd.totalSupplyAll; + if (totalSupplyAll < value) revert InsufficientTotalSupply(id, totalSupplyAll, value); + unchecked { + totalSupplyAll -= value; + } + dpd.totalSupplyAll = totalSupplyAll; + } + + function _batchTransferFrom(DataPoint dp, address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual { + if (ids.length != values.length) revert ArrayLengthMismatch(); + bytes32 diidFrom = _diid(dp, from); + bytes32 diidTo = _diid(dp, to); + DiidData storage diiddFrom = _diidData(dp, diidFrom); + DiidData storage diiddTo = _diidData(dp, diidTo); + for (uint256 i; i < ids.length; i++) { + uint256 id = ids.unsafeMemoryAccess(i); + uint256 value = values.unsafeMemoryAccess(i); + _decreaseBalance(diiddFrom, id, value, dp, diidFrom); + _increaseBalance(diiddTo, id, value, dp, diidTo); + } + } + + function _increaseBalance(DiidData storage diidd, uint256 id, uint256 value, DataPoint, bytes32) private { + diidd.balances[id] += value; + diidd.ids.add(id); // if id is already in the set, this call will return false, but we don't care + } + + function _decreaseBalance(DiidData storage diidd, uint256 id, uint256 value, DataPoint, bytes32 diidFrom) private { + uint256 diidBalance = diidd.balances[id]; + if (diidBalance < value) { + revert InsufficientBalance(diidFrom, id, diidBalance, value); + } else { + unchecked { + diidBalance -= value; + } + diidd.balances[id] = diidBalance; + if (diidBalance == 0) { + diidd.ids.remove(id); + } + } + } + + // =========== Helper functions ============ + + function _isDataPointAdmin(DataPoint dp, address account) internal view returns (bool) { + (uint32 chainId, address registry, ) = DataPoints.decode(dp); + ChainidTools.requireCurrentChain(chainId); + return IDataPointRegistry(registry).isAdmin(dp, account); + } + + function _diid(DataPoint dp, address account) internal view returns (bytes32) { + return IIDManager(msg.sender).diid(account, dp); + } + + function _tryDiid(DataPoint dp, address account) internal view returns (bytes32) { + try IIDManager(msg.sender).diid(account, dp) returns (bytes32 diid) { + return diid; + } catch { + return 0; + } + } + + function _dpData(DataPoint dp) internal view returns (DpData storage) { + DataPointStorage storage dps = _dataPointStorage(dp); + return dps.dpData; + } + + function _diidData(DataPoint dp, bytes32 diid) internal view returns (DiidData storage) { + DataPointStorage storage dps = _dataPointStorage(dp); + return dps.diidData[diid]; + } + + function _tryDpData(DataPoint dp) internal view returns (bool success, DpData storage) { + (bool found, DataPointStorage storage dps) = _tryDataPointStorage(dp); + if (!found) { + return (false, dps.dpData); + } + return (true, dps.dpData); + } + + function _tryDiidData(DataPoint dp, bytes32 diid) internal view returns (bool success, DiidData storage) { + (bool found, DataPointStorage storage dps) = _tryDataPointStorage(dp); + if (!found) { + return (false, dps.diidData[bytes32(0)]); + } + DiidData storage diidd = dps.diidData[diid]; + if (diidd.ids.length() == 0) { + // Here we use length of ids array as a flag that there is no data for the diid + return (false, diidd); + } + return (true, diidd); + } + + function _dataPointStorage(DataPoint dp) private view returns (DataPointStorage storage) { + DataPointStorage storage dps = dpStorages[dp]; + if (address(dps.dataIndexImplementation) == address(0)) { + revert UninitializedDataPoint(dp); + } + return dpStorages[dp]; + } + + function _tryDataPointStorage(DataPoint dp) private view returns (bool success, DataPointStorage storage) { + DataPointStorage storage dps = dpStorages[dp]; + if (address(dps.dataIndexImplementation) == address(0)) { + return (false, dps); + } + return (true, dps); + } +} diff --git a/assets/erc-7208/contracts/interfaces/IDataIndex.sol b/assets/erc-7208/contracts/interfaces/IDataIndex.sol new file mode 100644 index 0000000000..45dc3a6b29 --- /dev/null +++ b/assets/erc-7208/contracts/interfaces/IDataIndex.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../utils/DataPoints.sol"; + +/** + * @title Data Index interface + * @notice The interface defines functions to manage the access control of DataManagers to + * DataPoints as well as to the data related to these DataPoints in specific dataObjects + */ +interface IDataIndex { + /** + * @notice Verifies if DataManager is allowed to write in specific DataPoint + * @param dp Identifier of the DataPoint + * @param dm Address of DataManager + * @return if write access is allowed + */ + function isApprovedDataManager(DataPoint dp, address dm) external view returns (bool); + + /** + * @notice Defines if DataManager is allowed to write in specific DataPoint + * @param dp Identifier of the DataPoint + * @param dm Address of DataManager + * @param approved if DataManager should be approved for the DataPoint + * @dev Function SHOULD be restricted to DataPoint maintainer only + */ + function allowDataManager(DataPoint dp, address dm, bool approved) external; + + /** + * @notice Reads stored data + * @param dobj Identifier of DataObject + * @param dp Identifier of the DataPoint + * @param operation Read operation to execute on the data + * @param data Operation-specific data + * @return Operation-specific data + */ + function read(address dobj, DataPoint dp, bytes4 operation, bytes calldata data) external view returns (bytes memory); + + /** + * @notice Stores data + * @param dobj Identifier of DataObject + * @param dp Identifier of the DataPoint + * @param operation Write operation to execute on the data + * @param data Operation-specific data + * @return Operation-specific data (can be empty) + * @dev Function SHOULD be restricted to allowed DMs only + */ + function write(address dobj, DataPoint dp, bytes4 operation, bytes calldata data) external returns (bytes memory); +} diff --git a/assets/erc-7208/contracts/interfaces/IDataObject.sol b/assets/erc-7208/contracts/interfaces/IDataObject.sol new file mode 100644 index 0000000000..6f5430f14d --- /dev/null +++ b/assets/erc-7208/contracts/interfaces/IDataObject.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../utils/DataPoints.sol"; +import "./IDataIndex.sol"; + +/** + * @title Data Object Interface + * @notice Interface defines functions to manage the DataIndex implementation and the data + * stored in DataObjects and associated with DataPoints + */ +interface IDataObject { + /** + * @notice Reads stored data + * @param dp Identifier of the DataPoint + * @param operation Read operation to execute on the data + * @param data Operation-specific data + * @return Operation-specific data + */ + function read(DataPoint dp, bytes4 operation, bytes calldata data) external view returns (bytes memory); + + /** + * @notice Store data + * @param dp Identifier of the DataPoint + * @param operation Read operation to execute on the data + * @param data Operation-specific data + * @return Operation-specific data (can be empty) + */ + function write(DataPoint dp, bytes4 operation, bytes calldata data) external returns (bytes memory); + + /** + * @notice Sets DataIndex Implementation + * @param dp Identifier of the DataPoint + * @param newImpl address of the new DataIndex implementation + */ + function setDIImplementation(DataPoint dp, IDataIndex newImpl) external; +} diff --git a/assets/erc-7208/contracts/interfaces/IDataPointRegistry.sol b/assets/erc-7208/contracts/interfaces/IDataPointRegistry.sol new file mode 100644 index 0000000000..6e9483064d --- /dev/null +++ b/assets/erc-7208/contracts/interfaces/IDataPointRegistry.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {DataPoint} from "../utils/DataPoints.sol"; + +/** + * @title Data Point Registry Interface + * @notice Interface defines functions to manage creation, transfer and access control of DataPoints + */ +interface IDataPointRegistry { + /** + * @notice Event emitted when a DataPoint is allocated + * @param dp DataPoint identifier + * @param owner Owner of the DataPoint + */ + event DataPointAllocated(DataPoint indexed dp, address owner); + + /** + * @notice Event emitted when ownership of a DataPoint is transferred + * @param dp DataPoint identifier + * @param previousOwner Previous owner + * @param newOwner New owner + */ + event DataPointOwnershipTransferred(DataPoint indexed dp, address previousOwner, address newOwner); + + /** + * @notice Event emitted when Admin role is granted + * @param dp DataPoint identifier + * @param account Account granted with Admin role + */ + event DataPointAdminGranted(DataPoint indexed dp, address account); + + /** + * @notice Event emitted when Admin role is revoked + * @param dp DataPoint identifier + * @param account Account revoked from Admin role + */ + event DataPointAdminRevoked(DataPoint indexed dp, address account); + + /// @dev Error thrown when DataPoint allocation counter overflows + error CounterOverflow(); + + /// @dev Error thrown when native coin is sent in allocation + error NativeCoinDepositIsNotAccepted(); + + /** + * @notice Error thrown when caller is not the owner of the DataPoint + * @param dp DataPoint identifier + * @param owner Invalid owner + */ + error InvalidDataPointOwner(DataPoint dp, address owner); + + /** + * @notice Verifies if an address has an Admin role for a DataPoint + * @param dp DataPoint + * @param account Account to verify + */ + function isAdmin(DataPoint dp, address account) external view returns (bool); + + /** + * @notice Allocates a DataPoint to an owner + * @param owner Owner of the new DataPoint + * @dev Owner SHOULD be granted Admin role during allocation + */ + function allocate(address owner) external payable returns (DataPoint); + + /** + * @notice Transfers ownership of a DataPoint to a new owner + * @param dp DataPoint identifier + * @param newOwner New owner + */ + function transferOwnership(DataPoint dp, address newOwner) external; + + /** + * @notice Grant permission to grant/revoke other roles on the DataPoint inside an DataIndex Implementation + * This is useful if DataManagers are deployed during lifecycle of the application. + * @param dp DataPoint identifier + * @param account New admin + * @return If the role was granted (otherwise account already had the role) + */ + function grantAdminRole(DataPoint dp, address account) external returns (bool); + + /** + * @notice Revoke permission to grant/revoke other roles on the DataPoint inside an DataIndex Implementation + * @param dp DataPoint identifier + * @param account Old admin + * @dev If an owner revokes Admin role from himself, he can add it again + * @return If the role was revoked (otherwise account didn't had the role) + */ + function revokeAdminRole(DataPoint dp, address account) external returns (bool); +} diff --git a/assets/erc-7208/contracts/interfaces/IFractionTransferEventEmitter.sol b/assets/erc-7208/contracts/interfaces/IFractionTransferEventEmitter.sol new file mode 100644 index 0000000000..fd80a33a39 --- /dev/null +++ b/assets/erc-7208/contracts/interfaces/IFractionTransferEventEmitter.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title Fraction Transfer Event Emitter interface + * @notice Interface defines functions to emit events when fractions are transferred + */ +interface IFractionTransferEventEmitter { + /// @dev Emmited when caller is not one of expected addresses + error WrongTransferNotificationSource(); + + /** + * @notice Notifies about a fraction transfer + * @param from Address from which the fraction is transferred + * @param to Address to which the fraction is transferred + * @param value Amount of the fraction transferred + * @dev Should emit an event with the information about the transfer + */ + function fractionTransferredNotify(address from, address to, uint256 value) external; +} diff --git a/assets/erc-7208/contracts/interfaces/IFungibleFractionsOperations.sol b/assets/erc-7208/contracts/interfaces/IFungibleFractionsOperations.sol new file mode 100644 index 0000000000..9442f1c3fa --- /dev/null +++ b/assets/erc-7208/contracts/interfaces/IFungibleFractionsOperations.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title Fungible Fractions Operations Interface + * @notice Provides the operations of the FungibleFractionsDO to interact with + * fungible fractions tokens and their associated data + */ +interface IFungibleFractionsOperations { + /** + * @notice Operation used to get the balance of an account + * @param account The account address + * @param id The id of the token + * @return The balance of the account + */ + function balanceOf(address account, uint256 id) external view returns (uint256); + + /** + * @notice Operation used to get the balance of an account + * @param account The account address + * @param ids The ids of the tokens + * @return The balance of the account + */ + function balanceOfBatch(address account, uint256[] calldata ids) external view returns (uint256[] memory); + + /** + * @notice Operation used to get the balance of multiple accounts + * @param accounts The account addresses + * @param ids The ids of the tokens + * @return The balance of the accounts + */ + function balanceOfBatchAccounts(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); + + /** + * @notice Operation used to get the total supply of a token + * @param id The id of the token + * @return The total supply of the token + */ + function totalSupply(uint256 id) external view returns (uint256); + + /** + * @notice Operation used to get the total supply of all ids tokens + * @return The total supply of all ids tokens + */ + function totalSupplyAll() external view returns (uint256); + + /** + * @notice Operation used to check if an id exists + * @param id The id of the token + * @return True if the id exists, false otherwise + */ + function exists(uint256 id) external view returns (bool); + + /** + * @notice Operation used to transfer tokens from one account to another + * @param from The account to transfer from + * @param to The account to transfer to + * @param id The id of the token + * @param value The amount of tokens to transfer + */ + function transferFrom(address from, address to, uint256 id, uint256 value) external; + + /** + * @notice Operation used to transfer tokens from one account to another + * @param from The account to transfer from + * @param to The account to transfer to + * @param ids The ids of the tokens + * @param values The amounts of tokens to transfer + */ + function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata values) external; + + /** + * @notice Operation used to mint tokens + * @param to The account to mint to + * @param id The id of the token + * @param value The amount of tokens to mint + */ + function mint(address to, uint256 id, uint256 value) external; + + /** + * @notice Operation used to burn tokens + * @param from The account to burn from + * @param id The id of the token + * @param value The amount of tokens to burn + */ + function burn(address from, uint256 id, uint256 value) external; + + /** + * @notice Operation used to mint tokens + * @param to The account to mint to + * @param ids The ids of the tokens + * @param values The amounts of tokens to mint + */ + function batchMint(address to, uint256[] calldata ids, uint256[] calldata values) external; + + /** + * @notice Operation used to burn tokens + * @param from The account to burn from + * @param ids The ids of the tokens + * @param values The amounts of tokens to burn + */ + function batchBurn(address from, uint256[] calldata ids, uint256[] calldata values) external; +} diff --git a/assets/erc-7208/contracts/interfaces/IIDManager.sol b/assets/erc-7208/contracts/interfaces/IIDManager.sol new file mode 100644 index 0000000000..ea162eb17f --- /dev/null +++ b/assets/erc-7208/contracts/interfaces/IIDManager.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {DataPoint} from "../utils/DataPoints.sol"; + +/** + * @title ID Manager Interface + * @notice Interface defines functions to build DataIndex user identifiers and get information about them + */ +interface IIDManager { + /** + * @notice Provides DataIndex id for a specific account for a specific DataPoint + * @param account Address of the user + * @param dp DataPoint the id should be linked with + * @return DataIndex identifier + * @dev If no token available, this function REVERTS + */ + function diid(address account, DataPoint dp) external view returns (bytes32); + + /** + * @notice Provides information about owner of specific DataIndex id + * @param diid DataIndex id to get info for + * @return chainid of owner's address + * @return owner's address + */ + function ownerOf(bytes32 diid) external view returns (uint32, address); +} diff --git a/assets/erc-7208/contracts/utils/ChainidTools.sol b/assets/erc-7208/contracts/utils/ChainidTools.sol new file mode 100644 index 0000000000..163b6a7877 --- /dev/null +++ b/assets/erc-7208/contracts/utils/ChainidTools.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title ChainidTools library + * @notice This library provides helper to convert uint256 chainid provided by block.chainid to uint32 + * chainid used accross this DataIndex implementation + */ +library ChainidTools { + /// @dev Error thrown when chainid is not supported + error UnsupportedChain(uint256 chainId); + + /// @dev Error thrown when chainid is not the current chain + error UnexpectedChain(uint32 expected, uint32 requested); + + /// @dev Error thrown when chainid is not the expected chain + error DifferentChainExpected(uint256 chainId); + + /** + * @notice Converts block.chainid to uint32 chainid + * @return uint32 chainid + */ + function chainid() internal view returns (uint32) { + if (block.chainid < type(uint32).max) { + return uint32(block.chainid); + } + revert UnsupportedChain(block.chainid); + } + + /** + * @notice Requires current chain to be the same as requested + * @param chainId Requested chain ID + */ + function requireCurrentChain(uint32 chainId) internal view { + uint32 currentChain = uint32(block.chainid); + if (currentChain != chainId) revert UnexpectedChain(currentChain, chainId); + } + + /** + * @notice Requires current chain to be different than requested + * @param chainId Requested chain ID + */ + function requireNotCurrentChain(uint32 chainId) internal view { + if (block.chainid == 31337) return; // Allow LayerZero Endpoint Mock to be used on Hardhat testnet + uint32 currentChain = uint32(block.chainid); + if (currentChain == chainId) revert DifferentChainExpected(chainId); + } +} diff --git a/assets/erc-7208/contracts/utils/DataPoints.sol b/assets/erc-7208/contracts/utils/DataPoints.sol new file mode 100644 index 0000000000..a89b0c5023 --- /dev/null +++ b/assets/erc-7208/contracts/utils/DataPoints.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ChainidTools} from "./ChainidTools.sol"; + +/// @dev DataPoint is a 32 bytes structure which contains information about data point +type DataPoint is bytes32; + +/** + * DataPoint structure: + * 0xPPPPVVRRIIIIIIIIHHHHHHHHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + * - Prefix (bytes4) + * -- PPPP - Type prefix (0x4450) - ASCII representation of letters "DP" + * -- VV - Version of DataPoint specification, currently 0x00 + * -- RR - Reserved byte (should be 0x00 in current specification) + * - Registry-local identifier + * -- IIIIIIII - 32 bit implementation-specific id of the DataPoint + * - Chain ID (bytes4) + * -- HHHHHHHH - 32 bit of chain identifier + * - Registry Address (bytes20) + * -- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - Address of Registry which allocated the DataPoint + * + * !!! COMPATIBILITY REQUIREMENTS !!! + * - Registry address MUST be located in last 20 bytes of the DataPoint in ALL DataPoint implementations + * - PREFIX 0x44500000 SHOULD be used only by implementations with same DataPoint structure + */ + +/** + * @title DataPoints library + * @notice Library with utility functions to encode and decode DataPoint + */ +library DataPoints { + /// @dev represent PPPPVVRR prefix + bytes4 internal constant PREFIX = 0x44500000; + + /// @dev Error thrown when DataPoint structure is not supported + error UnsupportedDataPointStructure(); + + /** + * @notice Encode DataPoint + * @param registry Address of the registry which allocated the DataPoint + * @param id 32 bit implementation-specific id of the DataPoint + * @return Encoded DataPoint + */ + function encode(address registry, uint32 id) internal view returns (DataPoint) { + return + DataPoint.wrap( + bytes32((uint256(uint32(PREFIX)) << 224) | (uint256(id) << 192) | (uint256(ChainidTools.chainid()) << 160) | uint256(uint160(registry))) + ); + } + + /** + * @notice Decode DataPoint + * @param dp DataPoint to decode + * @return chainid Chain ID of the DataPoint + * @return registry Address of the registry which allocated the DataPoint + * @return id 32 bit implementation-specific id of the DataPoint + */ + function decode(DataPoint dp) internal pure returns (uint32 chainid, address registry, uint32 id) { + uint256 dpu = uint256(DataPoint.unwrap(dp)); + bytes4 prefix = bytes4(uint32(dpu >> 224)); + if (prefix != PREFIX) revert UnsupportedDataPointStructure(); + registry = address(uint160(dpu)); + chainid = uint32(dpu >> 160); + id = uint32(dpu >> 192); + } +} diff --git a/assets/erc-7208/contracts/utils/OmnichainAddresses.sol b/assets/erc-7208/contracts/utils/OmnichainAddresses.sol new file mode 100644 index 0000000000..25897074b6 --- /dev/null +++ b/assets/erc-7208/contracts/utils/OmnichainAddresses.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ChainidTools} from "./ChainidTools.sol"; + +/// @dev OmnichainAddress is a structure that represents address on specific chain +type OmnichainAddress is bytes32; + +/** + * OmnichainAddress structure: + * 0xPPPPVVRRRRRRRRRRHHHHHHHHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + * - Prefix (bytes4): + * -- PPPP - Type prefix (0x4F41) - ASCII representation of letters "OA" + * -- VV - Version of OmnichainAddress specification, currently 0x00 + * -- RR - Reserved byte + * - Reserved bytes (bytes4) + * -- RRRRRRRR - Reserved bytes (should be 0x00000000 in current specification) + * - Chain ID (bytes4) + * -- HHHHHHHH - 32 bit of chain identifier + * - User Address (bytes20) + * -- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - Address of the user + * + * Note: cheap conversion to `address` is possible: + * `address account = address(uint160(uint256(OmnichainAddress.unwrap(omnichainAddress))))` + * but it ignores chainid and prefix, so must be used with care + * + * !!! COMPATIBILITY REQUIREMENTS !!! + * - PREFIX 0x4F410000 SHOULD be used only by implementations with same OmnichainAddress structure + * - Requirements for other implementations: + * -- User Identifier MUST be persistent between compatible DataIndex implementations (so that user can use same ID in all compatible implementations) + * -- Any of compatible DataIndex implementation SHOULD be able to find owner of ID issued by any other compatible implementation + */ + +/** + * @title OmnichainAddresses library + * @notice Library with utility functions to encode and decode OmnichainAddress + */ +library OmnichainAddresses { + /// @dev represent PPPPVVRR prefix + bytes4 internal constant PREFIX = 0x4F410000; + + /// @dev Error thrown when OmnichainAddress structure is not supported + error UnsupportedOmnichainAddressesStructure(); + + /** + * @notice Encode OmnichainAddress + * @param account Address of the user + * @return Encoded OmnichainAddress + */ + function encode(address account) internal view returns (OmnichainAddress) { + return encode(ChainidTools.chainid(), account); + } + + /** + * @notice Encode OmnichainAddress + * @param chainid Chain ID to encode + * @param account Address of the user + * @return Encoded OmnichainAddress + */ + function encode(uint32 chainid, address account) internal pure returns (OmnichainAddress) { + return OmnichainAddress.wrap(bytes32((uint256(uint32(PREFIX)) << 224) | (uint256(chainid) << 160) | uint256(uint160(account)))); + } + + /** + * @notice Decode OmnichainAddress + * @param oa OmnichainAddress to decode + * @return chainid Chain ID of the OmnichainAddress + * @return account Address of the user + */ + function decode(OmnichainAddress oa) internal pure returns (uint32 chainid, address account) { + uint256 oau = uint256(OmnichainAddress.unwrap(oa)); + bytes4 prefix = bytes4(uint32(oau >> 224)); + if (prefix != PREFIX) revert UnsupportedOmnichainAddressesStructure(); + account = address(uint160(oau)); + chainid = uint32(oau >> 160); + } +} From e402021427d03ea0fc941838b64d9abf740754b4 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Fri, 2 Aug 2024 12:56:18 +0800 Subject: [PATCH 085/126] Update ERC-7631: Move to Review Merged by EIP-Bot. --- ERCS/erc-7631.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/ERCS/erc-7631.md b/ERCS/erc-7631.md index 2f905685bf..8909007dbe 100644 --- a/ERCS/erc-7631.md +++ b/ERCS/erc-7631.md @@ -4,7 +4,7 @@ title: Dual Nature Token Pair description: A specification for a co-joined fungible and non-fungible token pair author: vectorized (@vectorized), Thomas (@0xth0mas), Quit (@quitcrypto), Michael Amadi (@AmadiMichael), cygaar (@cygaar), Harrison (@pop-punk) discussions-to: https://ethereum-magicians.org/t/erc-7631-dual-nature-token-pair/18796 -status: Draft +status: Review type: Standards Track category: ERC created: 2024-02-21 @@ -62,17 +62,24 @@ The ERC-20 contract MAY implement the following interface. interface IERC7631BaseNFTSkippable { /// @dev Implementations SHOULD emit this event when the skip NFT status /// of `owner` is updated to `status`. + /// + /// The purpose of this event is to signal to indexers that the + /// skip NFT status has been changed. + /// + /// For simplicity of implementation, + /// this event MAY be emitted even if the status is unchanged. event SkipNFTSet(address indexed owner, bool status); /// @dev Returns true if ERC-721 mints and transfers to `owner` SHOULD be - /// skipped during ERC-20 to ERC-721 synchronization. Otherwise false. + /// skipped during ERC-20 to ERC-721 synchronization. + /// Otherwise, returns false. /// /// This method MAY revert /// (e.g. contract not initialized, method not supported). /// /// If this method reverts: /// - Interacting code SHOULD interpret `setSkipNFT` functionality as - /// unavailable (and hide any functionality to call `setSkipNFT`). + /// unavailable and hide any functionality to call `setSkipNFT`. /// - The skip NFT status for `owner` SHOULD be interpreted as undefined. /// /// Once a true or false value has been returned for a given `owner`, @@ -123,7 +130,7 @@ The `getSkipNFT` and `setSkipNFT` methods MAY revert. As contracts compiled with The skip NFT methods allow accounts to avoid having ERC-721 tokens automatically minted to it whenever there is an ERC-20 transfer. -This is useful in the following situations: +They are helpful in the following situations: - Loading vesting contracts with large amounts ERC-20 tokens to be vested to many users. - Loading candy machine contracts with large amounts of ERC-20 tokens to sell ERC-721 tokens to customers. @@ -132,7 +139,9 @@ This is useful in the following situations: Including the skip NFT methods in the standard will: - Enable applications to conveniently display the option for users to skip NFTs. -- Enable applications to transfer any amount of ERC-20 tokens without the O(n) gas costs associated with minting multiple ERC-721 tokens, which could surpass the block gas limit. +- Enable applications to transfer any amount of ERC-20 tokens without the O(n) gas costs associated with minting multiple ERC-721 tokens, which can surpass the block gas limit. + +These methods are recommended even on EVM chains with low gas costs, because bulk automatic ERC-721 transfers can still surpass the block gas limit. A useful pattern is to make `getSkipNFT` return true by default if `owner` is a smart contract. From ff5a0ba13d4204e54e03e9a1b1f3f0c9bedbe017 Mon Sep 17 00:00:00 2001 From: Christian Fries Date: Fri, 2 Aug 2024 10:29:55 +0200 Subject: [PATCH 086/126] Website: Fixed typos in events / added arguments to SettlementEvaluated event. Merged by EIP-Bot. --- ERCS/erc-6123.md | 10 ++--- assets/erc-6123/contracts/ERC20Settlement.sol | 13 +++--- assets/erc-6123/contracts/ISDC.sol | 44 ++++++++++--------- assets/erc-6123/contracts/SDCSingleTrade.sol | 4 +- .../SDCSingleTradePledgedBalance.sol | 19 ++++---- assets/erc-6123/test/SDCTests.js | 11 +++-- 6 files changed, 51 insertions(+), 50 deletions(-) diff --git a/ERCS/erc-6123.md b/ERCS/erc-6123.md index 43986463a8..de138cbe3b 100644 --- a/ERCS/erc-6123.md +++ b/ERCS/erc-6123.md @@ -115,10 +115,10 @@ function afterTransfer(bool success, uint256 transactionData) external; #### Trade Termination: `requestTermination` -Allows an eligible party to request a mutual termination of the trade with the correspondig `tradeData` with a termination amount she is willing to pay and provide further termination terms (e.g. an XML) +Allows an eligible party to request a mutual termination of the trade with the correspondig `tradeId` with a termination amount she is willing to pay and provide further termination terms (e.g. an XML) ```solidity -function requestTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; +function requestTradeTermination(string memory tradeId, int256 terminationPayment, string memory terminationTerms) external; ``` #### Trade Termination: `confirmTradeTermination` @@ -126,7 +126,7 @@ function requestTradeTermination(string memory tradeData, int256 terminationPaym Allows an eligible party to confirm a previously requested (mutual) trade termination, including termination payment value and termination terms ```solidity -function confirmTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; +function confirmTradeTermination(string memory tradeId, int256 terminationPayment, string memory terminationTerms) external; ``` #### Trade Termination: `cancelTradeTermination` @@ -134,7 +134,7 @@ function confirmTradeTermination(string memory tradeData, int256 terminationPaym The party that initiated `requestTradeTermination` has the option to withdraw the request, e.g., in the case where the termination is not confirmed in a timely manner. ```solidity -function cancelTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; +function cancelTradeTermination(string memory tradeId, int256 terminationPayment, string memory terminationTerms) external; ``` ### Trade Events @@ -178,7 +178,7 @@ event TradeActivated(string tradeId); Emitted when a settlement is requested. May trigger the settlement phase. ```solidity -event SettlementRequested(address initiator, string tradeData, string lastSettlementData); +event SettlementRequested(address initiator, string tradeId, string lastSettlementData); ``` ### SettlementEvaluated diff --git a/assets/erc-6123/contracts/ERC20Settlement.sol b/assets/erc-6123/contracts/ERC20Settlement.sol index 5aedb06d25..bd63f3cf3e 100644 --- a/assets/erc-6123/contracts/ERC20Settlement.sol +++ b/assets/erc-6123/contracts/ERC20Settlement.sol @@ -4,6 +4,7 @@ pragma solidity >=0.7.0 <0.9.0; import "./ISDC.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; import "./IERC20Settlement.sol"; contract ERC20Settlement is ERC20, IERC20Settlement{ @@ -39,9 +40,9 @@ contract ERC20Settlement is ERC20, IERC20Settlement{ function checkedTransfer(address to, uint256 value, uint256 transactionID) public onlySDC{ if ( balanceOf(sdcAddress) < value) - ISDC(sdcAddress).afterTransfer(false, transactionID); + ISDC(sdcAddress).afterTransfer(false, Strings.toString(transactionID)); else - ISDC(sdcAddress).afterTransfer(true, transactionID); + ISDC(sdcAddress).afterTransfer(true, Strings.toString(transactionID)); } function checkedTransferFrom(address from, address to, uint256 value, uint256 transactionID) external view onlySDC { @@ -54,14 +55,14 @@ contract ERC20Settlement is ERC20, IERC20Settlement{ for(uint256 i = 0; i < values.length; i++) requiredBalance += values[i]; if (balanceOf(msg.sender) < requiredBalance){ - ISDC(sdcAddress).afterTransfer(false, transactionID); + ISDC(sdcAddress).afterTransfer(false, Strings.toString(transactionID)); return; } else{ for(uint256 i = 0; i < to.length; i++){ _transfer(sdcAddress,to[i],values[i]); } - ISDC(sdcAddress).afterTransfer(true, transactionID); + ISDC(sdcAddress).afterTransfer(true, Strings.toString(transactionID)); } } @@ -77,7 +78,7 @@ contract ERC20Settlement is ERC20, IERC20Settlement{ totalRequiredBalance += values[j]; } if (balanceOf(fromAddress) < totalRequiredBalance){ - ISDC(sdcAddress).afterTransfer(false, transactionID); + ISDC(sdcAddress).afterTransfer(false, Strings.toString(transactionID)); return; } @@ -85,7 +86,7 @@ contract ERC20Settlement is ERC20, IERC20Settlement{ for(uint256 i = 0; i < to.length; i++){ _transfer(from[i],to[i],values[i]); } - ISDC(sdcAddress).afterTransfer(true, transactionID); + ISDC(sdcAddress).afterTransfer(true, Strings.toString(transactionID)); } } \ No newline at end of file diff --git a/assets/erc-6123/contracts/ISDC.sol b/assets/erc-6123/contracts/ISDC.sol index 90b299f72d..dddc9bc172 100644 --- a/assets/erc-6123/contracts/ISDC.sol +++ b/assets/erc-6123/contracts/ISDC.sol @@ -70,10 +70,14 @@ interface ISDC { /** * @dev Emitted when a new trade is incepted from a eligible counterparty * @param initiator is the address from which trade was incepted + * @param withParty is the party the inceptor wants to trade with * @param tradeId is the trade ID (e.g. generated internally) - * @param tradeData holding the trade parameters + * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param position is the position the inceptor has in that trade + * @param paymentAmount is the payment amount which can be positive or negative (viewed from the inceptor) + * @param initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) */ - event TradeIncepted(address initiator, string tradeId, string tradeData); + event TradeIncepted(address initiator, address withParty, string tradeId, string tradeData, int position, int256 paymentAmount, string initialSettlementData); /** * @dev Emitted when an incepted trade is confirmed by the opposite counterparty @@ -99,34 +103,38 @@ interface ISDC { /** * @dev Emitted when an active trade is terminated + * @param tradeId the trade identifier of the activated trade * @param cause string holding data associated with the termination, e.g. transactionData upon a failed transaction */ - event TradeTerminated(string cause); + event TradeTerminated(string tradeId, string cause); /* Events related to the settlement process */ /** - * @dev Emitted when Settlement phase is initiated + * @dev Emitted when a settlement gets requested + * @param initiator the address of the requesting party + * @param tradeData holding the stored trade data + * @param lastSettlementData holding the settlementdata from previous settlement (next settlement will be the increment of next valuation compared to former valuation) */ - event SettlementEvaluated(); + event SettlementRequested(address initiator, string tradeData, string lastSettlementData); /** - * @dev Emitted when settlement process has been finished + * @dev Emitted when Settlement has been valued and settlement phase is initiated + * @param initiator the address of the requesting party + * @param settlementAmount the settlement amount. If settlementAmount > 0 then receivingParty receives this amount from other party. If settlementAmount < 0 then other party receives -settlementAmount from receivingParty. + * @param settlementData. the tripple (product, previousSettlementData, settlementData) determines the settlementAmount. */ - event SettlementTranfered(string transactionData); + event SettlementEvaluated(address initiator, int256 settlementAmount, string settlementData); /** * @dev Emitted when settlement process has been finished */ - event SettlementFailed(string transactionData); + event SettlementTransferred(string transactionData); /** - * @dev Emitted when a settlement gets requested - * @param initiator the address of the requesting party - * @param tradeData holding the stored trade data - * @param lastSettlementData holding the settlementdata from previous settlement (next settlement will be the increment of next valuation compared to former valuation) + * @dev Emitted when settlement process has been finished */ - event SettlementRequested(address initiator, string tradeData, string lastSettlementData); + event SettlementFailed(string transactionData); /* Events related to trade termination */ @@ -154,12 +162,6 @@ interface ISDC { */ event TradeTerminationCanceled(address cpAddress, string tradeId, string terminationTerms); - /** - * @dev Emitted when trade processing is halted - * @param message of what has happened - */ - event ProcessHalted(string message); - /*------------------------------------------- FUNCTIONALITY ---------------------------------------------------------------------------------------*/ /// Trade Inception @@ -218,9 +220,9 @@ interface ISDC { * @notice May get called from outside to to finish a transfer (callback). The trade decides on how to proceed based on success flag * @param success tells the protocol whether transfer was successful * @param transactionData data associtated with the transfer, will be emitted via the events. - * @dev emit a {SettlementTranfered} or a {SettlementFailed} event. May emit a {TradeTerminated} event. + * @dev emit a {SettlementTransferred} or a {SettlementFailed} event. May emit a {TradeTerminated} event. */ - function afterTransfer(bool success, uint256 transactionData) external; + function afterTransfer(bool success, string memory transactionData) external; /// Trade termination diff --git a/assets/erc-6123/contracts/SDCSingleTrade.sol b/assets/erc-6123/contracts/SDCSingleTrade.sol index 71c3eba737..c6dc890ae3 100644 --- a/assets/erc-6123/contracts/SDCSingleTrade.sol +++ b/assets/erc-6123/contracts/SDCSingleTrade.sol @@ -19,7 +19,7 @@ import "./ERC20Settlement.sol"; * - upon termination all remaining 'locked' amounts will be transferred back to the counterparties */ -abstract contract SDC is ISDC { +abstract contract SDCSingleTrade is ISDC { /* * Trade States */ @@ -143,7 +143,7 @@ abstract contract SDC is ISDC { upfrontPayment = _position == 1 ? _paymentAmount : -_paymentAmount; // upfrontPayment is saved with view on the receiving party tradeID = Strings.toString(transactionHash); tradeData = _tradeData; // Set trade data to enable querying already in inception state - emit TradeIncepted(msg.sender, tradeID, _tradeData); + emit TradeIncepted(msg.sender, _withParty, tradeID, _tradeData, _position, _paymentAmount, _initialSettlementData); } /* diff --git a/assets/erc-6123/contracts/SDCSingleTradePledgedBalance.sol b/assets/erc-6123/contracts/SDCSingleTradePledgedBalance.sol index 0268780714..265512764c 100644 --- a/assets/erc-6123/contracts/SDCSingleTradePledgedBalance.sol +++ b/assets/erc-6123/contracts/SDCSingleTradePledgedBalance.sol @@ -35,8 +35,7 @@ import "./ERC20Settlement.sol"; *-------------------------------------* */ -contract SDCPledgedBalance is SDC { - +contract SDCSingleTradePledgedBalance is SDCSingleTrade { struct MarginRequirement { uint256 buffer; @@ -54,7 +53,7 @@ contract SDCPledgedBalance is SDC { address _settlementToken, uint256 _initialBuffer, // m uint256 _initalTerminationFee // p - ) SDC(_party1,_party2,_settlementToken) { + ) SDCSingleTrade(_party1,_party2,_settlementToken) { marginRequirements[party1] = MarginRequirement(_initialBuffer, _initalTerminationFee); marginRequirements[party2] = MarginRequirement(_initialBuffer, _initalTerminationFee); } @@ -87,7 +86,7 @@ contract SDCPledgedBalance is SDC { address[] memory to = new address[](1); uint256[] memory amounts = new uint256[](1); from[0] = settlementPayer; to[0] = otherParty(settlementPayer); amounts[0] = transferAmount; - emit SettlementEvaluated(); + emit SettlementEvaluated(msg.sender, settlementAmount, _settlementData); setTradeState(TradeState.InTransfer); settlementToken.checkedBatchTransferFrom(from,to,amounts,transactionID); } @@ -96,7 +95,7 @@ contract SDCPledgedBalance is SDC { * afterTransfer processes SDC depending on success of the respective payment and depending on the current trade state * Good Case: state will be settled, failed settlement will trigger the pledge balance transfer and termination */ - function afterTransfer(bool success, uint256 transactionHash) external override { + function afterTransfer(bool success, string memory transactionHash) external override { if ( inStateConfirmed()){ if (success){ setTradeState(TradeState.Settled); @@ -104,28 +103,28 @@ contract SDCPledgedBalance is SDC { } else{ setTradeState(TradeState.Terminated); - emit TradeTerminated("Upfront Transfer Failure"); + emit TradeTerminated(tradeID, "Upfront Transfer Failure"); } } else if ( inStateTransfer() ){ if (success){ setTradeState(TradeState.Settled); - emit SettlementTranfered("Settlement Settled - Pledge Transfer"); + emit SettlementTransferred("Settlement Settled - Pledge Transfer"); } else{ // Settlement & Pledge Case: transferAmount is transferred from SDC balance (i.e. pledged balance). int256 settlementAmount = settlementAmounts[settlementAmounts.length-1]; setTradeState(TradeState.InTermination); processTerminationWithPledge(settlementAmount); - emit TradeTerminated("Settlement Failed - Pledge Transfer"); + emit TradeTerminated(tradeID, "Settlement Failed - Pledge Transfer"); } } else if( inStateTermination() ){ if (success){ setTradeState(TradeState.Terminated); - emit TradeTerminated("Trade terminated sucessfully"); + emit TradeTerminated(tradeID, "Trade terminated sucessfully"); } else{ - emit TradeTerminated("Mutual Termination failed - Pledge Transfer"); + emit TradeTerminated(tradeID, "Mutual Termination failed - Pledge Transfer"); processTerminationWithPledge(getTerminationPayment()); } } diff --git a/assets/erc-6123/test/SDCTests.js b/assets/erc-6123/test/SDCTests.js index 180231a0f3..6351c12b47 100644 --- a/assets/erc-6123/test/SDCTests.js +++ b/assets/erc-6123/test/SDCTests.js @@ -41,13 +41,12 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { counterparty1 = _counterparty1; counterparty2 = _counterparty2; ERC20Factory = await ethers.getContractFactory("ERC20Settlement"); - SDCFactory = await ethers.getContractFactory("SDCPledgedBalance"); + SDCFactory = await ethers.getContractFactory("SDCSingleTradePledgedBalance"); //oken = await ERC20Factory.deploy(); // await token.deployed(); }); - it("1. Counterparties incept and confirm a trade successfully, Upfront is transferred from CP1 to CP2", async () => { let token = await ERC20Factory.deploy(); await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); @@ -187,7 +186,7 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, upfront, "initialMarketData"); const receipt = await incept_call.wait(); const event = receipt.events.find(event => event.event === 'TradeIncepted'); - const trade_id = event.args[1]; + const trade_id = event.args[2]; const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, -upfront, "initialMarketData"); await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); const terminate_call = await sdc.connect(counterparty1).requestTradeTermination(trade_id, terminationPayment, "terminationTerms"); @@ -212,7 +211,7 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, 0, "initialMarketData"); const receipt = await incept_call.wait(); const event = receipt.events.find(event => event.event === 'TradeIncepted'); - const trade_id = event.args[1]; + const trade_id = event.args[2]; const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, 0, "initialMarketData"); const terminate_call = await sdc.connect(counterparty2).requestTradeTermination(trade_id, -terminationPayment, "terminationTerms"); const confirm_terminate_call = await sdc.connect(counterparty1).confirmTradeTermination(trade_id, +terminationPayment, "terminationTerms"); @@ -229,7 +228,7 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, 0, "initialMarketData"); const receipt = await incept_call.wait(); const event = receipt.events.find(event => event.event === 'TradeIncepted'); - const trade_id = event.args[1]; + const trade_id = event.args[2]; const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, 0, "initialMarketData"); const terminate_call = await sdc.connect(counterparty1).requestTradeTermination(trade_id, -terminationPayment, "terminationTerms"); const confirm_terminate_call = await sdc.connect(counterparty2).confirmTradeTermination(trade_id, +terminationPayment, "terminationTerms"); @@ -305,7 +304,7 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, 0, "initialMarketData"); const receipt = await incept_call.wait(); const event = receipt.events.find(event => event.event === 'TradeIncepted'); - const trade_id = event.args[1]; + const trade_id = event.args[2]; const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, 0, "initialMarketData"); await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); const terminate_call = await sdc.connect(counterparty1).requestTradeTermination(trade_id, 10000000, "terminationTerms"); From 802224efa51f71c777a8b496e27159cb0a4cb1f7 Mon Sep 17 00:00:00 2001 From: Christian Fries Date: Fri, 2 Aug 2024 18:01:00 +0200 Subject: [PATCH 087/126] ERC-6123: small refactor rename SDC.sol to SDCSingleTrade.sol Merged by EIP-Bot. --- assets/erc-6123/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/erc-6123/README.md b/assets/erc-6123/README.md index 8688af6511..4011a0d456 100644 --- a/assets/erc-6123/README.md +++ b/assets/erc-6123/README.md @@ -11,8 +11,8 @@ We provide the essential steps to compile the contracts and run the provided uni ### Provided Contracts and Tests - `contracts/ISDC.sol` - Interface contract -- `contracts/SDC.sol` - SDC abstract contract for an OTC Derivative -- `contracts/SDCPledgedBalance.sol` - SDC full implementation for an OTC Derivative +- `contracts/SDCSingleTrade.sol` - SDC abstract contract for an OTC Derivative (single trade case only) +- `contracts/SDCSingleTradePledgedBalance.sol` - SDC full implementation for an OTC Derivative (single trade case only) - `contracts/IERC20Settlement.sol` - Interface (extending the ERC-20) for settlement tokens used in `SDCPledgedBalance`. - `contracts/ERC20Settlement.sol` - Mintable settlement token contract implementing `IERC20Settlement` for unit tests - `test/SDCTests.js` - Unit tests for the life-cycle of the sdc implementation From a6f10b4bf9b7ef55c4ce6d58ee63bf0b7eebe9e9 Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Fri, 2 Aug 2024 16:04:26 -0400 Subject: [PATCH 088/126] Update ERC-7677: chain id update Merged by EIP-Bot. --- ERCS/erc-7677.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/ERCS/erc-7677.md b/ERCS/erc-7677.md index 8c155c41f4..528f0e279d 100644 --- a/ERCS/erc-7677.md +++ b/ERCS/erc-7677.md @@ -242,15 +242,18 @@ The `paymasterService` capability is implemented by both apps and wallets. #### App Implementation -Apps need to give wallets a paymaster service URL they can make the above RPC calls to. They can do this using the `paymasterService` capability as part of an [EIP-5792](./eip-5792.md) `wallet_sendCalls` call. +Apps need to give wallets paymaster service URLs they can make the above RPC calls to. They can do this using the `paymasterService` capability as part of an [EIP-5792](./eip-5792.md) `wallet_sendCalls` call. ##### `wallet_sendCalls` Paymaster Capability Specification ```typescript -type PaymasterCapabilityParams = { - url: string; - context: Record; -} +type PaymasterCapabilityParams = Record< + `0x${string}`, // Chain ID + { + url: string; // Paymaster service URL for provided chain ID + context: Record; // Additional data defined by paymaster service providers + } +>; ``` ###### `wallet_sendCalls` Example Parameters @@ -259,25 +262,34 @@ type PaymasterCapabilityParams = { [ { "version": "1.0", - "chainId": "0x01", "from": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "calls": [ { "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "value": "0x9184e72a", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675", + "chainId": "0x01" }, { "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "value": "0x182183", - "data": "0xfbadbaf01" + "data": "0xfbadbaf01", + "chainId": "0x2105" } ], "capabilities": { "paymasterService": { - "url": "https://...", - "context": { - "policyId": "962b252c-a726-4a37-8d86-333ce0a07299" + "0x01": { + "url": "https://...", + "context": { + "policyId": "962b252c-a726-4a37-8d86-333ce0a07299" + } + }, + "0x2105": { + "url": "https://...", + "context": { + "policyId": "0a268db9-3243-4178-b1bd-d9b67a47d37b" + } } } } @@ -285,7 +297,7 @@ type PaymasterCapabilityParams = { ] ``` -The wallet will then make the above paymaster RPC calls to the URL specified in the `paymasterService` capability field. +The wallet will then make the above paymaster RPC calls to the URLs specified in the `paymasterService` capability field. #### Wallet Implementation From 2922ed25ce1a849e23735c109c175688088064df Mon Sep 17 00:00:00 2001 From: James Brown Date: Mon, 5 Aug 2024 12:18:11 +1000 Subject: [PATCH 089/126] Add ERC: Permissionless Script Registry Merged by EIP-Bot. --- ERCS/erc-7738.md | 154 + assets/erc-7738/.gitignore | 17 + .../contracts/DecentralisedRegistry.sol | 90 + .../DecentralisedRegistryPermissioned.sol | 130 + assets/erc-7738/contracts/IERC7738.sol | 17 + assets/erc-7738/contracts/stakingToken.sol | 48 + assets/erc-7738/hardhat.config.ts | 32 + assets/erc-7738/ignition/modules/Lock.ts | 17 + assets/erc-7738/package-lock.json | 6944 +++++++++++++++++ assets/erc-7738/package.json | 24 + .../test/DecentralisedRegistry.test.ts | 151 + .../test/PermissionedRegistry.test.ts | 111 + assets/erc-7738/tsconfig.json | 11 + 13 files changed, 7746 insertions(+) create mode 100644 ERCS/erc-7738.md create mode 100644 assets/erc-7738/.gitignore create mode 100644 assets/erc-7738/contracts/DecentralisedRegistry.sol create mode 100644 assets/erc-7738/contracts/DecentralisedRegistryPermissioned.sol create mode 100644 assets/erc-7738/contracts/IERC7738.sol create mode 100644 assets/erc-7738/contracts/stakingToken.sol create mode 100644 assets/erc-7738/hardhat.config.ts create mode 100644 assets/erc-7738/ignition/modules/Lock.ts create mode 100644 assets/erc-7738/package-lock.json create mode 100644 assets/erc-7738/package.json create mode 100644 assets/erc-7738/test/DecentralisedRegistry.test.ts create mode 100644 assets/erc-7738/test/PermissionedRegistry.test.ts create mode 100644 assets/erc-7738/tsconfig.json diff --git a/ERCS/erc-7738.md b/ERCS/erc-7738.md new file mode 100644 index 0000000000..3d29b72003 --- /dev/null +++ b/ERCS/erc-7738.md @@ -0,0 +1,154 @@ +--- +eip: 7738 +title: Permissionless Script Registry +description: Permissionless registry to fetch executable scripts for contracts +author: Victor Zhang (@zhangzhongnan928), James Brown (@JamesSmartCell) +discussions-to: https://ethereum-magicians.org/t/erc-7738-permissionless-script-registry/20503 +status: Draft +type: Standards Track +category: ERC +created: 2024-07-01 +requires: 173 +--- +## Abstract + +This EIP provides a means to create a standard registry for locating executable scripts associated with the token. + +## Motivation + +[ERC-5169](./eip-5169.md) provides a client script lookup method for contracts. This requires the contract to have implemented the `ERC-5169` interface at the time of construction (or allow an upgrade path). + +This proposal outlines a contract that can supply prototype and certified scripts. The contract would be a multichain singleton instance that would be deployed at identical addresses on supported chains. + +### Overview + +The registry contract will supply a set of URI links for a given contract address. These URI links point to script programs that can be fetched by a wallet, viewer or mini-dapp. + +The pointers can be set permissionlessly using a setter in the registry contract. + +## Specification + +The keywords โ€œMUSTโ€, โ€œMUST NOTโ€, โ€œREQUIREDโ€, โ€œSHALLโ€, โ€œSHALL NOTโ€, โ€œSHOULDโ€, โ€œSHOULD NOTโ€, โ€œRECOMMENDEDโ€, โ€œMAYโ€ and โ€œOPTIONALโ€ in this document are to be interpreted as described in RFC 2119. + +The contract MUST implement the `IERC7738` interface. +The contract MUST emit the `ScriptUpdate` event when the script is updated. +The contract SHOULD order the `scriptURI` returned so that the `ERC-173` `owner()` of the contract's script entries are returned first (in the case of simple implementations the wallet will pick the first `scriptURI` returned). +The contract SHOULD provide a means to page through entries if there are a large number of scriptURI entries. + +```solidity +interface IERC7738 { + /// @dev This event emits when the scriptURI is updated, + /// so wallets implementing this interface can update a cached script + event ScriptUpdate(address indexed contractAddress, string[] newScriptURI); + + /// @notice Get the scriptURI for the contract + /// @return The scriptURI + function scriptURI(address contractAddress) external view returns (string[] memory); + + /// @notice Update the scriptURI + /// emits event ScriptUpdate(address indexed contractAddress, scriptURI memory newScriptURI); + function setScriptURI(address contractAddress, string[] memory scriptURIList) external; +} +``` + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +## Rationale + +This method allows contracts written without the [ERC-5169](./eip-5169.md) interface to associate scripts with themselves, and avoids the need for a centralised online server, with subsequent need for security and the requires an organisation to become a gatekeeper for the database. + +## Test Cases + +Hardhat contract and test scripts for two implementations can be found in the asset folder. + +## Reference Implementation + +```solidity +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract DecentralisedRegistry is IERC7738 { + struct ScriptEntry { + mapping(address => string[]) scriptURIs; + address[] addrList; + } + + mapping(address => ScriptEntry) private _scriptURIs; + + function setScriptURI( + address contractAddress, + string[] memory scriptURIList + ) public { + require (scriptURIList.length > 0, "> 0 entries required in scriptURIList"); + bool isOwnerOrExistingEntry = Ownable(contractAddress).owner() == msg.sender + || _scriptURIs[contractAddress].scriptURIs[msg.sender].length > 0; + _scriptURIs[contractAddress].scriptURIs[msg.sender] = scriptURIList; + if (!isOwnerOrExistingEntry) { + _scriptURIs[contractAddress].addrList.push(msg.sender); + } + + emit ScriptUpdate(contractAddress, msg.sender, scriptURIList); + } + + // Return the list of scriptURI for this contract. + // Order the return list so `Owner()` assigned scripts are first in the list + function scriptURI( + address contractAddress + ) public view returns (string[] memory) { + //build scriptURI return list, owner first + address contractOwner = Ownable(contractAddress).owner(); + address[] memory addrList = _scriptURIs[contractAddress].addrList; + uint256 i; + + //now calculate list length + uint256 listLen = _scriptURIs[contractAddress].scriptURIs[contractOwner].length; + for (i = 0; i < addrList.length; i++) { + listLen += _scriptURIs[contractAddress].scriptURIs[addrList[i]].length; + } + + string[] memory ownerScripts = new string[](listLen); + + // Add owner scripts + uint256 scriptIndex = _addScriptURIs(contractOwner, contractAddress, ownerScripts, 0); + + // Add remainder scripts + for (uint256 i = 0; i < addrList.length; i++) { + scriptIndex = _addScriptURIs(addrList[i], contractAddress, ownerScripts, scriptIndex); + } + + return ownerScripts; + } + + function _addScriptURIs( + address user, + address contractAddress, + string[] memory ownerScripts, + uint256 scriptIndex + ) internal view returns (uint256) { + for (uint256 j = 0; j < _scriptURIs[contractAddress].scriptURIs[user].length; j++) { + string memory thisScriptURI = _scriptURIs[contractAddress].scriptURIs[user][j]; + if (bytes(thisScriptURI).length > 0) { + ownerScripts[scriptIndex++] = thisScriptURI; + } + } + return scriptIndex; + } +} +``` + +## Security Considerations + +The scripts provided could be authenticated in various ways: + +1. The target contract which the setter specifies implements the [ERC-173](./eip-173.md) `Ownable` interface. Once the script is fetched, the signature can be verified to match the Owner(). In the case of TokenScript this can be checked by a dapp or wallet using the TokenScript SDK, the TokenScript online verification service, or by extracting the signature from the XML, taking a keccak256 of the script and ecrecover the signing key address. +2. If the contract does not implement Ownable, further steps can be taken: + a. The hosting app/wallet can acertain the deployment key using 3rd party API or block explorer. The implementing wallet, dapp or viewer would then check the signature matches this deployment key. + b. Signing keys could be pre-authenticated by a hosting app, using an embedded keychain. + c. A governance token could allow a script council to authenticate requests to set and validate keys. + +If these criteria are not met: +- For mainnet implementations the implementing wallet should be cautious about using the script - it would be at the app and/or user's discretion. +- For testnets, it is acceptable to allow the script to function, at the discretion of the wallet provider. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/assets/erc-7738/.gitignore b/assets/erc-7738/.gitignore new file mode 100644 index 0000000000..e8c12ff4fe --- /dev/null +++ b/assets/erc-7738/.gitignore @@ -0,0 +1,17 @@ +node_modules +.env + +# Hardhat files +/cache +/artifacts + +# TypeChain files +/typechain +/typechain-types + +# solidity-coverage files +/coverage +/coverage.json + +# Hardhat Ignition default folder for deployments against a local node +ignition/deployments/chain-31337 diff --git a/assets/erc-7738/contracts/DecentralisedRegistry.sol b/assets/erc-7738/contracts/DecentralisedRegistry.sol new file mode 100644 index 0000000000..2bbb2cab59 --- /dev/null +++ b/assets/erc-7738/contracts/DecentralisedRegistry.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.24; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "./IERC7738.sol"; + +contract DecentralisedRegistry is IERC7738 { + struct ScriptEntry { + mapping(address => string[]) scriptURIs; + address[] addrList; + } + + uint256 public constant MAX_PAGE_SIZE = 500; + mapping(address => ScriptEntry) private _scriptURIs; + + function setScriptURI( + address contractAddress, + string[] memory scriptURIList + ) public { + require (scriptURIList.length > 0, "> 0 entries required in scriptURIList"); + bool isOwnerOrExistingEntry = Ownable(contractAddress).owner() == msg.sender + || _scriptURIs[contractAddress].scriptURIs[msg.sender].length > 0; + _scriptURIs[contractAddress].scriptURIs[msg.sender] = scriptURIList; + if (!isOwnerOrExistingEntry) { + _scriptURIs[contractAddress].addrList.push(msg.sender); + } + + emit ScriptUpdate(contractAddress, msg.sender, scriptURIList); + } + + function scriptURI( + address contractAddress + ) public view returns (string[] memory) { + return scriptURI(contractAddress, 1, MAX_PAGE_SIZE); + } + + function scriptURI(address contractAddress, uint256 page, uint256 pageSize) public view returns (string[] memory ownerScripts) { + require(page > 0 && pageSize > 0 && pageSize <= MAX_PAGE_SIZE, "Page >= 1 and pageSize <= MAX_PAGE_SIZE"); + + address contractOwner = Ownable(contractAddress).owner(); + address[] memory addrList = _scriptURIs[contractAddress].addrList; + uint256 startPoint = pageSize * (page - 1); + + uint256 listLen = _scriptURIs[contractAddress].scriptURIs[contractOwner].length; + for (uint256 i = 0; i < addrList.length; i++) { + listLen += _scriptURIs[contractAddress].scriptURIs[addrList[i]].length; + } + + uint256 arrayLen = listLen < pageSize ? listLen : pageSize; + ownerScripts = new string[](arrayLen); + uint256 scriptIndex = 0; + uint256 currentIndex = 0; + + if (startPoint >= listLen) { + return new string[](0) ; + } + + // Add owner scriptURIs + (scriptIndex, currentIndex) = _addScriptURIs(contractOwner, contractAddress, startPoint, scriptIndex, pageSize, ownerScripts, currentIndex); + + // Add remainder of scriptURIs + for (uint256 i = 0; i < addrList.length && scriptIndex < pageSize; i++) { + (scriptIndex, currentIndex) = _addScriptURIs(addrList[i], contractAddress, startPoint, scriptIndex, pageSize, ownerScripts, currentIndex); + } + } + + function _addScriptURIs( + address user, + address contractAddress, + uint256 startPoint, + uint256 scriptIndex, + uint256 pageSize, + string[] memory ownerScripts, + uint256 currentIndex + ) internal view returns (uint256, uint256) { + for (uint256 j = 0; j < _scriptURIs[contractAddress].scriptURIs[user].length; j++) { + string memory thisScriptURI = _scriptURIs[contractAddress].scriptURIs[user][j]; + if (bytes(thisScriptURI).length > 0) { + if (currentIndex >= startPoint) { + ownerScripts[scriptIndex++] = thisScriptURI; + } + if (scriptIndex >= pageSize) { + break; + } + } + currentIndex++; + } + return (scriptIndex, currentIndex); + } +} diff --git a/assets/erc-7738/contracts/DecentralisedRegistryPermissioned.sol b/assets/erc-7738/contracts/DecentralisedRegistryPermissioned.sol new file mode 100644 index 0000000000..9904140fb7 --- /dev/null +++ b/assets/erc-7738/contracts/DecentralisedRegistryPermissioned.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.24; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "./IERC7738.sol"; + +contract DecentralisedRegistryPermissioned is IERC7738 { + struct ScriptEntry { + string[] scriptURIs; + address[] delegateSigners; // list of authenticated addresses approved by owner + address owner; // provides a latch so that 3rd parties can create TokenScript entries + } + + mapping(address => ScriptEntry) private _scriptURIs; + + event RegisterOwner( + address indexed contractAddress, + address indexed newOwner + ); + event AddDelegateSigner( + address indexed contractAddress, + address indexed newDelegate + ); + event RevokeDelegateSigner( + address indexed contractAddress, + address indexed revokedDelegate + ); + + function scriptURI( + address contractAddress + ) public view returns (string[] memory) { + return _scriptURIs[contractAddress].scriptURIs; + } + + function setScriptURI( + address contractAddress, + string[] memory scriptURIList + ) public { + // in order to set scriptURI array, the sender must adhere to the following rules: + require( + isDelegateOrOwner(contractAddress, msg.sender), + "Not authorized" + ); + + emit ScriptUpdate(contractAddress, msg.sender, scriptURIList); + _scriptURIs[contractAddress].scriptURIs = scriptURIList; + } + + function registerOwner(address contractAddress) public { + ScriptEntry storage existingEntry = _scriptURIs[contractAddress]; + address contractOwner = Ownable(contractAddress).owner(); + address sender = msg.sender; + require(existingEntry.owner != sender, "Already set to this owner"); + require( + existingEntry.owner == address(0) || sender == contractOwner, + "Not authorized" + ); + emit RegisterOwner(contractAddress, sender); + existingEntry.owner = sender; + } + + function isDelegateOrOwner( + address contractAddress, + address check + ) public view returns (bool) { + ScriptEntry memory existingEntry = _scriptURIs[contractAddress]; + if (check == Ownable(contractAddress).owner()) { + return true; + } + uint256 length = existingEntry.delegateSigners.length; + for (uint256 i = 0; i < length; ) { + if (existingEntry.delegateSigners[i] == check) { + return true; + } + unchecked { + i++; + } + } + return false; + } + + function getDelegateIndex( + address contractAddress, + address check + ) public view returns (int256) { + ScriptEntry memory existingEntry = _scriptURIs[contractAddress]; + uint256 length = existingEntry.delegateSigners.length; + for (uint256 i = 0; i < length; ) { + if (existingEntry.delegateSigners[i] == check) { + return int256(i); + } + unchecked { + i++; + } + } + return -1; + } + + function addDelegateSigner( + address contractAddress, + address newSigner + ) public { + require( + msg.sender == Ownable(contractAddress).owner(), + "Contract Owner only" + ); + require( + getDelegateIndex(contractAddress, newSigner) < 0, + "Already a delegate signer" + ); + emit AddDelegateSigner(contractAddress, newSigner); + _scriptURIs[contractAddress].delegateSigners.push(newSigner); + } + + function revokeDelegateSigner( + address contractAddress, + address signer + ) public { + int256 delegateIndex = getDelegateIndex(contractAddress, signer); + require( + msg.sender == Ownable(contractAddress).owner(), + "Contract Owner only" + ); + require(delegateIndex > -1, "Unable to revoke unknown signer"); + emit RevokeDelegateSigner(contractAddress, signer); + delete _scriptURIs[contractAddress].delegateSigners[ + uint256(delegateIndex) + ]; + } +} diff --git a/assets/erc-7738/contracts/IERC7738.sol b/assets/erc-7738/contracts/IERC7738.sol new file mode 100644 index 0000000000..647caed76d --- /dev/null +++ b/assets/erc-7738/contracts/IERC7738.sol @@ -0,0 +1,17 @@ + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.24; +interface IERC7738 { + /// @dev This event emits when the scriptURI is updated, + /// so wallets implementing this interface can update a cached script + event ScriptUpdate(address indexed contractAddress, address indexed setter, string[] newScriptURI); + + /// @notice Get the scriptURI for the contract + /// @return The scriptURI + function scriptURI(address contractAddress) external view returns (string[] memory); + + /// @notice Update the scriptURI + /// emits event ScriptUpdate(address indexed contractAddress, scriptURI memory newScriptURI); + function setScriptURI(address contractAddress, string[] memory scriptURIList) external; +} \ No newline at end of file diff --git a/assets/erc-7738/contracts/stakingToken.sol b/assets/erc-7738/contracts/stakingToken.sol new file mode 100644 index 0000000000..60d1c08dea --- /dev/null +++ b/assets/erc-7738/contracts/stakingToken.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +// Compatible with OpenZeppelin Contracts ^5.0.0 +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract StakingToken is ERC721, ERC721Enumerable, Ownable { + uint256 private _nextTokenId; + + constructor() + ERC721("StakingToken", "STK") + Ownable(msg.sender) + { + safeMint(); + } + + function safeMint() public { + uint256 tokenId = _nextTokenId++; + _safeMint(msg.sender, tokenId); + } + + // The following functions are overrides required by Solidity. + function _update(address to, uint256 tokenId, address auth) + internal + override(ERC721, ERC721Enumerable) + returns (address) + { + return super._update(to, tokenId, auth); + } + + function _increaseBalance(address account, uint128 value) + internal + override(ERC721, ERC721Enumerable) + { + super._increaseBalance(account, value); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC721, ERC721Enumerable) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} \ No newline at end of file diff --git a/assets/erc-7738/hardhat.config.ts b/assets/erc-7738/hardhat.config.ts new file mode 100644 index 0000000000..5b44ba3649 --- /dev/null +++ b/assets/erc-7738/hardhat.config.ts @@ -0,0 +1,32 @@ +import "dotenv/config" +import { HardhatUserConfig } from "hardhat/config" +import "@nomicfoundation/hardhat-toolbox" +import "hardhat-gas-reporter" + +let { PRIVATE_KEY, INFURA_KEY, ETHERSCAN_API_KEY } = process.env; + +const config: HardhatUserConfig = { + solidity: { compilers: [{ version: "0.8.24" }, { version: "0.8.24" }] }, + networks: { + mainnet: { + url: `https://mainnet.infura.io/v3/${INFURA_KEY}`, + accounts: [`${PRIVATE_KEY}`] + }, + sepolia: { + url: `https://sepolia.infura.io/v3/${INFURA_KEY}`, + accounts: [`${PRIVATE_KEY}`], + }, + mumbai: { + url: `https://polygon-mumbai.infura.io/v3/${INFURA_KEY}`, + accounts: [`${PRIVATE_KEY}`] + }, + hardhat: { + gasPrice: 100000000000, + } + }, + etherscan: { + apiKey: `${ETHERSCAN_API_KEY}` //for contract verify + } +} + +export default config diff --git a/assets/erc-7738/ignition/modules/Lock.ts b/assets/erc-7738/ignition/modules/Lock.ts new file mode 100644 index 0000000000..eda0eba45b --- /dev/null +++ b/assets/erc-7738/ignition/modules/Lock.ts @@ -0,0 +1,17 @@ +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +const JAN_1ST_2030 = 1893456000; +const ONE_GWEI: bigint = 1_000_000_000n; + +const LockModule = buildModule("LockModule", (m) => { + const unlockTime = m.getParameter("unlockTime", JAN_1ST_2030); + const lockedAmount = m.getParameter("lockedAmount", ONE_GWEI); + + const lock = m.contract("Lock", [unlockTime], { + value: lockedAmount, + }); + + return { lock }; +}); + +export default LockModule; diff --git a/assets/erc-7738/package-lock.json b/assets/erc-7738/package-lock.json new file mode 100644 index 0000000000..7b67bc2db6 --- /dev/null +++ b/assets/erc-7738/package-lock.json @@ -0,0 +1,6944 @@ +{ + "name": "hardhat", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hardhat", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.6", + "@openzeppelin/contracts": "^5.0.2", + "dotenv": "^16.4.5", + "ethers": "^6.13.1", + "hardhat": "^2.22.6", + "stl-contracts": "^2.5.1" + }, + "devDependencies": { + "@nomicfoundation/hardhat-ignition-ethers": "^0.15.5", + "@nomicfoundation/hardhat-toolbox": "^5.0.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "devOptional": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "dev": true, + "peer": true, + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dev": true, + "peer": true, + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/json-wallets/node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true, + "peer": true + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/providers/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "devOptional": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "devOptional": true, + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/@metamask/eth-sig-util/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "peer": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nomicfoundation/edr": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.4.1.tgz", + "integrity": "sha512-NgrMo2rI9r28uidumvd+K2/AJLdxtXsUlJr3hj/pM6S1FCd/HiWaLeLa/cjCVPcE2u1rYAa3W6UFxLCB7S5Dhw==", + "dependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.4.1", + "@nomicfoundation/edr-darwin-x64": "0.4.1", + "@nomicfoundation/edr-linux-arm64-gnu": "0.4.1", + "@nomicfoundation/edr-linux-arm64-musl": "0.4.1", + "@nomicfoundation/edr-linux-x64-gnu": "0.4.1", + "@nomicfoundation/edr-linux-x64-musl": "0.4.1", + "@nomicfoundation/edr-win32-x64-msvc": "0.4.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.4.1.tgz", + "integrity": "sha512-XuiUUnWAVNw7JYv7nRqDWfpBm21HOxCRBQ8lQnRnmiets9Ss2X5Ul9mvBheIPh/D0wBzwJ8TRtsSrorpwE79cA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.4.1.tgz", + "integrity": "sha512-N1MfJqEX5ixaXlyyrHnaYxzwIT27Nc/jUgLI7ts4/9kRvPTvyZRYmXS1ciKhmUFr/WvFckTCix2RJbZoGGtX7g==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.4.1.tgz", + "integrity": "sha512-bSPOfmcFjJwDgWOV5kgZHeqg2OWu1cINrHSGjig0aVHehjcoX4Sgayrj6fyAxcOV5NQKA6WcyTFll6NrCxzWRA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.4.1.tgz", + "integrity": "sha512-F/+DgOdeBFQDrk+SX4aFffJFBgJfd75ZtE2mjcWNAh/qWiS7NfUxdQX/5OvNo/H6EY4a+3bZH6Bgzqg4mEWvMw==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.4.1.tgz", + "integrity": "sha512-POHhTWczIXCPhzKtY0Vt/l+VCqqCx5gNR5ErwSrNnLz/arfQobZFAU+nc61BX3Jch82TW8b3AbfGI73Kh7gO0w==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.4.1.tgz", + "integrity": "sha512-uu8oNp4Ozg3H1x1We0FF+rwXfFiAvsOm5GQ+OBx9YYOXnfDPWqguQfGIkhrti9GD0iYhfQ/WOG5wvp0IzzgGSg==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.4.1.tgz", + "integrity": "sha512-PaZHFw455z89ZiKYNTnKu+/TiVZVRI+mRJsbRTe2N0VlYfUBS1o2gdXBM12oP1t198HR7xQwEPPAslTFxGBqHA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", + "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", + "dependencies": { + "@nomicfoundation/ethereumjs-util": "9.0.4" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", + "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", + "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", + "dependencies": { + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", + "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/hardhat-chai-matchers": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.7.tgz", + "integrity": "sha512-RQfsiTwdf0SP+DtuNYvm4921X6VirCQq0Xyh+mnuGlTwEFSPZ/o27oQC+l+3Y/l48DDU7+ZcYBR+Fp+Rp94LfQ==", + "dev": true, + "peer": true, + "dependencies": { + "@types/chai-as-promised": "^7.1.3", + "chai-as-promised": "^7.1.1", + "deep-eql": "^4.0.1", + "ordinal": "^1.0.3" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "chai": "^4.2.0", + "ethers": "^6.1.0", + "hardhat": "^2.9.4" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.6.tgz", + "integrity": "sha512-/xzkFQAaHQhmIAYOQmvHBPwL+NkwLzT9gRZBsgWUYeV+E6pzXsBQsHfRYbAZ3XEYare+T7S+5Tg/1KDJgepSkA==", + "dependencies": { + "debug": "^4.1.1", + "lodash.isequal": "^4.5.0" + }, + "peerDependencies": { + "ethers": "^6.1.0", + "hardhat": "^2.0.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition/-/hardhat-ignition-0.15.5.tgz", + "integrity": "sha512-Y5nhFXFqt4owA6Ooag8ZBFDF2RAZElMXViknVIsi3m45pbQimS50ti6FU8HxfRkDnBARa40CIn7UGV0hrelzDw==", + "dev": true, + "peer": true, + "dependencies": { + "@nomicfoundation/ignition-core": "^0.15.5", + "@nomicfoundation/ignition-ui": "^0.15.5", + "chalk": "^4.0.0", + "debug": "^4.3.2", + "fs-extra": "^10.0.0", + "prompts": "^2.4.2" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-verify": "^2.0.1", + "hardhat": "^2.18.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition-ethers": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition-ethers/-/hardhat-ignition-ethers-0.15.5.tgz", + "integrity": "sha512-W6s1QN9CFxzSVZS6w9Jcj3WLaK32z2FP5MxNU2OKY1Fn9ZzLr+miXbUbWYuRHl6dxrrl6sE8cv33Cybv19pmCg==", + "dev": true, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.4", + "@nomicfoundation/hardhat-ignition": "^0.15.5", + "@nomicfoundation/ignition-core": "^0.15.5", + "ethers": "^6.7.0", + "hardhat": "^2.18.0" + } + }, + "node_modules/@nomicfoundation/hardhat-network-helpers": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.11.tgz", + "integrity": "sha512-uGPL7QSKvxrHRU69dx8jzoBvuztlLCtyFsbgfXIwIjnO3dqZRz2GNMHJoO3C3dIiUNM6jdNF4AUnoQKDscdYrA==", + "dev": true, + "peer": true, + "dependencies": { + "ethereumjs-util": "^7.1.4" + }, + "peerDependencies": { + "hardhat": "^2.9.5" + } + }, + "node_modules/@nomicfoundation/hardhat-toolbox": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-5.0.0.tgz", + "integrity": "sha512-FnUtUC5PsakCbwiVNsqlXVIWG5JIb5CEZoSXbJUsEBun22Bivx2jhF1/q9iQbzuaGpJKFQyOhemPB2+XlEE6pQ==", + "dev": true, + "peerDependencies": { + "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0", + "@nomicfoundation/hardhat-network-helpers": "^1.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "@typechain/ethers-v6": "^0.5.0", + "@typechain/hardhat": "^9.0.0", + "@types/chai": "^4.2.0", + "@types/mocha": ">=9.1.0", + "@types/node": ">=18.0.0", + "chai": "^4.2.0", + "ethers": "^6.4.0", + "hardhat": "^2.11.0", + "hardhat-gas-reporter": "^1.0.8", + "solidity-coverage": "^0.8.1", + "ts-node": ">=8.0.0", + "typechain": "^8.3.0", + "typescript": ">=4.5.0" + } + }, + "node_modules/@nomicfoundation/hardhat-verify": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.8.tgz", + "integrity": "sha512-x/OYya7A2Kcz+3W/J78dyDHxr0ezU23DKTrRKfy5wDPCnePqnr79vm8EXqX3gYps6IjPBYyGPZ9K6E5BnrWx5Q==", + "dev": true, + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@ethersproject/address": "^5.0.2", + "cbor": "^8.1.0", + "chalk": "^2.4.2", + "debug": "^4.1.1", + "lodash.clonedeep": "^4.5.0", + "semver": "^6.3.0", + "table": "^6.8.0", + "undici": "^5.14.0" + }, + "peerDependencies": { + "hardhat": "^2.0.4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/ignition-core": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-core/-/ignition-core-0.15.5.tgz", + "integrity": "sha512-FgvuoIXhakRSP524JzNQ4BviyzBBKpsFaOWubPZ4XACLT4/7vGqlJ/7DIn0D2NL2anQ2qs98/BNBY9WccXUX1Q==", + "dev": true, + "peer": true, + "dependencies": { + "@ethersproject/address": "5.6.1", + "@nomicfoundation/solidity-analyzer": "^0.1.1", + "cbor": "^9.0.0", + "debug": "^4.3.2", + "ethers": "^6.7.0", + "fs-extra": "^10.0.0", + "immer": "10.0.2", + "lodash": "4.17.21", + "ndjson": "2.0.0" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/@ethersproject/address": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.1.tgz", + "integrity": "sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.6.2", + "@ethersproject/bytes": "^5.6.1", + "@ethersproject/keccak256": "^5.6.1", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/rlp": "^5.6.1" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dev": true, + "peer": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@nomicfoundation/ignition-ui": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-ui/-/ignition-ui-0.15.5.tgz", + "integrity": "sha512-ZcE4rIn10qKahR4OqS8rl8NM2Fbg2QYiBXgMgj74ZI0++LlCcZgB5HyaBbX+lsnKHjTXtjYD3b+2mtg7jFbAMQ==", + "dev": true, + "peer": true + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz", + "integrity": "sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==", + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.2", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.2" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz", + "integrity": "sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz", + "integrity": "sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz", + "integrity": "sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz", + "integrity": "sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz", + "integrity": "sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz", + "integrity": "sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz", + "integrity": "sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@openzeppelin/contracts": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.2.tgz", + "integrity": "sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==" + }, + "node_modules/@scure/base": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz", + "integrity": "sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dependencies": { + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@solidity-parser/parser": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", + "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", + "dev": true, + "peer": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "devOptional": true, + "peer": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "devOptional": true, + "peer": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "devOptional": true, + "peer": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "devOptional": true, + "peer": true + }, + "node_modules/@typechain/ethers-v6": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz", + "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==", + "dev": true, + "peer": true, + "dependencies": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + }, + "peerDependencies": { + "ethers": "6.x", + "typechain": "^8.3.2", + "typescript": ">=4.7.0" + } + }, + "node_modules/@typechain/hardhat": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-9.1.0.tgz", + "integrity": "sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==", + "dev": true, + "peer": true, + "dependencies": { + "fs-extra": "^9.1.0" + }, + "peerDependencies": { + "@typechain/ethers-v6": "^0.5.1", + "ethers": "^6.1.0", + "hardhat": "^2.9.9", + "typechain": "^8.3.2" + } + }, + "node_modules/@typechain/hardhat/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "peer": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", + "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", + "dev": true, + "peer": true + }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "peer": true + }, + "node_modules/@types/mocha": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", + "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "dev": true, + "peer": true + }, + "node_modules/@types/node": { + "version": "20.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", + "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "peer": true + }, + "node_modules/@types/qs": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", + "dev": true, + "peer": true + }, + "node_modules/@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true, + "peer": true + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "devOptional": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "devOptional": true, + "peer": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "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==", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", + "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true, + "peer": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true, + "peer": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "peer": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true, + "peer": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "peer": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dev": true, + "peer": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "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==" + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "dev": true, + "peer": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "peer": true + }, + "node_modules/cbor": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", + "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", + "dev": true, + "peer": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "peer": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", + "dev": true, + "peer": true, + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "peer": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "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==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "peer": true, + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cli-table3/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "peer": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "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==" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "peer": true, + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", + "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", + "dev": true, + "peer": true, + "dependencies": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/command-line-usage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-usage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/command-line-usage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "node_modules/command-line-usage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/command-line-usage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "peer": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "peer": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true, + "peer": true + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", + "dev": true, + "peer": true + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "peer": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "peer": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/difflib": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", + "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", + "dev": true, + "peer": true, + "dependencies": { + "heap": ">= 0.2.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "peer": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "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==" + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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==", + "engines": { + "node": ">=6" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "peer": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "peer": true, + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eth-gas-reporter": { + "version": "0.2.27", + "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.27.tgz", + "integrity": "sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw==", + "dev": true, + "peer": true, + "dependencies": { + "@solidity-parser/parser": "^0.14.0", + "axios": "^1.5.1", + "cli-table3": "^0.5.0", + "colors": "1.4.0", + "ethereum-cryptography": "^1.0.3", + "ethers": "^5.7.2", + "fs-readdir-recursive": "^1.1.0", + "lodash": "^4.17.14", + "markdown-table": "^1.1.3", + "mocha": "^10.2.0", + "req-cwd": "^2.0.0", + "sha1": "^1.1.1", + "sync-request": "^6.0.0" + }, + "peerDependencies": { + "@codechecks/client": "^0.1.0" + }, + "peerDependenciesMeta": { + "@codechecks/client": { + "optional": true + } + } + }, + "node_modules/eth-gas-reporter/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "peer": true + }, + "node_modules/eth-gas-reporter/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "peer": true, + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "peer": true, + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/eth-gas-reporter/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, + "node_modules/ethereum-bloom-filters": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.1.0.tgz", + "integrity": "sha512-J1gDRkLpuGNvWYzWslBQR9cDV4nd4kfvVTE/Wy4Kkm4yb3EYRSlyi0eB/inTsSTTVyA0+HyzHgbr95Fn/Z1fSw==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "^1.4.0" + } + }, + "node_modules/ethereum-bloom-filters/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "dependencies": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-abi/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-abi/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ethereumjs-abi/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethers": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.1.tgz", + "integrity": "sha512-hdJ2HOxg/xx97Lm9HdCWk949BfYqYWpyw4//78SiwOLgASyfrNszfMUNB2joKjvGUdwhHfaiMMFFwacVVoLR9A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" + }, + "node_modules/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "dev": true, + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "peer": true + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "peer": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "peer": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "peer": true, + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==" + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true, + "peer": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", + "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^2.4.2", + "node-emoji": "^1.10.0" + }, + "bin": { + "testrpc-sc": "index.js" + } + }, + "node_modules/ghost-testrpc/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ghost-testrpc/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "node_modules/ghost-testrpc/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ghost-testrpc/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "peer": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "peer": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "peer": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "peer": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hardhat": { + "version": "2.22.6", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.6.tgz", + "integrity": "sha512-abFEnd9QACwEtSvZZGSmzvw7N3zhQN1cDKz5SLHAupfG24qTHofCjqvD5kT5Wwsq5XOL0ON1Mq5rr4v0XX5ciw==", + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/edr": "^0.4.1", + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-tx": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "@nomicfoundation/solidity-analyzer": "^0.1.0", + "@sentry/node": "^5.18.1", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "^5.1.0", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "boxen": "^5.1.2", + "chalk": "^2.4.2", + "chokidar": "^3.4.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "ethereumjs-abi": "^0.6.8", + "find-up": "^2.1.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "glob": "7.2.0", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.8.26", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tsort": "0.0.1", + "undici": "^5.14.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "bin": { + "hardhat": "internal/cli/bootstrap.js" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/hardhat-gas-reporter": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.10.tgz", + "integrity": "sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA==", + "dev": true, + "peer": true, + "dependencies": { + "array-uniq": "1.0.3", + "eth-gas-reporter": "^0.2.25", + "sha1": "^1.1.1" + }, + "peerDependencies": { + "hardhat": "^2.0.2" + } + }, + "node_modules/hardhat/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/hardhat/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/hardhat/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/hardhat/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/hardhat/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/hardhat/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/hardhat/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/hardhat/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/hardhat/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "peer": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", + "dev": true, + "peer": true + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "peer": true, + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true, + "peer": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.2.tgz", + "integrity": "sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==", + "dev": true, + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==" + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "peer": true + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dependencies": { + "fp-ts": "^1.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "peer": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "peer": true + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "peer": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "peer": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "peer": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "peer": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true, + "peer": true + }, + "node_modules/markdown-table": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", + "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", + "dev": true, + "peer": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "dev": true, + "peer": true + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "peer": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dependencies": { + "obliterator": "^2.0.0" + } + }, + "node_modules/mocha": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz", + "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/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==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/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==" + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ndjson": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ndjson/-/ndjson-2.0.0.tgz", + "integrity": "sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==", + "dev": true, + "peer": true, + "dependencies": { + "json-stringify-safe": "^5.0.1", + "minimist": "^1.2.5", + "readable-stream": "^3.6.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "ndjson": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "peer": true + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "peer": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "peer": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "dev": true, + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "peer": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "peer": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ordinal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", + "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", + "dev": true, + "peer": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true, + "peer": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "peer": true + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "peer": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "peer": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "peer": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.2.tgz", + "integrity": "sha512-x+NLUpx9SYrcwXtX7ob1gnkSems4i/mGZX5SlYxwIau6RrUSODO89TR/XDGGpn5RPWSYIB+aSfuSlV5+CmbTBg==", + "dev": true, + "peer": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "peer": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dev": true, + "peer": true, + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/req-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", + "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", + "dev": true, + "peer": true, + "dependencies": { + "req-from": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/req-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", + "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", + "dev": true, + "peer": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "peer": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sc-istanbul": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", + "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", + "dev": true, + "peer": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/sc-istanbul/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/sc-istanbul/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "peer": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sc-istanbul/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sc-istanbul/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true, + "peer": true + }, + "node_modules/sc-istanbul/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "peer": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", + "dev": true, + "peer": true, + "dependencies": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "peer": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/solc": { + "version": "0.8.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", + "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", + "dependencies": { + "command-exists": "^1.2.8", + "commander": "^8.1.0", + "follow-redirects": "^1.12.1", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solc.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/solidity-coverage": { + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.12.tgz", + "integrity": "sha512-8cOB1PtjnjFRqOgwFiD8DaUsYJtVJ6+YdXQtSZDrLGf8cdhhh8xzTtGzVTGeBf15kTv0v7lYPJlV/az7zLEPJw==", + "dev": true, + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.0.9", + "@solidity-parser/parser": "^0.18.0", + "chalk": "^2.4.2", + "death": "^1.1.0", + "difflib": "^0.2.4", + "fs-extra": "^8.1.0", + "ghost-testrpc": "^0.0.2", + "global-modules": "^2.0.0", + "globby": "^10.0.1", + "jsonschema": "^1.2.4", + "lodash": "^4.17.21", + "mocha": "^10.2.0", + "node-emoji": "^1.10.0", + "pify": "^4.0.1", + "recursive-readdir": "^2.2.2", + "sc-istanbul": "^0.4.5", + "semver": "^7.3.4", + "shelljs": "^0.8.3", + "web3-utils": "^1.3.6" + }, + "bin": { + "solidity-coverage": "plugins/bin.js" + }, + "peerDependencies": { + "hardhat": "^2.11.0" + } + }, + "node_modules/solidity-coverage/node_modules/@solidity-parser/parser": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", + "dev": true, + "peer": true + }, + "node_modules/solidity-coverage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/solidity-coverage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "node_modules/solidity-coverage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/solidity-coverage/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/solidity-coverage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/solidity-coverage/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solidity-coverage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "peer": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "peer": true + }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stl-contracts": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/stl-contracts/-/stl-contracts-2.5.1.tgz", + "integrity": "sha512-fkIq5BPq2/MTkQEDgXRM5WtrpQXqar3lDtha7N446UoCvNWP2isKfcRJ0RKatyMegGvRK1yWugDbbR3OGZiGXg==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", + "dev": true, + "peer": true + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dev": true, + "peer": true, + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dev": true, + "peer": true, + "dependencies": { + "get-port": "^3.1.0" + } + }, + "node_modules/table": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "dev": true, + "peer": true, + "dependencies": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true, + "peer": true + }, + "node_modules/then-request/node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "peer": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-command-line-args": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz", + "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^6.1.0", + "string-format": "^2.0.0" + }, + "bin": { + "write-markdown": "dist/write-markdown.js" + } + }, + "node_modules/ts-essentials": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", + "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "typescript": ">=3.7.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "devOptional": true, + "peer": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true, + "peer": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typechain": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", + "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", + "dev": true, + "peer": true, + "dependencies": { + "@types/prettier": "^2.1.1", + "debug": "^4.3.1", + "fs-extra": "^7.0.0", + "glob": "7.1.7", + "js-sha3": "^0.8.0", + "lodash": "^4.17.15", + "mkdirp": "^1.0.4", + "prettier": "^2.3.1", + "ts-command-line-args": "^2.2.0", + "ts-essentials": "^7.0.1" + }, + "bin": { + "typechain": "dist/cli/cli.js" + }, + "peerDependencies": { + "typescript": ">=4.3.0" + } + }, + "node_modules/typechain/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/typechain/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typechain/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/typechain/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typechain/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "peer": true + }, + "node_modules/typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "devOptional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uglify-js": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz", + "integrity": "sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "dev": true, + "peer": true + }, + "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==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "devOptional": true, + "peer": true + }, + "node_modules/web3-utils": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", + "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", + "dev": true, + "peer": true, + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "peer": true + }, + "node_modules/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "dev": true, + "peer": true, + "dependencies": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==" + }, + "node_modules/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==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/assets/erc-7738/package.json b/assets/erc-7738/package.json new file mode 100644 index 0000000000..ff5f4896e3 --- /dev/null +++ b/assets/erc-7738/package.json @@ -0,0 +1,24 @@ +{ + "name": "hardhat", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.6", + "@openzeppelin/contracts": "^5.0.2", + "dotenv": "^16.4.5", + "ethers": "^6.13.1", + "hardhat": "^2.22.6", + "stl-contracts": "^2.5.1" + }, + "devDependencies": { + "@nomicfoundation/hardhat-ignition-ethers": "^0.15.5", + "@nomicfoundation/hardhat-toolbox": "^5.0.0" + } +} diff --git a/assets/erc-7738/test/DecentralisedRegistry.test.ts b/assets/erc-7738/test/DecentralisedRegistry.test.ts new file mode 100644 index 0000000000..677f8b7deb --- /dev/null +++ b/assets/erc-7738/test/DecentralisedRegistry.test.ts @@ -0,0 +1,151 @@ +const { ethers } = require("hardhat"); + +import { expect } from "chai"; +import { + loadFixture, +} from "@nomicfoundation/hardhat-toolbox/network-helpers"; + +const scriptURI1: string = "https://scripttoken.net/script1"; +const scriptURI2: string = "https://scripttoken.net/script2"; +const scriptURI3: string = "https://scripttoken.net/script3"; +const scriptURI4: string = "https://scripttoken.net/script4"; +const origScriptURI: string = "https://scripttoken.net/script"; + +async function deployInitialFixture() { + // Contracts are deployed using the first signer/account by default + const [owner, otherAccount, otherAccount2, otherAccount3, otherAccount4] = await ethers.getSigners(); + + const StakingToken = (await ethers.getContractFactory("StakingToken")).connect( + owner + ); + const stakingToken = await StakingToken.deploy(); + await stakingToken.waitForDeployment(); + + //Deploy registry contract + const Registry = (await ethers.getContractFactory("DecentralisedRegistry")).connect( + otherAccount + ); + const registry = await Registry.deploy(); + await registry.waitForDeployment(); + + return { + owner, + otherAccount, + otherAccount2, + otherAccount3, + otherAccount4, + stakingToken, + registry, + }; +} + +describe("DecentralisedRegistry", function () { + + it("mint and set scriptURI with owner", async function () { + const { + owner, + otherAccount, + otherAccount2, + otherAccount3, + otherAccount4, + stakingToken, + registry + } = await loadFixture(deployInitialFixture); + + await expect(stakingToken.connect(owner).safeMint()) + .to.emit(stakingToken, "Transfer") + .withArgs(ethers.ZeroAddress, owner.address, 1); + + const scriptURI = [scriptURI1]; + const scriptURITest2 = [scriptURI2]; + const scriptURITest3 = [scriptURI3, scriptURI4]; + const origScript = [origScriptURI]; + + //set scriptURI + await registry.connect(otherAccount2).setScriptURI(stakingToken.target, scriptURI); + + //console.log(`Current ScriptURI = ${await registry.scriptURI(stakingToken.target)}`); + + expect((await registry.scriptURI(stakingToken.target)).toString()).to.be.equal(scriptURI.toString()); + + //attempt to set Script URI with delegate account + await registry.connect(otherAccount).setScriptURI(stakingToken.target, scriptURITest2); + + //now dump the current full scriptURI + //console.log(`Current ScriptURI = ${await registry.scriptURI(stakingToken.target)}`); + + await registry.connect(otherAccount3).setScriptURI(stakingToken.target, scriptURITest3); + + //console.log(`Current ScriptURI = ${await registry.scriptURI(stakingToken.target)}`); + + //now owner writes, should come first + await registry.connect(owner).setScriptURI(stakingToken.target, origScript); + + let finalScriptURI = await registry.scriptURI(stakingToken.target); + + //console.log(`Current ScriptURI = ${finalScriptURI}`); + + //test first element + let firstScriptURI = finalScriptURI[0]; + + //ensure owner script is first in list + expect(firstScriptURI).to.equal(origScriptURI); + + //add another entry + await registry.connect(otherAccount4).setScriptURI(stakingToken.target, [scriptURI4, scriptURI1, scriptURI2]); + + //owner script should still be first + finalScriptURI = await registry.scriptURI(stakingToken.target); + firstScriptURI = finalScriptURI[0]; + expect(firstScriptURI).to.equal(origScriptURI); + + //attempt to write zero sized scriptURI + await expect(registry.connect(otherAccount4).setScriptURI(stakingToken.target, [])) + .to.revertedWith("> 0 entries required in scriptURIList"); + + //remove an entry + await registry.connect(otherAccount4).setScriptURI(stakingToken.target, [""]); + //console.log(`Current ScriptURI = ${await registry.scriptURI(stakingToken.target)}`); + + //and another + await registry.connect(otherAccount3).setScriptURI(stakingToken.target, [""]); + //console.log(`Current ScriptURI = ${await registry.scriptURI(stakingToken.target)}`); + + //now add back + await registry.connect(otherAccount3).setScriptURI(stakingToken.target, [scriptURI4, scriptURI1, scriptURI2]); + finalScriptURI = await registry.scriptURI(stakingToken.target); + //console.log(`Current ScriptURI = ${finalScriptURI}`); + + //check final return, should be: + let correctFinal = [origScriptURI, scriptURI1, scriptURI2, scriptURI4, scriptURI1, scriptURI2, ""]; + expect((await registry.scriptURI(stakingToken.target)).toString()).to.be.equal(correctFinal.toString()); + + checkPageSize(2, correctFinal, registry, stakingToken); + checkPageSize(3, correctFinal, registry, stakingToken); + checkPageSize(1, correctFinal, registry, stakingToken); + checkPageSize(500, correctFinal, registry, stakingToken); + }); + + + async function checkPageSize(pageSize: number, correctFinal: string[], registry: any, stakingToken: any) { + const totalPages = Math.ceil(correctFinal.length / pageSize); + + for (let page = 1; page <= totalPages; page++) { + let start = (page - 1) * pageSize; + let expected = []; + + for (let j = 0; j < pageSize; j++) { + if (start + j < correctFinal.length) { + expected[j] = correctFinal[start + j]; + } else { + break; + } + } + + let contractReturn = await registry.scriptURI(stakingToken.target, page, pageSize); + //console.log(`Actual return = ${contractReturn} Expected = ${expected}`); + expect(contractReturn.toString()).to.be.equal(expected.toString()); + } + } + +}) \ No newline at end of file diff --git a/assets/erc-7738/test/PermissionedRegistry.test.ts b/assets/erc-7738/test/PermissionedRegistry.test.ts new file mode 100644 index 0000000000..d14c7adbe9 --- /dev/null +++ b/assets/erc-7738/test/PermissionedRegistry.test.ts @@ -0,0 +1,111 @@ +const { ethers } = require("hardhat"); + +import { expect } from "chai"; +import { + loadFixture, +} from "@nomicfoundation/hardhat-toolbox/network-helpers"; + +const scriptURI1: string = "https://scripttoken.net/script1"; + +async function deployInitialFixture() { + // Contracts are deployed using the first signer/account by default + const [owner, otherAccount, otherAccount2] = await ethers.getSigners(); + + const StakingToken = (await ethers.getContractFactory("StakingToken")).connect( + owner + ); + const stakingToken = await StakingToken.deploy(); + await stakingToken.waitForDeployment(); + + //Deploy registry contract + const Registry = (await ethers.getContractFactory("DecentralisedRegistryPermissioned")).connect( + otherAccount + ); + const registry = await Registry.deploy(); + await registry.waitForDeployment(); + + return { + owner, + otherAccount, + otherAccount2, + stakingToken, + registry, + }; +} + +describe("Decentralised Permissioned Registry", function () { + + it("mint and set scriptURI with owner", async function () { + const { + owner, + otherAccount, + otherAccount2, + stakingToken, + registry + } = await loadFixture(deployInitialFixture); + + await expect(stakingToken.connect(owner).safeMint()) + .to.emit(stakingToken, "Transfer") + .withArgs(ethers.ZeroAddress, owner.address, 1); + + //initially register with otherAccount + await registry.connect(otherAccount).registerOwner(stakingToken.target); + + //owner can override + await registry.connect(owner).registerOwner(stakingToken.target); + + await expect(registry.connect(otherAccount).registerOwner(stakingToken.target)) + .to.revertedWith("Not authorized"); + + const scriptURI = [scriptURI1]; + + //set scriptURI + await expect(registry.connect(otherAccount).setScriptURI(stakingToken.target, scriptURI)) + .to.revertedWith("Not authorized"); + await expect(registry.connect(otherAccount2).setScriptURI(stakingToken.target, scriptURI)) + .to.revertedWith("Not authorized"); + + await registry.connect(owner).setScriptURI(stakingToken.target, scriptURI); + + expect((await registry.scriptURI(stakingToken.target)).toString()).to.be.equal(scriptURI.toString()); + + //add delegate + await expect(registry.connect(otherAccount).addDelegateSigner(stakingToken.target, otherAccount2)) + .to.revertedWith("Contract Owner only"); + + await registry.connect(owner).addDelegateSigner(stakingToken.target, otherAccount2); + + //attempt to set Script URI with delegate account + await registry.connect(otherAccount2).setScriptURI(stakingToken.target, scriptURI); + + // Verify script signing operation. + // Assume that a process has come back from evaluating the address of a TokenScript and needs to verify if the + // key is allowed to validate the contract + let ecRecoverSigningKey = otherAccount2.address; + + expect((await registry.isDelegateOrOwner(stakingToken.target, otherAccount.address))) + .to.be.equal(false); + + expect((await registry.isDelegateOrOwner(stakingToken.target, ecRecoverSigningKey))) + .to.be.equal(true); + + expect((await registry.isDelegateOrOwner(stakingToken.target, owner.address))) + .to.be.equal(true); + + // revoke + await expect(registry.connect(otherAccount2).revokeDelegateSigner(stakingToken.target, otherAccount2)) + .to.revertedWith("Contract Owner only"); + + await registry.connect(owner).revokeDelegateSigner(stakingToken.target, otherAccount2); + + await expect(registry.connect(otherAccount2).setScriptURI(stakingToken.target, scriptURI)) + .to.revertedWith("Not authorized"); + + expect((await registry.isDelegateOrOwner(stakingToken.target, ecRecoverSigningKey))) + .to.be.equal(false); + + expect((await registry.isDelegateOrOwner(stakingToken.target, owner.address))) + .to.be.equal(true); + }); + +}) \ No newline at end of file diff --git a/assets/erc-7738/tsconfig.json b/assets/erc-7738/tsconfig.json new file mode 100644 index 0000000000..574e785c71 --- /dev/null +++ b/assets/erc-7738/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true + } +} From def486b1ede283f6166f6b9a83200cbd05e5089e Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Mon, 5 Aug 2024 03:07:47 -0400 Subject: [PATCH 090/126] Update ERC-7682: Add assets Merged by EIP-Bot. --- ERCS/erc-7682.md | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/ERCS/erc-7682.md b/ERCS/erc-7682.md index a210c89dce..226aad6b19 100644 --- a/ERCS/erc-7682.md +++ b/ERCS/erc-7682.md @@ -1,7 +1,7 @@ --- eip: 7682 title: Auxiliary Funds Capability -description: A way for wallets to indicate to apps that they have access to additional funds +description: A capability allowing wallets to indicate that they have access to additional funds. author: Lukas Rosario (@lukasrosario), Wilson Cusack (@wilsoncusack) discussions-to: https://ethereum-magicians.org/t/erc-7682-auxiliary-funds-capability/19599 status: Draft @@ -15,6 +15,11 @@ requires: 5792 An [EIP-5792](./eip-5792.md) compliant capability that allows wallets to indicate to apps that they have access to funds beyond those that can be accounted for by looking up balances onchain given the wallet's address. +A wallet's ability to access auxiliary funds is communicated to apps as part of its response to an [EIP-5792](./eip-5792.md) `wallet_getCapabilities` request. The following standard does not specify the source of these auxiliary funds, but some examples are: + +- Funds from offchain sources that can be onramped and used just-in-time +- Wallets that manage many accounts, where assets across those accounts can be transfered to the required account before submitting a transaction requested by an app + ## Motivation Many applications check users' balances before letting them complete some action. For example, if a user wants to swap some amount of tokens on a dex, the dex will commonly block the user from doing so if it sees that the user does not have that amount of tokens at their address. However, more advanced wallets have features that let users access funds from other sources. Wallets need a way to tell apps that they have access to additional funds so that users using these more advanced wallets are not blocked by balance checks. @@ -25,13 +30,20 @@ One new [EIP-5792](./eip-5792.md) wallet capability is defined. ### Wallet Implementation -To conform to this specification, wallets that wish to indicate that they have access to auxiliary funds MUST respond to `wallet_getCapabilities` calls with an `auxiliaryFunds` object with a `supported` field set to `true` for each chain they have access to auxiliary funds on. This specification does not put any constraints on the source of the auxiliary funds. +To conform to this specification, wallets that wish to indicate that they have access to auxiliary funds MUST, for each chain they have access to auxiliary funds on, respond to `wallet_getCapabilities` calls with an `auxiliaryFunds` object with a `supported` field set to `true`. + +Wallets may also optionally specify which assets they have additional access to with an `assets` field, which maps to an array of addresses representing the assets the wallet might have additional access to. If a wallet does not respond with this optional array of assets, the application SHOULD assume the wallet has additional access to any asset. + +This specification does not put any constraints on the source of the auxiliary funds. + +In this specification, a chain's native asset (e.g. Ether on Ethereum) MUST be represented by "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" as specified by [EIP-7528](./eip-7528). #### `wallet_getCapabilities` Response Specification ```typescript type AuxiliaryFundsCapability = { supported: boolean; + assets?: `0x${string}`[]; } ``` @@ -41,12 +53,20 @@ type AuxiliaryFundsCapability = { { "0x2105": { "auxiliaryFunds": { - "supported": true - }, + "supported": true, + "assets": [ + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" + ] + } }, "0x14A34": { "auxiliaryFunds": { - "supported": true + "supported": true, + "assets": [ + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "0x036CbD53842c5426634e7929541eC2318f3dCF7e" + ] } } } @@ -66,10 +86,6 @@ An alternative we considered is defining a way for apps to fetch available auxil The shape of this capability allows for a more advanced extension if apps feel more functionality is needed. -#### Auxiliary Funds per Asset - -We could also specify auxiliary funds support per asset. We decided against this because this list could get quite large if a wallet has auxiliary funds supports for many assets, and a single boolean should be enough for apps to not block users from taking actions. - ## Security Considerations Apps MUST NOT make any assumptions about the source of auxiliary funds. Apps' smart contracts should still, as they would today, make appropriate balance checks onchain when processing a transaction. From f899c81ec706b052a8c93cc421cda6710f0e881a Mon Sep 17 00:00:00 2001 From: Christian Fries Date: Thu, 8 Aug 2024 13:11:06 +0200 Subject: [PATCH 091/126] Website: Reverting change tradeId to tradeData for termination. Merged by EIP-Bot. --- ERCS/erc-6123.md | 47 +++++++++++++------- assets/erc-6123/contracts/ISDC.sol | 30 +++++++------ assets/erc-6123/contracts/SDCSingleTrade.sol | 11 +++-- assets/erc-6123/package.json | 2 +- assets/erc-6123/test/SDCTests.js | 20 ++++++--- 5 files changed, 71 insertions(+), 39 deletions(-) diff --git a/ERCS/erc-6123.md b/ERCS/erc-6123.md index de138cbe3b..0c9332eab3 100644 --- a/ERCS/erc-6123.md +++ b/ERCS/erc-6123.md @@ -65,9 +65,12 @@ The following methods specify a Smart Derivative Contract's trade initiation and A party can initiate a trade by providing the party address to trade with, trade data, trade position, payment amount for the trade and initial settlement data. Only registered counterparties are allowed to use that function. ```solidity -function inceptTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external; +function inceptTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external returns (string memory); ``` +The position and the paymentAmount are viewed from the incepter. +The function will return a generated unique `tradeId`. The trade id will also be emitted by an event. + #### Trade Initiation Phase: `confirmTrade` A counterparty can confirm a trade by providing its trade specification data, which then gets matched against the data stored from `inceptTrade` call. @@ -76,6 +79,8 @@ A counterparty can confirm a trade by providing its trade specification data, wh function confirmTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external; ``` +Here, the position and the paymentAmount is viewed from the confimer (opposite sign compared to the call to `inceptTrade`). + #### Trade Initiation Phase: `cancelTrade` The counterparty that called `inceptTrade` has the option to cancel the trade, e.g., in the case where the trade is not confirmed in a timely manner. @@ -109,7 +114,7 @@ This might result in a termination or start of the next settlement phase, depend The transactionData is emitted as part of the corresponding event: `TradeSettled` or `TradeTerminated` ```solidity -function afterTransfer(bool success, uint256 transactionData) external; +function afterTransfer(bool success, string memory transactionData) external; ``` @@ -162,7 +167,7 @@ event TradeConfirmed(address confirmer, string tradeId); Emitted on trade cancellation - method 'cancelTrade' ```solidity -event TradeCanceled(address confirmer, string tradeId); +event TradeCanceled(address initiator, string tradeId); ``` #### TradeActivated @@ -173,12 +178,13 @@ Emitted when a Trade is activated event TradeActivated(string tradeId); ``` + ### SettlementRequested Emitted when a settlement is requested. May trigger the settlement phase. ```solidity -event SettlementRequested(address initiator, string tradeId, string lastSettlementData); +event SettlementRequested(address initiator, string tradeData, string lastSettlementData); ``` ### SettlementEvaluated @@ -186,15 +192,33 @@ event SettlementRequested(address initiator, string tradeId, string lastSettleme Emitted when the settlement phase is started. ```solidity -event SettlementEvaluated(); +event SettlementEvaluated(address initiator, int256 settlementAmount, string settlementData); +``` + +### SettlementTransferred + +Emitted when the settlement succeeded. + +```solidity +event SettlementTransferred(string transactionData); ``` + +### SettlementFailed + +Emitted when the settlement failed. + +```solidity +event SettlementFailed(string transactionData); +``` + + #### TradeTerminationRequest Emitted when termination request is initiated by a counterparty ```solidity -event TradeTerminationRequest(address cpAddress, string tradeId, string terminationTerms); +event TradeTerminationRequest(address initiator, string tradeId, int256 terminationPayment, string terminationTerms); ``` #### TradeTerminationConfirmed @@ -202,7 +226,7 @@ event TradeTerminationRequest(address cpAddress, string tradeId, string terminat Emitted when termination request is confirmed by a counterparty ```solidity -event TradeTerminationConfirmed(address cpAddress, string tradeId, string terminationTerms); +event TradeTerminationConfirmed(address confirmer, string tradeId, int256 terminationPayment, string terminationTerms); ``` #### TradeTerminationCanceled @@ -210,7 +234,7 @@ event TradeTerminationConfirmed(address cpAddress, string tradeId, string termin Emitted when termination request is canceled by the requesting counterparty ```solidity -event TradeTerminationCanceled(address cpAddress, string tradeId, string terminationTerms); +event TradeTerminationCanceled(address initiator, string tradeId, string terminationTerms); ``` #### TradeTerminated @@ -221,13 +245,6 @@ Emitted when trade is terminated event TradeTerminated(string cause); ``` -#### ProcessHalted - -Emitted when trade processing stops. - -```solidity -event ProcessHalted(); -``` ## Rationale diff --git a/assets/erc-6123/contracts/ISDC.sol b/assets/erc-6123/contracts/ISDC.sol index dddc9bc172..1a2feb6c27 100644 --- a/assets/erc-6123/contracts/ISDC.sol +++ b/assets/erc-6123/contracts/ISDC.sol @@ -140,27 +140,29 @@ interface ISDC { /** * @dev Emitted when a counterparty proactively requests an early termination of the underlying trade - * @param cpAddress the address of the requesting party + * @param initiator the address of the requesting party + * @param terminationPayment an agreed termination amount (viewed from the requester) * @param tradeId the trade identifier which is supposed to be terminated * @param terminationTerms termination terms */ - event TradeTerminationRequest(address cpAddress, string tradeId, int256 terminationPayment, string terminationTerms); + event TradeTerminationRequest(address initiator, string tradeId, int256 terminationPayment, string terminationTerms); /** * @dev Emitted when early termination request is confirmed by the opposite party - * @param cpAddress the party which confirms the trade termination + * @param confirmer the party which confirms the trade termination * @param tradeId the trade identifier which is supposed to be terminated + * @param terminationPayment an agreed termination amount (viewed from the confirmer, negative of the value provided by the requester) * @param terminationTerms termination terms */ - event TradeTerminationConfirmed(address cpAddress, string tradeId, int256 terminationPayment, string terminationTerms); + event TradeTerminationConfirmed(address confirmer, string tradeId, int256 terminationPayment, string terminationTerms); /** * @dev Emitted when a counterparty cancels its requests an early termination of the underlying trade - * @param cpAddress the address of the requesting party + * @param initiator the address of the requesting party * @param tradeId the trade identifier which is supposed to be terminated * @param terminationTerms termination terms */ - event TradeTerminationCanceled(address cpAddress, string tradeId, string terminationTerms); + event TradeTerminationCanceled(address initiator, string tradeId, string terminationTerms); /*------------------------------------------- FUNCTIONALITY ---------------------------------------------------------------------------------------*/ @@ -174,8 +176,9 @@ interface ISDC { * @param position is the position the inceptor has in that trade * @param paymentAmount is the payment amount which can be positive or negative (viewed from the inceptor) * @param initialSettlementData the initial settlement data (e.g. initial market data at which trade was incepted) + * @return the tradeId uniquely determining this trade. */ - function inceptTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external; + function inceptTrade(address withParty, string memory tradeData, int position, int256 paymentAmount, string memory initialSettlementData) external returns (string memory); /** * @notice Performs a matching of provided trade data and settlement data of a previous trade inception @@ -229,26 +232,27 @@ interface ISDC { /** * @notice Called from a counterparty to request a mutual termination * @dev emits a {TradeTerminationRequest} - * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param tradeId the trade identifier which is supposed to be terminated * @param terminationPayment an agreed termination amount (viewed from the requester) * @param terminationTerms the termination terms to be stored on chain. */ - function requestTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; + function requestTradeTermination(string memory tradeId, int256 terminationPayment, string memory terminationTerms) external; /** * @notice Called from a party to confirm an incepted termination, which might trigger a final settlement before trade gets closed * @dev emits a {TradeTerminationConfirmed} - * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param tradeId the trade identifier which is supposed to be terminated * @param terminationPayment an agreed termination amount (viewed from the confirmer, negative of the value provided by the requester) * @param terminationTerms the termination terms to be stored on chain. */ - function confirmTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; + function confirmTradeTermination(string memory tradeId, int256 terminationPayment, string memory terminationTerms) external; /** * @notice Called from a party to confirm an incepted termination, which might trigger a final settlement before trade gets closed * @dev emits a {TradeTerminationCanceled} - * @param tradeData a description of the trade specification e.g. in xml format, suggested structure - see assets/eip-6123/doc/sample-tradedata-filestructure.xml + * @param tradeId the trade identifier which is supposed to be terminated + * @param terminationPayment an agreed termination amount (viewed from the requester) * @param terminationTerms the termination terms */ - function cancelTradeTermination(string memory tradeData, int256 terminationPayment, string memory terminationTerms) external; + function cancelTradeTermination(string memory tradeId, int256 terminationPayment, string memory terminationTerms) external; } diff --git a/assets/erc-6123/contracts/SDCSingleTrade.sol b/assets/erc-6123/contracts/SDCSingleTrade.sol index c6dc890ae3..6c66929650 100644 --- a/assets/erc-6123/contracts/SDCSingleTrade.sol +++ b/assets/erc-6123/contracts/SDCSingleTrade.sol @@ -133,7 +133,7 @@ abstract contract SDCSingleTrade is ISDC { * emits a TradeIncepted * can be called only when TradeState = Incepted */ - function inceptTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external override onlyCounterparty onlyWhenTradeInactive { + function inceptTrade(address _withParty, string memory _tradeData, int _position, int256 _paymentAmount, string memory _initialSettlementData) external override onlyCounterparty onlyWhenTradeInactive returns (string memory) { require(msg.sender != _withParty, "Calling party cannot be the same as withParty"); require(_position == 1 || _position == -1, "Position can only be +1 or -1"); tradeState = TradeState.Incepted; // Set TradeState to Incepted @@ -144,6 +144,7 @@ abstract contract SDCSingleTrade is ISDC { tradeID = Strings.toString(transactionHash); tradeData = _tradeData; // Set trade data to enable querying already in inception state emit TradeIncepted(msg.sender, _withParty, tradeID, _tradeData, _position, _paymentAmount, _initialSettlementData); + return tradeID; } /* @@ -189,8 +190,7 @@ abstract contract SDCSingleTrade is ISDC { require(keccak256(abi.encodePacked(tradeID)) == keccak256(abi.encodePacked(_tradeId)), "Trade ID mismatch"); uint256 hash = uint256(keccak256(abi.encode(_tradeId, "terminate", _terminationPayment, terminationTerms))); pendingRequests[hash] = msg.sender; - terminationPayment = _terminationPayment; // termination payment will be provided in view of receiving party - emit TradeTerminationRequest(msg.sender, _tradeId, terminationPayment, terminationTerms); + emit TradeTerminationRequest(msg.sender, _tradeId, _terminationPayment, terminationTerms); } /* @@ -203,7 +203,10 @@ abstract contract SDCSingleTrade is ISDC { uint256 hashConfirm = uint256(keccak256(abi.encode(_tradeId, "terminate", -_terminationPayment, terminationTerms))); require(pendingRequests[hashConfirm] == pendingRequestParty, "Confirmation of termination failed due to wrong party or missing request"); delete pendingRequests[hashConfirm]; - emit TradeTerminationConfirmed(msg.sender, _tradeId, terminationPayment, terminationTerms); + + terminationPayment = msg.sender == receivingParty ? _terminationPayment : -_terminationPayment; // termination payment will be provided in view of receiving party + + emit TradeTerminationConfirmed(msg.sender, _tradeId, _terminationPayment, terminationTerms); /* Trigger Termination Payment Amount */ address payerAddress = terminationPayment > 0 ? otherParty(receivingParty) : receivingParty; uint256 absPaymentAmount = uint256(abs(_terminationPayment)); diff --git a/assets/erc-6123/package.json b/assets/erc-6123/package.json index 19e85871df..2286d3af93 100644 --- a/assets/erc-6123/package.json +++ b/assets/erc-6123/package.json @@ -1,6 +1,6 @@ { "name": "@finmath.net/sdc", - "version": "0.4.0", + "version": "0.4.2", "description": "Solidity Smart Derivative Contracts", "author": "Christian Fries, Peter Kohl-Landgraf, Alexandros Korpis", "license": "ISC", diff --git a/assets/erc-6123/test/SDCTests.js b/assets/erc-6123/test/SDCTests.js index 6351c12b47..22e39646d8 100644 --- a/assets/erc-6123/test/SDCTests.js +++ b/assets/erc-6123/test/SDCTests.js @@ -208,34 +208,42 @@ describe("Livecycle Unit-Tests for SDC Plege Balance", () => { await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + // Note: position = 1 => counterparty1 is the receivingParty const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, 0, "initialMarketData"); const receipt = await incept_call.wait(); const event = receipt.events.find(event => event.event === 'TradeIncepted'); const trade_id = event.args[2]; const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, 0, "initialMarketData"); + + // Note: terminationPayment is considered to be viewed from the requester here. const terminate_call = await sdc.connect(counterparty2).requestTradeTermination(trade_id, -terminationPayment, "terminationTerms"); + // Note: terminationPayment is considered to be viewed from the confirmer here. const confirm_terminate_call = await sdc.connect(counterparty1).confirmTradeTermination(trade_id, +terminationPayment, "terminationTerms"); + let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); - await expect(cp1_balance).equal(initialLiquidityBalance-terminationPayment); - await expect(cp2_balance).equal(initialLiquidityBalance+terminationPayment); + await expect(cp1_balance).equal(initialLiquidityBalance+terminationPayment); + await expect(cp2_balance).equal(initialLiquidityBalance-terminationPayment); }); + it("9b. CP1 is Receiving Party, Trade-Termination is incepted by CP1 which pays the termination payment to CP2", async () => { let token = await ERC20Factory.deploy(); await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); let sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,token.address,marginBufferAmount,terminationFee); + // Note: position = 1 => counterparty1 is the receivingParty const incept_call = await sdc.connect(counterparty1).inceptTrade(counterparty2.address, trade_data, 1, 0, "initialMarketData"); const receipt = await incept_call.wait(); const event = receipt.events.find(event => event.event === 'TradeIncepted'); const trade_id = event.args[2]; const confirm_call = await sdc.connect(counterparty2).confirmTrade(counterparty1.address, trade_data, -1, 0, "initialMarketData"); - const terminate_call = await sdc.connect(counterparty1).requestTradeTermination(trade_id, -terminationPayment, "terminationTerms"); - const confirm_terminate_call = await sdc.connect(counterparty2).confirmTradeTermination(trade_id, +terminationPayment, "terminationTerms"); + // Note: terminationPayment is considered to be viewed from the requester here. + const terminate_call = await sdc.connect(counterparty1).requestTradeTermination(trade_id, terminationPayment, "terminationTerms"); + const confirm_terminate_call = await sdc.connect(counterparty2).confirmTradeTermination(trade_id, -terminationPayment, "terminationTerms"); let cp1_balance = await token.connect(counterparty1).balanceOf(counterparty1.address); let cp2_balance = await token.connect(counterparty1).balanceOf(counterparty2.address); - await expect(cp1_balance).equal(initialLiquidityBalance-terminationPayment); - await expect(cp2_balance).equal(initialLiquidityBalance+terminationPayment); + await expect(cp1_balance).equal(initialLiquidityBalance+terminationPayment); + await expect(cp2_balance).equal(initialLiquidityBalance-terminationPayment); }); it("10. Successful Inception with Upfront transferred from CP2 to CP1 + successful settlement transferred from CP1 to CP2", async () => { From 85eb202a8c05bc373f0583cb9eaff9237ff69b65 Mon Sep 17 00:00:00 2001 From: Christian Fries Date: Thu, 8 Aug 2024 13:26:13 +0200 Subject: [PATCH 092/126] Update ERC-6123: Minor fix to headlines (added subsection). Merged by EIP-Bot. --- ERCS/erc-6123.md | 56 +++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/ERCS/erc-6123.md b/ERCS/erc-6123.md index 0c9332eab3..e58ec6f2e1 100644 --- a/ERCS/erc-6123.md +++ b/ERCS/erc-6123.md @@ -117,7 +117,6 @@ The transactionData is emitted as part of the corresponding event: `TradeSettled function afterTransfer(bool success, string memory transactionData) external; ``` - #### Trade Termination: `requestTermination` Allows an eligible party to request a mutual termination of the trade with the correspondig `tradeId` with a termination amount she is willing to pay and provide further termination terms (e.g. an XML) @@ -142,6 +141,7 @@ The party that initiated `requestTradeTermination` has the option to withdraw th function cancelTradeTermination(string memory tradeId, int256 terminationPayment, string memory terminationTerms) external; ``` + ### Trade Events The following events are emitted during an SDC Trade life-cycle. @@ -178,71 +178,73 @@ Emitted when a Trade is activated event TradeActivated(string tradeId); ``` +#### TradeTerminationRequest -### SettlementRequested - -Emitted when a settlement is requested. May trigger the settlement phase. +Emitted when termination request is initiated by a counterparty ```solidity -event SettlementRequested(address initiator, string tradeData, string lastSettlementData); +event TradeTerminationRequest(address initiator, string tradeId, int256 terminationPayment, string terminationTerms); ``` -### SettlementEvaluated +#### TradeTerminationConfirmed -Emitted when the settlement phase is started. +Emitted when termination request is confirmed by a counterparty ```solidity -event SettlementEvaluated(address initiator, int256 settlementAmount, string settlementData); +event TradeTerminationConfirmed(address confirmer, string tradeId, int256 terminationPayment, string terminationTerms); ``` -### SettlementTransferred +#### TradeTerminationCanceled -Emitted when the settlement succeeded. +Emitted when termination request is canceled by the requesting counterparty ```solidity -event SettlementTransferred(string transactionData); +event TradeTerminationCanceled(address initiator, string tradeId, string terminationTerms); ``` +#### TradeTerminated -### SettlementFailed - -Emitted when the settlement failed. +Emitted when trade is terminated ```solidity -event SettlementFailed(string transactionData); +event TradeTerminated(string cause); ``` -#### TradeTerminationRequest +### Settlement Events -Emitted when termination request is initiated by a counterparty +The following events are emitted during the settlement phases. + +#### SettlementRequested + +Emitted when a settlement is requested. May trigger the settlement phase. ```solidity -event TradeTerminationRequest(address initiator, string tradeId, int256 terminationPayment, string terminationTerms); +event SettlementRequested(address initiator, string tradeData, string lastSettlementData); ``` -#### TradeTerminationConfirmed +#### SettlementEvaluated -Emitted when termination request is confirmed by a counterparty +Emitted when the settlement phase is started. ```solidity -event TradeTerminationConfirmed(address confirmer, string tradeId, int256 terminationPayment, string terminationTerms); +event SettlementEvaluated(address initiator, int256 settlementAmount, string settlementData); ``` -#### TradeTerminationCanceled +#### SettlementTransferred -Emitted when termination request is canceled by the requesting counterparty +Emitted when the settlement succeeded. ```solidity -event TradeTerminationCanceled(address initiator, string tradeId, string terminationTerms); +event SettlementTransferred(string transactionData); ``` -#### TradeTerminated +#### SettlementFailed -Emitted when trade is terminated +Emitted when the settlement failed. ```solidity -event TradeTerminated(string cause); +event SettlementFailed(string transactionData); ``` From d78b3128fba0a7ccb823ce413bdcd885e2c4ed8b Mon Sep 17 00:00:00 2001 From: Christian Fries Date: Thu, 8 Aug 2024 14:49:17 +0200 Subject: [PATCH 093/126] Update ERC-6123: Update Diagrams Merged by EIP-Bot. --- ERCS/erc-6123.md | 6 + assets/erc-6123/doc/sdc_trade_states.svg | 257 +++++++++++------------ assets/erc-6123/doc/sequence.puml | 35 +-- assets/erc-6123/doc/sequence.svg | 2 +- 4 files changed, 153 insertions(+), 147 deletions(-) diff --git a/ERCS/erc-6123.md b/ERCS/erc-6123.md index e58ec6f2e1..eeedbc2bb2 100644 --- a/ERCS/erc-6123.md +++ b/ERCS/erc-6123.md @@ -264,9 +264,15 @@ The interface design and reference implementation are based on the following con ![image info](../assets/eip-6123/doc/sdc_trade_states.svg) +The diagram shows the trade states of a single trade SDC as in `SDCSingleTrade.sol`. + ### Sequence diagram of reference implementation 'SDCPledgedBalance.sol' + ![image info](../assets/eip-6123/doc/sequence.svg) +The sequence diagram show the function calls that create the trade and stellement state transitions +and the emitted events. + ## Test Cases Life-cycle unit tests based on the sample implementation and usage of [ERC-20](./eip-20.md) token is provided. See file [test/SDCTests.js](../assets/eip-6123/test/SDCTests.js) diff --git a/assets/erc-6123/doc/sdc_trade_states.svg b/assets/erc-6123/doc/sdc_trade_states.svg index fb99c78fc4..31219ae7c5 100644 --- a/assets/erc-6123/doc/sdc_trade_states.svg +++ b/assets/erc-6123/doc/sdc_trade_states.svg @@ -1,9 +1,9 @@ - + + xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="11.6929in" height="8.26772in" + viewBox="0 0 841.89 595.276" xml:space="preserve" color-interpolation-filters="sRGB" class="st22"> @@ -19,22 +19,21 @@ .st5 {fill:#316176} .st6 {stroke:#316176;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} .st7 {fill:#ffffff;font-family:Calibri;font-size:1.00001em} - .st8 {fill:#aecedd;stroke:#85b6cc;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5;visibility:hidden} - .st9 {fill:#ffffff;stroke:#85b6cc;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} - .st10 {fill:#aecedd} - .st11 {stroke:#85b6cc;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} - .st12 {marker-end:url(#mrkr4-49);stroke:#9b9b9b;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} - .st13 {fill:#9b9b9b;fill-opacity:1;stroke:#9b9b9b;stroke-opacity:1;stroke-width:0.16556291390728} - .st14 {fill:#ffffff;stroke:none;stroke-linecap:butt;stroke-width:7.2} - .st15 {fill:#595959;font-family:Calibri;font-size:0.666664em} - .st16 {font-size:1em} - .st17 {fill:#316176;stroke:#316176;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} - .st18 {fill:#ffffff;stroke:none;stroke-linecap:butt} - .st19 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} - .st20 {marker-end:url(#mrkr4-49);stroke:#9b9b9b;stroke-dasharray:3.5,2.5;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} - .st21 {fill:none} - .st22 {stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} - .st23 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3} + .st8 {fill:#ffffff;font-family:Calibri;font-size:0.833336em} + .st9 {fill:#aecedd;stroke:#85b6cc;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5;visibility:hidden} + .st10 {fill:#ffffff;stroke:#85b6cc;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} + .st11 {fill:#aecedd} + .st12 {stroke:#85b6cc;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} + .st13 {marker-end:url(#mrkr4-49);stroke:#9b9b9b;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} + .st14 {fill:#9b9b9b;fill-opacity:1;stroke:#9b9b9b;stroke-opacity:1;stroke-width:0.16556291390728} + .st15 {fill:#ffffff;stroke:none;stroke-linecap:butt;stroke-width:7.2} + .st16 {fill:#595959;font-family:Calibri;font-size:0.666664em} + .st17 {font-size:1em} + .st18 {fill:#316176;stroke:#316176;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} + .st19 {fill:#ffffff;stroke:none;stroke-linecap:butt} + .st20 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} + .st21 {marker-end:url(#mrkr4-49);stroke:#9b9b9b;stroke-dasharray:3.5,2.5;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} + .st22 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3} ]]> @@ -42,7 +41,7 @@ - @@ -53,20 +52,20 @@ Zeichenblatt-1 + v:shadowOffsetY="-8.50394"/> - + Anfangszustand - Start + Inactive - + - Start - + Inactive + @@ -100,11 +99,11 @@ + class="st6"/> Incepted
- + @@ -138,11 +137,11 @@ + class="st6"/> Confirmed - + @@ -153,34 +152,34 @@ Zusammengesetzter Zustand.408 - + - + Sheet.409 - Terminated - + InTermination + Sheet.410 - + - - + - - Terminated + + InTermination - + @@ -191,7 +190,7 @@ Zusammengesetzter Zustand.419 - + @@ -206,51 +205,51 @@ - + + L35.43 595.28 Z" class="st11"/> + class="st12"/> Cancelled - + Dynamischer Verbinder.304 tx: inceptTrade by Couterparty1 - - - tx: inceptTradeby Couterparty1 - + + + tx: inceptTradeby Couterparty1 + Dynamischer Verbinder.313 tx: confirmTrade by Counterparty2 - - - tx: confirmTradeby Counterparty2 - + + + tx: confirmTradeby Counterparty2 + Dynamischer Verbinder.66 tx: afterTransfer - - - tx: afterTransfer - + + + tx: afterTransfer + Zustand.71 - + Sheet.354 @@ -260,23 +259,23 @@ - + Valuation - + Auswahl.292 - + - + Zustand.336 - + Sheet.360 @@ -286,16 +285,16 @@ - + Settled - + Zustand.380 - + Sheet.381 @@ -305,64 +304,65 @@ - + inTransfer - + Dynamischer Verbinder.398 success - - - success - + + + success + Dynamischer Verbinder.399 tx: initiateSettlement - - - - tx: initiateSettlement - + + + + tx: initiateSettlement + Dynamischer Verbinder.401 Initiate Transfer of Margin Buffers to SDC Balance - - - Initiate Transfer of Margin Buffers to SDC Balance - + + + Initiate Transfer of Margin Buffers to SDC Balance + Dynamischer Verbinder tx: perfomSettlement Initiate Transfer of Settlement Amount - - - tx: perfomSettlementInitiate Transfer ofSettlement Amount - + + + tx: perfomSettlementInitiate Transfer ofSettlement Amount + Dynamischer Verbinder.411 fail - - - - fail - + + + + fail + Dynamischer Verbinder.412 - Transfer Open Settlement Amount from SDC Balance + Final Transfer either from Partyโ€˜s or SDC Balance - - - - Transfer Open Settlement Amount from SDC Balance - + + + + Final Transfereither from Partyโ€˜sor SDC Balance + Sheet.413 - + - + - + Endzustand - End + Terminated - + - End - + Terminated + Auswahl.415 - + - + Dynamischer Verbinder.417 - + - + Dynamischer Verbinder.418 - Mutuall terminated or Maturity + Mutual Termination - - - - Mutuall terminated or Maturity - + + + + Mutual Termination + Dynamischer Verbinder.422 OPT: cancelInception - - - OPT: cancelInception - - Dynamischer Verbinder.430 - fail - - - - - - fail + + + OPT: cancelInception diff --git a/assets/erc-6123/doc/sequence.puml b/assets/erc-6123/doc/sequence.puml index ea04005b0e..a240a31445 100644 --- a/assets/erc-6123/doc/sequence.puml +++ b/assets/erc-6123/doc/sequence.puml @@ -9,12 +9,9 @@ title SmartDerivativeContract with Settlement-Token and off-chain Valuation Serv participant SettlementToken - participant EventHandler participant ValuationService - - activate EventHandler activate SettlementToken activate ValuationService @@ -31,17 +28,23 @@ CP1 ->SDC: tx 'inceptTrade' SDC-->EventHandler: emit TradeIncepted == TradeState 'Incepted' == -CP2->SDC: tx 'confirmTrade' -SDC->SDC: validate tradedata -SDC-->EventHandler: emit TradeConfirmed + CP2->SDC: tx 'confirmTrade' + SDC->SDC: validate tradedata + + SDC-->EventHandler: emit TradeConfirmed == TradeState 'Confirmed' == -SDC ->SettlementToken: tx 'transferFrom' margin buffers and termination fees\nto SDC address for CP1 and CP2 -SDC->SettlementToken: tx 'transferFrom' optional Upfront Fee from Paying to Receiving Party + SDC -> SettlementToken: tx 'transferFrom' margin buffers and termination fees\nto SDC address for CP1 and CP2 + SDC -> SettlementToken: tx 'transferFrom' optional Upfront Fee from Paying to Receiving Party + == TradeState 'inTransfer' == -SettlementToken->SDC: callback tx 'afterSettlement' + + SettlementToken->SDC: callback tx 'afterTransfer' + + SDC-->EventHandler: emit TradeActivated + ==ProcessState 'Settled' == end @@ -52,25 +55,31 @@ loop Every Settlement CP1->SDC: tx: 'initiateSettlement' == TradeState 'Valuation' == -SDC-->EventHandler:emit ProcessSettlementRequest +SDC-->EventHandler:emit SettlementRequested EventHandler->ValuationService: request valuation data ValuationService->EventHandler: return valuation data EventHandler->SDC: callback: tx 'performSettlement' SDC->SDC:Caps Settlement Amount at Margin Buffer Level + +SDC-->EventHandler: emit SettlementEvaluated + SDC->SettlementToken: tx 'transferFrom' settlement amount from Paying Party to Receiving Party Balance == TradeState 'inTransfer' == -SDC-->EventHandler: emit AwaitingTransfer alt Transfer Check - SettlementToken->SDC: callback tx 'afterSettlement' + SettlementToken->SDC: callback tx 'afterTransfer' else success + SDC-->EventHandler: emit SettlementTransferred == TradeState 'Settled' == else fail + SDC-->EventHandler: emit SettlementFailed + SDC->SettlementToken: tx 'transfer' Settlement Amount from SDC Balance to Receiving Party SDC->SettlementToken: tx 'transfer' Termination Fee from SDC Balance to Receiving Party - SDC->SettlementToken: tx 'transfer' - Release remainigBalances to parties + SDC->SettlementToken: tx 'transfer' - Release remainigBalances to parties + SDC-->EventHandler: emit TradeTerminated == TradeState 'Terminated' == end diff --git a/assets/erc-6123/doc/sequence.svg b/assets/erc-6123/doc/sequence.svg index f7e877a984..99362b5977 100644 --- a/assets/erc-6123/doc/sequence.svg +++ b/assets/erc-6123/doc/sequence.svg @@ -1 +1 @@ -SmartDerivativeContract with Settlement-Token and off-chain Valuation ServiceCP1CP1CP2CP2SDCSDCSettlementTokenSettlementTokenEventHandlerEventHandlerValuationServiceValuationServiceInitialize Tradeallocate balancesallocate balancestx 'deploy' a SDC with token addresstx set 'allowance' for SDC addresstx set 'allowance' for SDC addresstx 'inceptTrade'emit TradeInceptedTradeState 'Incepted'tx 'confirmTrade'validate tradedataemit TradeConfirmedTradeState 'Confirmed'tx 'transferFrom' margin buffers and termination feesto SDC address for CP1 and CP2tx 'transferFrom' optional Upfront Fee from Paying to Receiving PartyTradeState 'inTransfer'callback tx 'afterSettlement'ProcessState 'Settled'loop[Every Settlement]tx: 'initiateSettlement'TradeState 'Valuation'emit ProcessSettlementRequestrequest valuation datareturn valuation datacallback: tx 'performSettlement'Caps Settlement Amount at Margin Buffer Leveltx 'transferFrom' settlement amount from Paying Party to Receiving Party BalanceTradeState 'inTransfer'emit AwaitingTransferalt[Transfer Check]callback tx 'afterSettlement'[success]TradeState 'Settled'[fail]tx 'transfer' Settlement Amount from SDC Balance to Receiving Partytx 'transfer' Termination Fee from SDC Balance to Receiving Partytx 'approve' - Unlock remaing Party BalancesTradeState 'Terminated' \ No newline at end of file +SmartDerivativeContract with Settlement-Token and off-chain Valuation ServiceCP1CP1CP2CP2SDCSDCSettlementTokenSettlementTokenEventHandlerEventHandlerValuationServiceValuationServiceInitialize Tradeallocate balancesallocate balancestx 'deploy' a SDC with token addresstx 'inceptTrade'emit TradeInceptedTradeState 'Incepted'tx 'confirmTrade'validate tradedataemit TradeConfirmedTradeState 'Confirmed'tx 'transferFrom' margin buffers and termination feesto SDC address for CP1 and CP2tx 'transferFrom' optional Upfront Fee from Paying to Receiving PartyTradeState 'inTransfer'callback tx 'afterTransfer'emit TradeActivatedProcessState 'Settled'loop[Every Settlement]tx: 'initiateSettlement'TradeState 'Valuation'emit SettlementRequestedrequest valuation datareturn valuation datacallback: tx 'performSettlement'Caps Settlement Amount at Margin Buffer Levelemit SettlementEvaluatedtx 'transferFrom' settlement amount from Paying Party to Receiving Party BalanceTradeState 'inTransfer'alt[Transfer Check]callback tx 'afterTransfer'[success]emit SettlementTransferredTradeState 'Settled'[fail]emit SettlementFailedtx 'transfer' Settlement Amount from SDC Balance to Receiving Partytx 'transfer' Termination Fee from SDC Balance to Receiving Partytx 'transfer' - Release remainigBalances to partiesemit TradeTerminatedTradeState 'Terminated' \ No newline at end of file From 5c95af494e667ba35102c1b3093c5d966f5c2264 Mon Sep 17 00:00:00 2001 From: Ko Fujimura Date: Sun, 11 Aug 2024 08:35:27 +0900 Subject: [PATCH 094/126] Update ERC-7303: Update erc-7303.md Merged by EIP-Bot. --- ERCS/erc-7303.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7303.md b/ERCS/erc-7303.md index ab2a23c5cb..109800737d 100644 --- a/ERCS/erc-7303.md +++ b/ERCS/erc-7303.md @@ -154,7 +154,7 @@ contract MyToken is ERC721, ERC7303 { _grantRoleByERC1155(BURNER_ROLE, 0x..., ...); } - function safeMint(address to, uint256 tokenId, string memory uri) + function safeMint(address to, uint256 tokenId) public onlyHasToken(MINTER_ROLE, msg.sender) { _safeMint(to, tokenId); From f9fdd106b92e5e3d4b7f8d8afbb8261fb61c5c1b Mon Sep 17 00:00:00 2001 From: Konrad Date: Tue, 13 Aug 2024 12:50:30 +0200 Subject: [PATCH 095/126] Update ERC-7579: specify fallback authorization control Merged by EIP-Bot. --- ERCS/erc-7579.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ERCS/erc-7579.md b/ERCS/erc-7579.md index 199130bf71..02a467b8f6 100644 --- a/ERCS/erc-7579.md +++ b/ERCS/erc-7579.md @@ -54,8 +54,8 @@ To comply with this standard, smart accounts MUST implement the execution interf interface IExecution { /** * @dev Executes a transaction on behalf of the account. - * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details - * @param executionCalldata The encoded execution call data + * @param mode The encoded execution mode of the transaction. + * @param executionCalldata The encoded execution call data. * * MUST ensure adequate authorization control: e.g. onlyEntryPointOrSelf if used with ERC-4337 * If a mode is requested that is not supported by the Account, it MUST revert @@ -65,8 +65,8 @@ interface IExecution { /** * @dev Executes a transaction on behalf of the account. * This function is intended to be called by Executor Modules - * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details - * @param executionCalldata The encoded execution call data + * @param mode The encoded execution mode of the transaction. + * @param executionCalldata The encoded execution call data. * * MUST ensure adequate authorization control: i.e. onlyExecutorModule * If a mode is requested that is not supported by the Account, it MUST revert @@ -218,10 +218,10 @@ Smart accounts MAY implement a fallback function that forwards the call to a fal If the smart account has a fallback handler installed, it: -- MUST implement authorization control -- MUST use `call` to invoke the fallback handler +- MUST use `call` or `staticcall` to invoke the fallback handler - MUST utilize [ERC-2771](./eip-2771.md) to add the original `msg.sender` to the `calldata` sent to the fallback handler - MUST route to fallback handlers based on the function selector of the calldata +- MAY implement authorization control, which SHOULD be done via hooks #### ERC-165 @@ -305,7 +305,7 @@ Executors MUST implement the `IModule` interface and have module type id: `2`. Fallback handlers MUST implement the `IModule` interface and have module type id: `3`. -Fallback handlers that implement authorization control, MUST NOT rely on `msg.sender` for authorization control but MUST use ERC-2771 `_msgSender()` instead. +Fallback handlers MAY implement authorization control. Fallback handlers that do implement authorization control, MUST NOT rely on `msg.sender` for authorization control but MUST use ERC-2771 `_msgSender()` instead. #### Hooks From b7b790300906f1f19499504f8798ce32bb2235bf Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Thu, 15 Aug 2024 01:30:36 -0400 Subject: [PATCH 096/126] Update ERC-7677: Move to Review Merged by EIP-Bot. --- ERCS/erc-7677.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7677.md b/ERCS/erc-7677.md index 528f0e279d..8f49665cb6 100644 --- a/ERCS/erc-7677.md +++ b/ERCS/erc-7677.md @@ -4,7 +4,7 @@ title: Paymaster Web Service Capability description: A way for apps to communicate with smart wallets about paymaster web services author: Lukas Rosario (@lukasrosario), Dror Tirosh (@drortirosh), Wilson Cusack (@wilsoncusack), Kristof Gazso (@kristofgazso), Hazim Jumali (@hazim-j) discussions-to: https://ethereum-magicians.org/t/erc-7677-paymaster-web-service-capability/19530 -status: Draft +status: Review type: Standards Track category: ERC created: 2024-04-03 From 9f7e122cf208f8cd21421dd038a72a1e7aa096e7 Mon Sep 17 00:00:00 2001 From: Dror Tirosh Date: Thu, 15 Aug 2024 14:52:59 +0300 Subject: [PATCH 097/126] Update ERC-4337: erc4337 updates Merged by EIP-Bot. --- ERCS/erc-4337.md | 93 ++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/ERCS/erc-4337.md b/ERCS/erc-4337.md index e4a26772cb..49aadfa97b 100644 --- a/ERCS/erc-4337.md +++ b/ERCS/erc-4337.md @@ -76,14 +76,14 @@ To avoid Ethereum consensus changes, we do not attempt to create new transaction | `paymasterData` | `bytes` | Data for paymaster (only if paymaster exists) | | `signature` | `bytes` | Data passed into the account to verify authorization | -Users send `UserOperation` objects to a dedicated user operation mempool. The are not concerned with the packed version. +Users send `UserOperation` objects to a dedicated user operation mempool. They are not concerned with the packed version. A specialized class of actors called **bundlers** (either block builders running special-purpose code, or users that can relay transactions to block builders eg. through a bundle marketplace such as Flashbots that can guarantee next-block-or-never inclusion) listen in on the user operation mempool, and create **bundle transactions**. A bundle transaction packages up multiple `UserOperation` objects into a single `handleOps` call to a pre-published global **entry point contract**. To prevent replay attacks (both cross-chain and multiple `EntryPoint` implementations), the `signature` should depend on `chainid` and the `EntryPoint` address. ### EntryPoint definition -When passed to on-chain contacts (the EntryPoint contract, and then to account and paymaster), a packed version of the above structure is used: +When passed to on-chain contacts (the EntryPoint contract, and then to the account and paymaster), a packed version of the above structure is used: | Field | Type | Description | |----------------------|-----------|------------------------------------------------------------------------| @@ -132,12 +132,12 @@ The `userOpHash` is a hash over the userOp (except signature), entryPoint and ch The account: * MUST validate the caller is a trusted EntryPoint -* If the account does not support signature aggregation, it MUST validate the signature is a valid signature of the `userOpHash`, and +* If the account does not support signature aggregation, it MUST validate that the signature is a valid signature of the `userOpHash`, and SHOULD return SIG_VALIDATION_FAILED (and not revert) on signature mismatch. Any other error MUST revert. -* MUST pay the entryPoint (caller) at least the "missingAccountFunds" (which might be zero, in case current account's deposit is high enough) +* MUST pay the entryPoint (caller) at least the "missingAccountFunds" (which might be zero, in case the current account's deposit is high enough) * The account MAY pay more than this minimum, to cover future transactions (it can always issue `withdrawTo` to retrieve it) * The return value MUST be packed of `authorizer`, `validUntil` and `validAfter` timestamps. - * authorizer - 0 for valid signature, 1 to mark signature failure. Otherwise, an address of an authorizer contract. This ERC defines "signature aggregator" as authorizer. + * authorizer - 0 for valid signature, 1 to mark signature failure. Otherwise, an address of an authorizer contract. This ERC defines a "signature aggregator" as an authorizer. * `validUntil` is 6-byte timestamp value, or zero for "infinite". The UserOp is valid only up to this time. * `validAfter` is 6-byte timestamp. The UserOp is valid only after this time. @@ -208,7 +208,7 @@ this bundler is supposed to track the `key` and `sequence` pair of the UserOpera In some cases, an account may need to have an "administrative" channel of operations running in parallel to normal operations. - In this case, the account may use specific `key` when calling methods on the account itself: + In this case, the account may use a specific `key` when calling methods on the account itself: ```solidity bytes4 sig = bytes4(userOp.callData[0 : 4]); @@ -224,7 +224,7 @@ this bundler is supposed to track the `key` and `sequence` pair of the UserOpera There are 2 separate entry point methods: `handleOps` and `handleAggregatedOps` -* `handleOps` handle userOps of accounts that don't require any signature aggregator. +* `handleOps` handles userOps of accounts that don't require any signature aggregator. * `handleAggregatedOps` can handle a batch that contains userOps of multiple aggregators (and also requests without any aggregator) * `handleAggregatedOps` performs the same logic below as `handleOps`, but it must transfer the correct aggregator to each userOp, and also must call `validateSignatures` on each aggregator before doing all the per-account validation. The entry point's `handleOps` function must perform the following steps (we first describe the simpler non-paymaster case). It must make two loops, the **verification loop** and the **execution loop**. In the verification loop, the `handleOps` call must perform the following steps for each `UserOperation`: @@ -279,7 +279,7 @@ enum PostOpMode { } ``` -The EntryPoint must implement the following API to let entities like paymasters to have a stake, and thus have more flexibility in their storage access (see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details.) +The EntryPoint must implement the following API to let entities like paymasters have a stake, and thus have more flexibility in their storage access (see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details.) ```solidity // add a stake to the calling entity @@ -295,7 +295,7 @@ function withdrawStake(address payable withdrawAddress) external The paymaster must also have a deposit, which the entry point will charge UserOperation costs from. The deposit (for paying gas fees) is separate from the stake (which is locked). -The EntryPoint must implement the following interface to allow paymasters (and optionally accounts) manage their deposit: +The EntryPoint must implement the following interface to allow paymasters (and optionally accounts) to manage their deposit: ```c++ // return the deposit of an account @@ -313,9 +313,9 @@ function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) ext When a client receives a `UserOperation`, it must first run some basic sanity checks, namely that: * Either the `sender` is an existing contract, or the `initCode` is not empty (but not both) -* If `initCode` is not empty, parse its first 20 bytes as a factory address. Record whether the factory is staked, in case the later simulation indicates that it needs to be. If the factory accesses global state, it must be staked - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. +* If `initCode` is not empty, parse its first 20 bytes as a factory address. Record whether the factory is staked, in case the later simulation indicates that it needs to be. If the factory accesses the global state, it must be staked - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. * The `verificationGasLimit` is sufficiently low (`<= MAX_VERIFICATION_GAS`) and the `preVerificationGas` is sufficiently high (enough to pay for the calldata gas cost of serializing the `UserOperation` plus `PRE_VERIFICATION_OVERHEAD_GAS`) -* The `paymasterAndData` is either empty, or start with the **paymaster** address, which is a contract that (i) currently has nonempty code on chain, (ii) has a sufficient deposit to pay for the UserOperation, and (iii) is not currently banned. During simulation, the paymaster's stake is also checked, depending on its storage usage - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. +* The `paymasterAndData` is either empty, or starts with the **paymaster** address, which is a contract that (i) currently has nonempty code on chain, (ii) has a sufficient deposit to pay for the UserOperation, and (iii) is not currently banned. During simulation, the paymaster's stake is also checked, depending on its storage usage - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. * The callgas is at least the cost of a `CALL` with non-zero value. * The `maxFeePerGas` and `maxPriorityFeePerGas` are above a configurable minimum value that the client is willing to accept. At the minimum, they are sufficiently high to be included with the current `block.basefee`. * The sender doesn't have another `UserOperation` already present in the pool (or it replaces an existing entry with the same sender and nonce, with a higher `maxPriorityFeePerGas` and an equally increased `maxFeePerGas`). Only one `UserOperation` per sender may be included in a single batch. A sender is exempt from this rule and may have multiple `UserOperations` in the pool and in a batch if it is staked (see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) below), but this exception is of limited use to normal accounts. @@ -340,12 +340,12 @@ interface IAggregator { * An account signifies it uses signature aggregation returning its address from `validateUserOp`. * During `simulateValidation`, this aggregator is returned to the bundler as part of the `aggregatorInfo` struct. -* The bundler should first accept the aggregator (aggregators must be staked. bundler should verify it is not throttle/banned) +* The bundler should first accept the aggregator (aggregators must be staked. bundler should verify it is not throttled/banned) * To accept the UserOp, the bundler must call **validateUserOpSignature()** to validate the userOp's signature. This method returned an alternate signature (usually empty) that should be used during bundling. * The bundler MUST call `validateUserOp` a second time on the account with the UserOperation using that returned signature, and make sure it returns the same value. * **aggregateSignatures()** must aggregate all UserOp signatures into a single value. -* Note that the above methods are helper method for the bundler. The bundler MAY use a native library to perform the same validation and aggregation logic. +* Note that the above methods are helper methods for the bundler. The bundler MAY use a native library to perform the same validation and aggregation logic. * **validateSignatures()** MUST validate the aggregated signature matches for all UserOperations in the array, and revert otherwise. This method is called on-chain by `handleOps()` @@ -353,11 +353,11 @@ interface IAggregator { #### Simulation Rationale -In order to add a UserOperation into the mempool (and later to add it into a bundle) we need to "simulate" its validation to make sure it is valid, and that it is capable of paying for its own execution. +To add a UserOperation into the mempool (and later to add it into a bundle) we need to "simulate" its validation to make sure it is valid, and that it pays for its own execution. In addition, we need to verify that the same will hold true when executed on-chain. For this purpose, a UserOperation is not allowed to access any information that might change between simulation and execution, such as current block time, number, hash etc. -In addition, a UserOperation is only allowed to access data related to this sender address: Multiple UserOperations should not access the same storage, so that it is impossible to invalidate a large number of UserOperations with a single state change. -There are 3 special contracts that interact with the account: the factory (initCode) that deploys the contract, the paymaster that can pay for the gas, and signature aggregator (described later) +In addition, a UserOperation is only allowed to access data related to this sender address: Multiple UserOperations should not access the same storage, so it is impossible to invalidate a large number of UserOperations with a single state change. +There are 3 special contracts that interact with the account: the factory (initCode) that deploys the contract, the paymaster that can pay for the gas, and a signature aggregator (described later) Each of these contracts is also restricted in its storage access, to make sure UserOperation validations are isolated. #### Simulation Specification: @@ -419,15 +419,15 @@ Either return value may contain a "validAfter" and "validUntil" timestamps, whic A node MAY drop a UserOperation if it expires too soon (e.g. wouldn't make it to the next block) by either the account or paymaster. If the `ValidationResult` includes `sigFail`, the client SHOULD drop the `UserOperation`. -In order to prevent DoS attack on bundlers, they must make sure the validation methods above pass the validation rules, which constraint their usage of opcodes and storage. +To prevent DoS attacks on bundlers, they must make sure the validation methods above pass the validation rules, which constrain their usage of opcodes and storage. For the complete procedure see [ERC-7562](./eip-7562.md) ### Alternative Mempools The simulation rules above are strict and prevent the ability of paymasters and signature aggregators to grief the system. -However, there might be use-cases where specific paymasters (and signature aggregators) can be validated +However, there might be use cases where specific paymasters (and signature aggregators) can be validated (through manual auditing) and verified that they cannot cause any problem, while still require relaxing of the opcode rules. -A bundler cannot simply "whitelist" request from a specific paymaster: if that paymaster is not accepted by all +A bundler cannot simply "whitelist" a request from a specific paymaster: if that paymaster is not accepted by all bundlers, then its support will be sporadic at best. Instead, we introduce the term "alternate mempool": a modified validation rules, and procedure of propagating them to other bundlers. @@ -435,7 +435,7 @@ The procedure of using alternate mempools is defined in [ERC-7562](./eip-7562.md ### Bundling -Bundling is the process where a node/bundler collects multiple UserOperations and create a single transaction to submit on-chain. +Bundling is the process where a node/bundler collects multiple UserOperations and creates a single transaction to submit on-chain. During bundling, the bundler should: @@ -475,42 +475,42 @@ When a bundler includes a bundle in a block it must ensure that earlier transact ### Error codes. While performing validation, the EntryPoint must revert on failures. During simulation, the calling bundler MUST be able to determine which entity (factory, account or paymaster) caused the failure. -The attribution of revert to entity is done using the call-tracing: the last entity called by the EntryPoint prior the revert is the entity that caused the revert. +The attribution of a revert to an entity is done using call-tracing: the last entity called by the EntryPoint prior to the revert is the entity that caused the revert. * For diagnostic purposes, the EntryPoint must only revert with explicit FailedOp() or FailedOpWithRevert() errors. * The message of the error starts with event code, AA## -* Event code starting with "AA1" signify an error during account creation -* Event code starting with "AA2" signify an error during account validation (validateUserOp) -* Event code starting with "AA3" signify an error during paymaster validation (validatePaymasterUserOp) +* Event code starting with "AA1" signifies an error during account creation +* Event code starting with "AA2" signifies an error during account validation (validateUserOp) +* Event code starting with "AA3" signifies an error during paymaster validation (validatePaymasterUserOp) ## Rationale -The main challenge with a purely smart contract wallet based account abstraction system is DoS safety: how can a block builder including an operation make sure that it will actually pay fees, without having to first execute the entire operation? +The main challenge with a purely smart contract wallet-based account abstraction system is DoS safety: how can a block builder including an operation make sure that it will actually pay fees, without having to first execute the entire operation? Requiring the block builder to execute the entire operation opens a DoS attack vector, as an attacker could easily send many operations that pretend to pay a fee but then revert at the last moment after a long execution. Similarly, to prevent attackers from cheaply clogging the mempool, nodes in the P2P network need to check if an operation will pay a fee before they are willing to forward it. -The first step is clean separation between validation (acceptance of UserOperation, and acceptance to pay) and execution. -In this proposal, we expect accounts to have a `validateUserOp` method that takes as input a `UserOperation`, and verify the signature and pay the fee. +The first step is a clean separation between validation (acceptance of UserOperation, and acceptance to pay) and execution. +In this proposal, we expect accounts to have a `validateUserOp` method that takes as input a `UserOperation`, verifies the signature and pays the fee. Only if this method returns successfully, the execution will happen. The entry point-based approach allows for a clean separation between verification and execution, and keeps accounts' logic simple. It enforces the simple rule that only after validation is successful (and the UserOp can pay), the execution is done, and also guarantees the fee payment. ### Validation Rules Rationale -The next step is protecting the bundlers from denial-of-service attacks by a mass number of UserOperation that appear to be valid (and pay) but that eventually revert, and thus block the bundler from processing valid UserOperations. +The next step is protecting the bundlers from denial-of-service attacks by a mass number of UserOperations that appear to be valid (and pay) but that eventually revert, and thus block the bundler from processing valid UserOperations. There are two types of UserOperations that can fail validation: -1. UserOperations that succeed in initial validation (and accepted into the mempool), but relay on environment state to fail later when attempting to include them in a block. +1. UserOperations that succeed in initial validation (and accepted into the mempool), but rely on the environment state to fail later when attempting to include them in a block. 2. UserOperations that are valid when checked independently, by fail when bundled together to be put on-chain. To prevent such rogue UserOperations, the bundler is required to follow a set of [restrictions on the validation function](./eip-7562.md), to prevent such denial-of-service attacks. ### Reputation Rationale. -UserOperation's storage access rules prevent them from interfere with each other. -But "global" entities - paymasters, factories and aggregators are accessed by multiple UserOperations, and thus might invalidate multiple previously-valid UserOperations. +UserOperation's storage access rules prevent them from interfering with each other. +But "global" entities - paymasters, factories and aggregators are accessed by multiple UserOperations, and thus might invalidate multiple previously valid UserOperations. -To prevent abuse, we throttle down (or completely ban for a period of time) an entity that causes invalidation of large number of UserOperations in the mempool. -To prevent such entities from "sybil-attack", we require them to stake with the system, and thus make such DoS attack very expensive. -Note that this stake is never slashed, and can be withdrawn any time (after unstake delay) +To prevent abuse, we throttle down (or completely ban for a period of time) an entity that causes invalidation of a large number of UserOperations in the mempool. +To prevent such entities from "Sybil-attack", we require them to stake with the system, and thus make such DoS attack very expensive. +Note that this stake is never slashed, and can be withdrawn at any time (after unstake delay) Unstaked entities are allowed, under the rules below. @@ -521,11 +521,11 @@ The stake value is not enforced on-chain, but specifically by each node while si ### Reputation scoring and throttling/banning for global entities [ERC-7562] defines a set of rules a bundler must follow when accepting UserOperations into the mempool. -It also descrbies the "reputation|" +It also descrbies the "reputation" ### Paymasters -Paymaster contracts allow abstraction of gas: having a contract, that is not the sender of the transaction, pay for the transaction fees. +Paymaster contracts allow the abstraction of gas: having a contract, that is not the sender of the transaction, to pay for the transaction fees. Paymaster architecture allows them to follow the model of "pre-charge, and later refund". E.g. a token-paymaster may pre-charge the user with the max possible price of the transaction, and refund the user with the excess afterwards. @@ -538,7 +538,7 @@ The wallet creation itself is done by a "factory" contract, with wallet-specific The factory is expected to use CREATE2 (not CREATE) to create the wallet, so that the order of creation of wallets doesn't interfere with the generated addresses. The `initCode` field (if non-zero length) is parsed as a 20-byte address, followed by "calldata" to pass to this address. This method call is expected to create a wallet and return its address. -If the factory does use CREATE2 or some other deterministic method to create the wallet, it's expected to return the wallet address even if the wallet has already been created. This is to make it easier for clients to query the address without knowing if the wallet has already been deployed, by simulating a call to `entryPoint.getSenderAddress()`, which calls the factory under the hood. +If the factory does use CREATE2 or some other deterministic method to create the wallet, it's expected to return the wallet address even if the wallet has already been created. This comes to make it easier for clients to query the address without knowing if the wallet has already been deployed, by simulating a call to `entryPoint.getSenderAddress()`, which calls the factory under the hood. When `initCode` is specified, if either the `sender` address points to an existing contract, or (after calling the initCode) the `sender` address still does not exist, then the operation is aborted. The `initCode` MUST NOT be called directly from the entryPoint, but from another address. @@ -547,7 +547,7 @@ For security reasons, it is important that the generated contract address will d This way, even if someone can create a wallet at that address, he can't set different credentials to control it. The factory has to be staked if it accesses global storage - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. -NOTE: In order for the wallet to determine the "counterfactual" address of the wallet (prior its creation), +NOTE: In order for the wallet to determine the "counterfactual" address of the wallet (prior to its creation), it should make a static call to the `entryPoint.getSenderAddress()` ### Entry point upgrading @@ -580,7 +580,7 @@ The result `SHOULD` be set to the **userOpHash** if and only if the request pass * The `message` field SHOULD be set to the revert message from the paymaster * The `data` field MUST contain a `paymaster` value * **code: -32502** - transaction rejected because of opcode validation - * **code: -32503** - UserOperation out of time-range: either wallet or paymaster returned a time-range, and it is already expired (or will expire soon) + * **code: -32503** - UserOperation out of time-range: either wallet or paymaster returned a time-range, and it has already expired (or will expire soon) * The `data` field SHOULD contain the `validUntil` and `validAfter` values * The `data` field SHOULD contain a `paymaster` value, if this error was triggered by the paymaster * **code: -32504** - transaction rejected because paymaster (or signature aggregator) is throttled/banned @@ -591,6 +591,7 @@ The result `SHOULD` be set to the **userOpHash** if and only if the request pass * **code: -32506** - transaction rejected because wallet specified unsupported signature aggregator * The `data` field SHOULD contain an `aggregator` value * **code: -32507** - transaction rejected because of wallet signature check failed (or paymaster signature, if the paymaster uses its data as signature) + * **code: -32508** - transaction rejected because paymaster balance can't cover all pending UserOperations. ##### Example: @@ -669,7 +670,7 @@ Response: Estimate the gas values for a UserOperation. Given UserOperation optionally without gas limits and gas prices, return the needed gas limits. -The signature field is ignored by the wallet, so that the operation will not require user's approval. +The signature field is ignored by the wallet, so that the operation will not require the user's approval. Still, it might require putting a "semi-valid" signature (e.g. a signature in the right length) **Parameters**: @@ -733,9 +734,9 @@ Return a UserOperation receipt based on a hash (userOpHash) returned by `eth_sen * **sender** * **nonce** * **paymaster** the paymaster used for this userOp (or empty) -* **actualGasCost** - actual amount paid (by account or paymaster) for this UserOperation +* **actualGasCost** - the actual amount paid (by account or paymaster) for this UserOperation * **actualGasUsed** - total gas used by this UserOperation (including preVerification, creation, validation and execution) -* **success** boolean - did this execution completed without revert +* **success** boolean - did this execution completed without a revert * **reason** in case of revert, this is the revert reason * **logs** the logs generated by this UserOperation (not including logs of other UserOperations in the same bundle) * **receipt** the TransactionReceipt object. @@ -788,7 +789,7 @@ Returns [EIP-155](./eip-155.md) Chain ID. ### RPC methods (debug Namespace) -This api must only be available on testing mode and is required by the compatibility test suite. In production, any `debug_*` rpc calls should be blocked. +This api must only be available in testing mode and is required by the compatibility test suite. In production, any `debug_*` rpc calls should be blocked. #### * debug_bundler_clearState @@ -906,7 +907,7 @@ After setting mode to "manual", an explicit call to debug_bundler_sendBundleNow #### * debug_bundler_setReputation -Sets reputation of given addresses. parameters: +Sets the reputation of given addresses. parameters: **Parameters:** @@ -914,7 +915,7 @@ Sets reputation of given addresses. parameters: * `address` - The address to set the reputation for. * `opsSeen` - number of times a user operations with that entity was seen and added to the mempool - * `opsIncluded` - number of times a user operations that uses this entity was included on-chain + * `opsIncluded` - number of times user operations that use this entity was included on-chain * **EntryPoint** the entrypoint used by eth_sendUserOperation @@ -961,7 +962,7 @@ An array of reputation entries with the fields: * `address` - The address to set the reputation for. * `opsSeen` - number of times a user operations with that entity was seen and added to the mempool -* `opsIncluded` - number of times a user operations that uses this entity was included on-chain +* `opsIncluded` - number of times user operation that use this entity was included on-chain * `status` - (string) The status of the address in the bundler 'ok' | 'throttled' | 'banned'. ```json= From 82b1fda4f9d7c6051968d5691c00cb8ff98a3872 Mon Sep 17 00:00:00 2001 From: Duke tho Date: Fri, 16 Aug 2024 01:08:40 +0700 Subject: [PATCH 098/126] Add ERC: Ownership Delegation and Context for ERC-721 Merged by EIP-Bot. --- ERCS/erc-7695.md | 497 +++++++++++++++++++++++++++++++++++ assets/erc-7695/mortgage.svg | 3 + assets/erc-7695/rental.svg | 3 + 3 files changed, 503 insertions(+) create mode 100644 ERCS/erc-7695.md create mode 100644 assets/erc-7695/mortgage.svg create mode 100644 assets/erc-7695/rental.svg diff --git a/ERCS/erc-7695.md b/ERCS/erc-7695.md new file mode 100644 index 0000000000..5e1e652a97 --- /dev/null +++ b/ERCS/erc-7695.md @@ -0,0 +1,497 @@ +--- +eip: 7695 +title: Ownership Delegation and Context for ERC-721 +description: Introduces contexts and ownership delegation for ERC-721 tokens, expanding dApps and financial use cases without transferring ownership +author: Duc Tho Tran (@ducthotran2010) +discussions-to: https://ethereum-magicians.org/t/erc-7695-ownership-delegation-and-context-for-non-fungible-token/19716 +status: Draft +type: Standards Track +category: ERC +created: 2024-04-02 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension for [ERC-721](./eip-721.md), designed to specify users for various contexts with a locking feature and allow temporary ownership delegation without changing the original owner. + +This EIP preserves the benefits and rights of the owner while expanding the utility of NFTs across various dApps by adding the concepts of Ownership Delegation and Contexts, which define specific roles: Controller and User, who can use the NFT within defined contexts. + +## Motivation + +For standard [ERC-721](./eip-721.md) NFTs, there are several use cases in financial applications, including: + +- Staking NFTs to earn rewards. +- Mortgaging an NFT to generate income. +- Granting users for different purposes like rental and token delegationโ€”where someone pays to use tokens and pays another party to use the tokens. + +Traditionally, these applications require ownership transfers to lock the NFT in contracts. However, other decentralized applications (dApps) recognize token ownership as proof that the token owner is entitled to benefits within their reward systems, such as airdrops or tiered rewards. If token owners have their tokens locked in contracts, they are not eligible to receive benefits from holding these tokens, or the reward systems have to support as many contracts as possible to help these owners. + +This is because there is only an Owner role indicating the ownership rights, developing on top ofย [ERC-721](./eip-721.md)ย has often posed challenges. This proposal aims to solve these challenges by contextualizing the use case to be handled by controllers and distinguishing ownership rights from other roles at the standard level through an ownership delegation mechanism. Standardizing these measures, dApp developers can more easily construct infrastructure and protocols on top of this standard. + +## Specification + +The keywords โ€œMUSTโ€, โ€œMUST NOTโ€, โ€œREQUIREDโ€, โ€œSHALLโ€, โ€œSHALL NOTโ€, โ€œSHOULDโ€, โ€œSHOULD NOTโ€, โ€œRECOMMENDEDโ€, โ€œMAYโ€, and โ€œOPTIONALโ€ in this document are to be interpreted as described in RFC 2119. + +### Definitions + +This specification encompasses the following components: + +**Token Context** provides a specified use case of a token. It serves as the association relationship between Tokens and Contexts. Within each unique token context, there exists an allocated user who is authorized to utilize the token within that context. In a specified context, there are two distinct roles: + +- **Controller**: This role possesses the authority to control the context. +- **User**: This role signifies the primary token user within the given context. + +**Ownership Rights** of a token are defined to be able to: + +- Transfer that token to a new owner. +- Add token context(s): attaching that token to/from one or many contexts. +- Remove token context(s): detaching that token to/from one or many contexts. + +**Ownership Delegation** involves distinguishing between owner and ownership rights by delegating ownership to other accounts for a specific duration. During this period, owners temporarily cede ownership rights until the delegation expires. + +### Roles + +| Roles | Explanation / Permission | Quantity per Token | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | +| Owner | โ€ข Has **Ownership Rights** by default
โ€ข Delegates an account to hold **Ownership Rights** in a duration | $1$ | +| Ownership Delegatee | โ€ข Has **Ownership Rights** in a delegation duration
โ€ข Renounces before delegation expires | $1$ | +| Ownership Manager | โ€ข Is one who holds **Ownership Rights**
โ€ข If not delegated yet, it is referenced to **Owner**, otherwise it is referenced to **Ownership Delegatee** | $1$ | +| **Context Roles** | | $n$ | +| Controller | โ€ข Transfers controller
โ€ข Sets context user
โ€ข (Un)locks token transfer | $1$ per context | +| User | โ€ข Authorized to use token in its context | $1$ per context | + +### Interface + +**Smart contracts implementing this standard MUST implement all the functions in theย `IERC7695`ย interface.** + +Smart contracts implementing this standard MUST implement the [ERC-165](./eip-165.md) `supportsInterface` function and MUST return the constant value `true` if `0x486b6fba` is passed through the `interfaceID` argument. + +```solidity +/// Note: the ERC-165 identifier for this interface is 0x486b6fba. +interface IERC7695 /* is IERC721, IERC165 */ { + /// @dev This emits when a context is updated by any mechanism. + event ContextUpdated(bytes32 indexed ctxHash, address indexed controller, uint64 detachingDuration); + /// @dev This emits when a token is attached to a certain context by any mechanism. + event ContextAttached(bytes32 indexed ctxHash, uint256 indexed tokenId); + /// @dev This emits when a token is requested to detach from a certain context by any mechanism. + event ContextDetachmentRequested(bytes32 indexed ctxHash, uint256 indexed tokenId); + /// @dev This emits when a token is detached from a certain context by any mechanism. + event ContextDetached(bytes32 indexed ctxHash, uint256 indexed tokenId); + /// @dev This emits when a user is assigned to a certain context by any mechanism. + event ContextUserAssigned(bytes32 indexed ctxHash, uint256 indexed tokenId, address indexed user); + /// @dev This emits when a token is (un)locked in a certain context by any mechanism. + event ContextLockUpdated(bytes32 indexed ctxHash, uint256 indexed tokenId, bool locked); + /// @dev This emits when the ownership delegation is started by any mechanism. + event OwnershipDelegationStarted(uint256 indexed tokenId, address indexed delegatee, uint64 until); + /// @dev This emits when the ownership delegation is accepted by any mechanism. + event OwnershipDelegationAccepted(uint256 indexed tokenId, address indexed delegatee, uint64 until); + /// @dev This emits when the ownership delegation is stopped by any mechanism. + event OwnershipDelegationStopped(uint256 indexed tokenId, address indexed delegatee); + + /// @notice Gets the longest duration the detaching can happen. + function maxDetachingDuration() external view returns (uint64); + + /// @notice Gets controller address and detachment duration of a context. + /// @dev MUST revert if the context is not existent. + /// @param ctxHash A hash of context to query the controller. + /// @return controller The address of the context controller. + /// @return detachingDuration The duration must be waited for detachment in second(s). + function getContext(bytes32 ctxHash) external view returns (address controller, uint64 detachingDuration); + + /// @notice Creates a new context. + /// @dev MUST revert if the context is already existent. + /// MUST revert if the controller address is zero address. + /// MUST revert if the detaching duration is larger than max detaching duration. + /// MUST emit the event {ContextUpdated} to reflect context created and controller set. + /// @param controller The address that controls the created context. + /// @param detachingDuration The duration must be waited for detachment in second(s). + /// @param ctxMsg The message of new context to be used for hashing. + /// @return ctxHash Hash of the created context. + function createContext(address controller, uint64 detachingDuration, bytes calldata ctxMsg) + external + returns (bytes32 ctxHash); + + /// @notice Updates an existing context. + /// @dev MUST revert if method caller is not the current controller. + /// MUST revert if the context is non-existent. + /// MUST revert if the new controller address is zero address. + /// MUST revert if the detaching duration is larger than max detaching duration. + /// MUST emit the event {ContextUpdated} on success. + /// @param ctxHash Hash of the context to set. + /// @param newController The address of new controller. + /// @param newDetachingDuration The new duration must be waited for detachment in second(s). + function updateContext(bytes32 ctxHash, address newController, uint64 newDetachingDuration) external; + + /// @notice Queries if a token is attached to a certain context. + /// @param ctxHash Hash of a context. + /// @param tokenId The NFT to query. + /// @return True if the token is attached to the context, false if not. + function isAttachedWithContext(bytes32 ctxHash, uint256 tokenId) external view returns (bool); + + /// @notice Attaches a token with a certain context. + /// @dev See "attachContext rules" in "Token (Un)lock Rules". + /// @param ctxHash Hash of a context. + /// @param tokenId The NFT to be attached. + /// @param data Additional data with no specified format, MUST be sent unaltered in call to the {IERC7695ContextCallback} hook(s) on controller. + function attachContext(bytes32 ctxHash, uint256 tokenId, bytes calldata data) external; + + /// @notice Requests to detach a token from a certain context. + /// @dev See "requestDetachContext rules" in "Token (Un)lock Rules". + /// @param ctxHash Hash of a context. + /// @param tokenId The NFT to be detached. + /// @param data Additional data with no specified format, MUST be sent unaltered in call to the {IERC7695ContextCallback} hook(s) on controller. + function requestDetachContext(bytes32 ctxHash, uint256 tokenId, bytes calldata data) external; + + /// @notice Executes context detachment. + /// @dev See "execDetachContext rules" in "Token (Un)lock Rules". + /// @param ctxHash Hash of a context. + /// @param tokenId The NFT to be detached. + /// @param data Additional data with no specified format, MUST be sent unaltered in call to the {IERC7695ContextCallback} hook(s) on controller. + function execDetachContext(bytes32 ctxHash, uint256 tokenId, bytes calldata data) external; + + /// @notice Finds the context user of a token. + /// @param ctxHash Hash of a context. + /// @param tokenId The NFT to be detached. + /// @return user Address of the context user. + function getContextUser(bytes32 ctxHash, uint256 tokenId) external view returns (address user); + + /// @notice Updates the context user of a token. + /// @dev MUST revert if the method caller is not context controller. + /// MUST revert if the context is non-existent. + /// MUST revert if the token is not attached to the context. + /// MUST emit the event {ContextUserAssigned} on success. + /// @param ctxHash Hash of a context. + /// @param tokenId The NFT to be update. + /// @param user Address of the new user. + function setContextUser(bytes32 ctxHash, uint256 tokenId, address user) external; + + /// @notice Queries if the lock a token is locked in a certain context. + /// @param ctxHash Hash of a context. + /// @param tokenId The NFT to be queried. + /// @return True if the token context is locked, false if not. + function isTokenContextLocked(bytes32 ctxHash, uint256 tokenId) external view returns (bool); + + /// @notice (Un)locks a token in a certain context. + /// @dev See "setContextLock rules" in "Token (Un)lock Rules". + /// @param ctxHash Hash of a context. + /// @param tokenId The NFT to be queried. + /// @param lock New status to be (un)locked. + function setContextLock(bytes32 ctxHash, uint256 tokenId, bool lock) external; + + /// @notice Finds the ownership manager of a specified token. + /// @param tokenId The NFT to be queried. + /// @return manager Address of delegatee. + function getOwnershipManager(uint256 tokenId) external view returns(address manager); + + /// @notice Finds the ownership delegatee of a token. + /// @dev MUST revert if there is no (or an expired) ownership delegation. + /// @param tokenId The NFT to be queried. + /// @return delegatee Address of delegatee. + /// @return until The delegation expiry time. + function getOwnershipDelegatee(uint256 tokenId) external view returns (address delegatee, uint64 until); + + /// @notice Finds the pending ownership delegatee of a token. + /// @dev MUST revert if there is no (or an expired) pending ownership delegation. + /// @param tokenId The NFT to be queried. + /// @return delegatee Address of pending delegatee. + /// @return until The delegation expiry time in the future. + function pendingOwnershipDelegatee(uint256 tokenId) external view returns (address delegatee, uint64 until); + + /// @notice Starts ownership delegation and retains ownership until a specific timestamp. + /// @dev Replaces the pending delegation if any. + /// See "startDelegateOwnership rules" in "Ownership Delegation Rules". + /// @param tokenId The NFT to be delegated. + /// @param delegatee Address of new delegatee. + /// @param until The delegation expiry time. + function startDelegateOwnership(uint256 tokenId, address delegatee, uint64 until) external; + + /// @notice Accepts ownership delegation request. + /// @dev See "acceptOwnershipDelegation rules" in "Ownership Delegation Rules". + /// @param tokenId The NFT to be accepted. + function acceptOwnershipDelegation(uint256 tokenId) external; + + /// @notice Stops the current ownership delegation. + /// @dev See "stopOwnershipDelegation rules" in "Ownership Delegation Rules". + /// @param tokenId The NFT to be stopped. + function stopOwnershipDelegation(uint256 tokenId) external; +} +``` + +**Enumerable extension** + +The enumeration extension is OPTIONAL for this standard. This allows your contract to publish its full list of contexts and make them discoverable. When calling the `supportsInterface` function MUST return the constant value `true` if `0xcebf44b7` is passed through the `interfaceID` argument. + +```solidity +/// Note: the ERC-165 identifier for this interface is 0xcebf44b7. +interface IERC7695Enumerable /* is IERC165 */ { + /// @dev Returns a created context in this contract at `index`. + /// An index must be a value between 0 and {getContextCount}, non-inclusive. + /// Note: When using {getContext} and {getContextCount}, make sure you perform all queries on the same block. + function getContext(uint256 index) external view returns(bytes32 ctxHash); + + /// @dev Returns the number of contexts created in the contract. + function getContextCount() external view returns(uint256); + + /// @dev Returns a context attached to a token at `index`. + /// An index must be a value between 0 and {getAttachedContextCount}, non-inclusive. + /// Note: When using {getAttachedContext} and {getAttachedContextCount}, make sure you perform all queries on the same block. + function getAttachedContext(uint256 tokenId, uint256 index) external view returns(bytes32 ctxHash); + + /// @dev Returns the number of contexts attached to the token. + function getAttachedContextCount(uint256 tokenId) external view returns(uint256); +} +``` + +**Controller Interface** + +The controller is RECOMMENDED to be a contract and including callback methods to allow callbacks in cases where there are any attachment or detachment requests. When calling the `supportsInterface` function MUST return the constant value `true` if `0xad0491f1` is passed through the `interfaceID` argument. + +```solidity +/// Note: the ERC-165 identifier for this interface is 0xad0491f1. +interface IERC7695ContextCallback /* is IERC165 */ { + /// @dev This method is called once the token is attached by any mechanism. + /// This function MAY throw to revert and reject the attachment. + /// @param ctxHash The hash of context invoked this call. + /// @param tokenId NFT identifier which is being attached. + /// @param operator The address which called {attachContext} function. + /// @param data Additional data with no specified format. + function onAttached(bytes32 ctxHash, uint256 tokenId, address operator, bytes calldata data) external; + + /// @dev This method is called once the token detachment is requested by any mechanism. + /// @param ctxHash The hash of context invoked this call. + /// @param tokenId NFT identifier which is being requested for detachment. + /// @param operator The address which called {requestDetachContext} function. + /// @param data Additional data with no specified format. + function onDetachRequested(bytes32 ctxHash, uint256 tokenId, address operator, bytes calldata data) external; + + /// @dev This method is called once a token context is detached by any mechanism. + /// @param ctxHash The hash of context invoked this call. + /// @param tokenId NFT identifier which is being detached. + /// @param user The address of the context user which is being detached. + /// @param operator The address which called {execDetachContext} function. + /// @param data Additional data with no specified format. + function onExecDetachContext(bytes32 ctxHash, uint256 tokenId, address user, address operator, bytes calldata data) external; +} +``` + +### Ownership Delegation Rules + +**startDelegateOwnership rules** + +- MUST revert unless there is no delegation. +- MUST revert unless the method caller is the owner, an authorized operator of owner, or the approved address for this NFT. +- MUST revert unless the expiry time is in the future. +- MUST revert if the delegatee address is the owner or zero address. +- MUST revert if the token is not existent. +- MUST emit the event `OwnershipDelegationStarted` on success. +- After the above conditions are met, this function MUST replace the pending delegation if any. + +**acceptOwnershipDelegation rules** + +- MUST revert if there is no delegation. +- MUST revert unless the method caller is the delegatee, or an authorized operator of delegatee. +- MUST revert unless the expiry time is in the future. +- MUST emit the event `OwnershipDelegationAccepted` on success. +- After the above conditions are met, the delegatee MUST be recorded as the ownership manager until the delegation expires. + +**stopDelegateOwnership rules** + +- MUST revert unless the delegation is already accepted. +- MUST revert unless the expiry time is in the future. +- MUST revert unless the method caller is the delegatee, or an authorized operator of delegatee. +- MUST emit the event `OwnershipDelegationStopped` on success. +- After the above conditions are met, the owner MUST be recorded as the ownership manager. + +### **Token (Un)lock Rules** + +To be more explicit about how token (un)locked, these functions: + +- A token can be attached to a context using the `attachContext` method +- The `setContextLock` function MUST be called by the controller to (un)lock +- The `requestDetachContext` and `execDetachContext` functions MUST be called by the ownership manager and MUST operate with respect to theย `IERC7695ContextCallback`ย hook functions + +A list of scenarios and rules follows. + +**Scenarios** + +**_Scenario#1:_** Context controller wants to (un)lock a token that is not requested for detachment. + +- `setContextLock` MUST be called successfully + +**_Scenario#2:_** Context controller wants to (un)lock a token that is requested for detachment. + +- `setContextLock` MUST be reverted + +**_Scenario#3:_** Ownership manager wants to (unlock and) detach a locked token and the callback controller implements `IERC7695ContextCallback`. + +- Caller MUST: + - Call `requestDetachContext` function successfully + - Wait at least context detaching duration (see variable `detachingDuration` in the `getContext` function) + - Call `execDetachContext` function successfully +- `requestDetachContext` MUST call the `onDetachRequested` function despite the call result +- `execDetachContext` MUST call the `onExecDetachContext` function despite the call result + +**_Scenario#4:_** Ownership manager wants to (unlock and) detach a locked token and the callback controller does not implement `IERC7695ContextCallback`. + +- Caller MUST: + - Call `requestDetachContext` function successfully + - Wait at least context detaching duration (see variable `detachingDuration` in the `getContext` function) + - Call `execDetachContext` function successfully + +**_Scenario#5:_** Ownership manager wants to detach an unlocked token and the callback controller implements `IERC7695ContextCallback`. + +- Caller MUST call `requestDetachContext` function successfully +- `requestDetachContext` MUST call the `onExecDetachContext` function despite the result +- `execDetachContext` MUST NOT be called + +**_Scenario#6:_** Ownership manager wants to detach an unlocked token and the callback controller does not implement `IERC7695ContextCallback`. + +- Caller MUST call `requestDetachContext` function successfully +- `execDetachContext` MUST NOT be called + +**Rules** + +**attachContext rules** + +- MUST revert unless the method caller is the ownership manager, an authorized operator of ownership manager, or the approved address for this NFT (if the token is not being delegated). +- MUST revert if the context is non-existent. +- MUST revert if the token is already attached to the context. +- MUST emit the event `ContextAttached`. +- After the above conditions are met, this function MUST check if the controller address is a smart contract (e.g. code size > 0). If so, it MUST call `onAttached` and MUST revert if the call is failed. + - Theย `data`ย argument provided by the caller MUST be passed with its contents unaltered to theย `onAttached`ย hook function via itsย `data`ย argument. + +**setContextLock rules** + +- MUST revert if the context is non-existent. +- MUST revert if the token is not attached to the context. +- MUST revert if a detachment request has previously been made. +- MUST revert if the method caller is not context controller. +- MUST emit the event `ContextLockUpdated` on success. + +**requestDetachContext rules** + +- MUST revert if a detachment request has previously been made. +- MUST revert unless the method caller is the context controller, the ownership manager, an authorized operator of the ownership manager, or the approved address for this NFT (if the token is not being delegated). +- If the caller is context controller or the token context is not locked, MUST emit the `ContextDetached` event. After the above conditions are met, this function MUST check if the controller address is a smart contract (e.g. code size > 0). If so, it MUST call `onExecDetachContext` and the call result MUST be skipped. + - Theย `data`ย argument provided by the caller MUST be passed with its contents unaltered to theย `onExecDetachContext`ย hook function via itsย `data`ย argument. +- If the token context is locked, MUST emit the `ContextDetachRequested` event. After the above conditions are met, this function MUST check if the controller address is a smart contract (e.g. code size > 0). If so, it MUST call `onDetachRequested` and the call result MUST be skipped. + - Theย `data`ย argument provided by the caller MUST be passed with its contents unaltered to theย `onDetachRequested`ย hook function via itsย `data`ย argument. + +**execDetachContext rules** + +- MUST revert unless the method caller is the ownership manager, an authorized operator of ownership manager, or the approved address for this NFT (if the token is not being delegated). +- MUST revert unless a detachment request has previously been made and the specified detaching duration has passed (use variable `detachingDuration` in the `getContext` function when requesting detachment). +- MUST emit the `ContextDetached` event. +- After the above conditions are met, this function MUST check if the controller address is a smart contract (e.g. code size > 0). If so, it MUST call `onExecDetachContext` and the call result MUST be skipped. + - Theย `data`ย argument provided by the caller MUST be passed with its contents unaltered to theย `onExecDetachContext`ย hook function via itsย `data`ย argument. + +### Additional Transfer Rules + +In addition to extending from [ERC-721](./eip-721.md) for the transfer mechanism when transferring an NFT, the implementation: + +- MUST revert unless the method caller is the ownership manager, an authorized operator of ownership manager, or the approved address for this NFT (if the token is not being delegated). +- MUST revoke ownership delegation if any. +- MUST detach every attached context: + - MUST revert unless a detachment request has previously been made and the specified detaching duration has passed (use variable `detachingDuration` in the `getContext` function when requesting detachment) if the token is locked. + - MUST check if the controller address is a smart contract (e.g. code size > 0). If so, it MUST call the `onExecDetachContext` function (with an empty `data` argument `""`) and the call result MUST be skipped. + +## Rationale + +When designing the proposal, we considered the following concerns. + +### Multiple contexts for multiple use cases + +This proposal is centered around Token Context to allow for the creation of distinct contexts tailored to various decentralized applications (dApps). The context controller assumes the role of facilitating (rental or delegation) dApps, by enabling the granting of usage rights to another user without modifying the NFT's owner record. Besides, this proposal provides the lock feature for contexts to ensure trustlessness in performing these dApps, especially staking cases. + +### Providing an unlock mechanism for owners + +By providing an unlock mechanism for owners, this approach allows owners to unlock their tokens independently, without relying on the context controller to initiate the process. This prevents scenarios where, should the controller lose control, owners would be unable to unlock their tokens. + +### Attachment and detachment callbacks + +The callback results of the `onDetachRequested` and `onExecDetachContext` functions in the **Token (Un)lock Rules** are skipped because we are intentionally removing the controller's ability to stop detachment, ensuring token detachment is independent of the controller's actions. + +Additionally, to retain the permission to reject incoming attachments, the operation reverts if the call to the `onAttach` function fails. + +### Ownership delegation + +This feature provides a new approach by separating the owner and ownership. Primarily designed to facilitate delegating for third parties, it enables delegating another account as the manager of ownership, distinct from the owner. + +Unlike `approve` or `setApprovalForAll` methods, which grant permission to other accounts while maintaining ownership status. Ownership delegation goes beyond simply granting permissions; it involves transferring the owner's rights to the delegatee, with provisions for automatic reversion upon expiration. This mechanism prevents potential abuses, such as requesting mortgages and transfers to alternative accounts if the owner retains ownership rights. + +The **2-step delegation** process is provided to prevent mistakes in assigning delegatees, it must be done through two steps: offer and confirm. In cases the delegation needs to be canceled before its scheduled expiry, the delegatees can invoke `stopOwnershipDelegation` method. + +### Transfer method mechanism + +As part of the integration with the transfer method, we extended its implicit behavior to include token approval: + +- **Reset Ownership Delegation:** Automatically resets ownership delegations. The `OwnershipDelegationStopped` event is intentionally not emitted. +- **Detach All Contexts:** Similarly, all contexts associated with the token are detached if none of them is locked. The `ContextDetached` event is intentionally not emitted. + +These modifications are to ensure trustlessness and gas efficiency during token transfers, providing a seamless experience for users. + +## Backwards Compatibility + +This proposal is backward compatible withย [ERC-721](./eip-721.md). + +## Security Considerations + +### Detaching duration + +When developing this token standard to serve multiple contexts: + +- The contract deployer should establish an appropriate upper threshold for detachment delay (by `maxDetachingDuration` method). +- The context owner should anticipate potential use cases and establish an appropriate period not larger than the upper threshold. + +This precaution is essential to mitigate the risk of the owner abusing systems by spamming listings and transferring tokens to another owner in a short time. + +### Duplicated token usage + +When initiating a new context, the context controllers should track all other contexts within the NFT contract to prevent duplicated usage. + +For example, suppose a scenario where a token is locked for rental purposes within a particular game. If that game introduces another context (e.g. supporting delegation in that game), it could lead to duplicated token usage within the game, despite being intended for different contexts. + +In such cases, a shared context for rental and delegation purposes can be considered. Or there must be some restrictions on the new delegation context to prevent reusing that token in the game. + +### Ownership Delegation Buffer Time + +When constructing systems that rely on ownership delegation for product development, it is imperative to incorporate a buffer time (of at least `maxDetachingDuration` seconds) when requesting ownership delegation. This precaution is essential to mitigate the risk of potential abuse, particularly if one of the associated contexts locks the token until the delegation time expires. +For example, consider a scenario where a mortgage contract is built atop this standard, which has a maximum detaching duration of 7 days, while the required delegation period is only 3 days. In such cases, without an adequate buffer time, the owner could exploit the system by withdrawing funds and invoking the relevant context to lock the token, thus preventing its unlock and transfer. + +### Validating Callback Callers + +To enhance security and integrity in interactions between contracts, it is essential to validate the caller of any callback function while implementing the `IERC7695ContextCallback`. This validation ensures that the `msg.sender` of the callback is indeed the expected contract address, typically the token contract or a designated controller contract. Such checks are crucial for preventing unauthorized actions that could be executed by malicious entities pretending to be a legitimate contract. + +### Recommended practices + +**Rental** + +This is a typical use case for rentals, supposing A(owner) owns a token and wants to list his/her token for rent, and B(user) wants to rent the token to play in a certain game. + +![Rental Flow](../assets/eip-7695/rental.svg) + +**Mortgage** + +When constructing collateral systems, it is recommended to support token owners who wish to rent out their tokens while using them for collateral lending. This approach enhances the appeal of mortgage systems, creating a more attractive and versatile financial ecosystem that meets many different needs. + +This is a typical use case for mortgages, supposing A(owner) owns a token and wants to mortgage, and B(lender) wants to earn interest by lending their funds to A. + +![Mortgage Flow](../assets/eip-7695/mortgage.svg) + +### Risk of Token Owner + +**Phishing attacks** + +It is crucial to note that the owner role has the ability to delegate ownership to another account, allowing it to authorize transfers out of the respective wallet. Consequently, some malicious actors could deceive the token owner into delegating them as a delegatee by invoking the `startDelegateOwnership` method. This risk can be considered the same as the `approve` or `setApprovalForAll` methods. + +**Ownership rights loss** + +When interacting with a contract system (e.g. mortgage), where owners have to delegate their ownership rights to the smart contract, it's imperative to: + +- Ensure the timeframe for delegation is reasonable and not excessively distant. If the contract mandates a delegation period that extends too far into the future, make sure it includes a provision to revoke ownership delegation when specific conditions are met. Failing to include such a provision could lead to the loss of ownership rights until the delegation expires. +- Be aware that if the contract owner or their operator is compromised, the token ownership can be altered. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/assets/erc-7695/mortgage.svg b/assets/erc-7695/mortgage.svg new file mode 100644 index 0000000000..609d66f790 --- /dev/null +++ b/assets/erc-7695/mortgage.svg @@ -0,0 +1,3 @@ + + +NFTMortgageNFTMortgagealt[NFT is locked]alt[A kept agreement][A breached agreement]B(Lender)A(Owner)start delegate ownership for [Mortgage]1use NFT to request a loan2fill a loan3accept ownership delegation4pay debt & finish agreement5stop ownership delegation6refund7revoke agreement8request unlock (and detach)9wait delay time10claim NFT11invoke transfer12transfer NFT to B13B(Lender)A(Owner) \ No newline at end of file diff --git a/assets/erc-7695/rental.svg b/assets/erc-7695/rental.svg new file mode 100644 index 0000000000..92ef30dead --- /dev/null +++ b/assets/erc-7695/rental.svg @@ -0,0 +1,3 @@ + + +NFTRentalNFTRentalRental is the context controllerloop[each period]alt[A & B kept agreement][B breached agreement][A breached agreement]B(User)A(Owner)list NFT for rent1fill funds and rent2lock NFT3set B as [User]4request claim funds5transfer funds in the period6finish agreement7revoke agreement8revoke agreement9unlock NFT10set A as [User]11transfer min funds12refund13B(User)A(Owner) \ No newline at end of file From e3f5bbea37291eb6fc65dbc8bbf9c7fe794c4852 Mon Sep 17 00:00:00 2001 From: T1MOH593 <79079180+T1MOH593@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:45:21 +0400 Subject: [PATCH 099/126] Fix incorrect comment copied from previous function (#429) --- assets/erc-3448/MetaProxyFactory.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/erc-3448/MetaProxyFactory.sol b/assets/erc-3448/MetaProxyFactory.sol index e7c3679124..e8286b36ef 100644 --- a/assets/erc-3448/MetaProxyFactory.sol +++ b/assets/erc-3448/MetaProxyFactory.sol @@ -85,7 +85,7 @@ contract MetaProxyFactory { mstore(ptr, length) ptr := add(ptr, 32) - // The size is deploy code + contract code + calldatasize - 4 + 32. + // The size is deploy code + contract code + length + 32. addr := create(0, start, sub(ptr, start)) } } From 49d81df6ba372101296790426f37a2d1d668c47e Mon Sep 17 00:00:00 2001 From: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:18:35 -0400 Subject: [PATCH 100/126] Update erc-7588.md (#597) --- ERCS/erc-7588.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7588.md b/ERCS/erc-7588.md index 2cd4682820..62ce8f5d9b 100644 --- a/ERCS/erc-7588.md +++ b/ERCS/erc-7588.md @@ -4,7 +4,8 @@ title: Blob Transactions Metadata JSON Schema description: Attaching metadata to blobs carried by blob transactions author: Gavin Fu (@gavfu), Leo Wang (@wanglie1986), Bova Chen (@appoipp), Aiden X (@4ever9) discussions-to: https://ethereum-magicians.org/t/erc7588-attaching-metadata-to-blobs-carried-by-blob-transactions/17873 -status: Review +status: Last Call +last-call-deadline: 2024-09-03 type: Standards Track category: ERC created: 2024-01-01 @@ -149,4 +150,4 @@ This EIP does not introduce any new security risks or vulnerabilities, as the me ## Copyright -Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). From 6e37a79f874148f01cc39ca716be774ae463af50 Mon Sep 17 00:00:00 2001 From: "drCathieSo.eth" Date: Tue, 20 Aug 2024 16:20:52 +0200 Subject: [PATCH 101/126] Website: Move ERC-7007 to Last Call Merged by EIP-Bot. --- ERCS/erc-7007.md | 133 +- .../erc-7007/contracts/ERC7007Enumerable.sol | 7 +- assets/erc-7007/contracts/ERC7007Opml.sol | 45 +- assets/erc-7007/contracts/ERC7007Zkml.sol | 23 +- assets/erc-7007/contracts/IERC7007.sol | 20 +- .../erc-7007/contracts/IERC7007Updatable.sol | 6 +- assets/erc-7007/package-lock.json | 16534 ---------------- assets/erc-7007/test/test.js | 8 +- 8 files changed, 115 insertions(+), 16661 deletions(-) delete mode 100644 assets/erc-7007/package-lock.json diff --git a/ERCS/erc-7007.md b/ERCS/erc-7007.md index 34801db404..caac5be17b 100644 --- a/ERCS/erc-7007.md +++ b/ERCS/erc-7007.md @@ -4,7 +4,8 @@ title: Verifiable AI-Generated Content Token description: An ERC-721 extension for verifiable AI-generated content tokens using Zero-Knowledge and Optimistic Machine Learning techniques author: Cathie So (@socathie), Xiaohang Yu (@xhyumiracle), Conway (@0x1cc), Lee Ting Ting (@tina1998612), Kartin discussions-to: https://ethereum-magicians.org/t/eip-7007-zkml-aigc-nfts-an-erc-721-extension-interface-for-zkml-based-aigc-nfts/14216 -status: Review +status: Last Call +last-call-deadline: 2024-09-30 type: Standards Track category: ERC created: 2023-05-10 @@ -13,17 +14,17 @@ requires: 165, 721 ## Abstract -The verifiable AI-generated content (AIGC) non-fungible token (NFT) standard is an extension of the [ERC-721](./eip-721.md) token standard for AIGC. It proposes a set of interfaces for basic interactions and enumerable interactions for AIGC-NFTs. The standard includes a `mint` and `verify` function interface, a new `Mint` event, optional `Enumerable` and `Updatable` extensions, and a JSON schema for AIGC-NFT metadata. Additionally, it incorporates Zero-Knowledge Machine Learning (zkML) and Optimistic Machine Learning (opML) capabilities to enable verification of AIGC data correctness. In this standard, the `tokenId` is indexed by the `prompt`. +The verifiable AI-generated content (AIGC) non-fungible token (NFT) standard is an extension of the [ERC-721](./eip-721.md) token standard for AIGC. It proposes a set of interfaces for basic interactions and enumerable interactions for AIGC-NFTs. The standard includes an `addAigcData` and `verify` function interface, a new `AigcData` event, optional `Enumerable` and `Updatable` extensions, and a JSON schema for AIGC-NFT metadata. Additionally, it incorporates Zero-Knowledge Machine Learning (zkML) and Optimistic Machine Learning (opML) capabilities to enable verification of AIGC data correctness. In this standard, the `tokenId` is indexed by the `prompt`. ## Motivation -The verifiable AIGC-NFT standard aims to extend the existing [ERC-721](./eip-721.md) token standard to accommodate the unique requirements of AI-generated content NFTs representing models in a collection. This standard provides interfaces to use zkML or opML to verify whether or not the AIGC data for an NFT is generated from a certain ML model with a certain input (prompt). The proposed interfaces allow for additional functionality related to minting, verifying, and enumerating AIGC-NFTs. Additionally, the metadata schema provides a structured format for storing information related to AIGC-NFTs, such as the prompt used to generate the content and the proof of ownership. +The verifiable AIGC-NFT standard aims to extend the existing [ERC-721](./eip-721.md) token standard to accommodate the unique requirements of AI-generated content NFTs representing models in a collection. This standard provides interfaces to use zkML or opML to verify whether or not the AIGC data for an NFT is generated from a certain ML model with a certain input (prompt). The proposed interfaces allow for additional functionality related to adding AIGC data, verifying, and enumerating AIGC-NFTs. Additionally, the metadata schema provides a structured format for storing information related to AIGC-NFTs, such as the prompt used to generate the content and the proof of ownership. This standard supports two primary types of proofs: validity proofs and fraud proofs. In practice, zkML and opML are commonly employed as the prevailing instances for these types of proofs. Developers can choose their preferred ones. In the zkML scenario, this standard enables model owners to publish their trained model and its ZKP verifier to Ethereum. Any user can claim an input (prompt) and publish the inference task. Any node that maintains the model and the proving circuit can perform the inference and proving, and submit the output of inference and the ZK proof for the inference trace to the verifier. The user that initiates the inference task will own the output for the inference of that model and input (prompt). -In the opML scenario, this standard enables model owners to publish their trained model to Ethereum. Any user can claim an input (prompt) and publish the inference task. Any node that maintains the model can perform the inference and submit the inference output. Other nodes can challenge this result within a predefined challenge period. At the end of the challenge period, the user can verify that they own the output for the inference of that model and prompt. +In the opML scenario, this standard enables model owners to publish their trained model to Ethereum. Any user can claim an input (prompt) and publish the inference task. Any node that maintains the model can perform the inference and submit the inference output. Other nodes can challenge this result within a predefined challenge period. At the end of the challenge period, the user can verify that they own the output for the inference of that model and prompt, and update the AIGC data as needed. This capability is especially beneficial for AI model authors and AI content creators seeking to capitalize on their creations. With this standard, every input prompt and its resulting content can be securely verified on the blockchain. This opens up opportunities for implementing revenue-sharing mechanisms for all AI-generated content (AIGC) NFT sales. AI model authors can now share their models without concerns that open-sourcing will diminish their financial value. @@ -32,10 +33,11 @@ An example workflow of a zkML AIGC NFT project compliant with this proposal is a ![zkML Suggested Workflow](../assets/eip-7007/workflow.png) There are 4 components in this workflow: -* ML model - contains weights of a pre-trained model; given an inference input, generates the output -* zkML prover - given an inference task with input and output, generates a ZK proof -* AIGC-NFT smart contract - contract compliant with this proposal, with full [ERC-721](./eip-721.md) functionalities -* Verifier smart contract - implements a `verify` function, given an inference task and its ZK proof, returns the verification result as a boolean + +- ML model - contains weights of a pre-trained model; given an inference input, generates the output +- zkML prover - given an inference task with input and output, generates a ZK proof +- AIGC-NFT smart contract - contract compliant with this proposal, with full [ERC-721](./eip-721.md) functionalities +- Verifier smart contract - implements a `verify` function, given an inference task and its ZK proof, returns the verification result as a boolean ## Specification @@ -43,47 +45,37 @@ The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SH **Every compliant contract must implement the `IERC7007`, [`ERC721`](./eip-721.md), and [`ERC165`](./eip-165.md) interfaces.** -The verifiable AIGC-NFT standard includes the following interfaces: +The verifiable AIGC-NFT standard includes the following interfaces: -`IERC7007`: Defines a `mint` function and a `Mint` event for minting AIGC-NFTs. Defines a `verify` function to check the validity of the combination of prompt and aigcData using zkML/opML techniques. +`IERC7007`: Defines an `addAigcData` function and an `AigcData` event for adding AIGC data to AIGC-NFTs. Defines a `verify` function to check the validity of the combination of prompt and aigcData using zkML/opML techniques. ```solidity pragma solidity ^0.8.18; /** * @dev Required interface of an ERC7007 compliant contract. - * Note: the ERC-165 identifier for this interface is 0x7e52e423. + * Note: the ERC-165 identifier for this interface is 0x702c55a6. */ interface IERC7007 is IERC165, IERC721 { /** - * @dev Emitted when `tokenId` token is minted. + * @dev Emitted when `tokenId` token's AIGC data is added. */ - event Mint( - address indexed to, + event AigcData( uint256 indexed tokenId, bytes indexed prompt, - bytes aigcData, - string uri, + bytes indexed aigcData, bytes proof ); /** - * @dev Mint token at `tokenId` given `to`, `prompt`, `aigcData`, `uri`, and `proof`. `proof` means that we input the ZK proof when using zkML and byte zero when using opML as the verification method. - * - * Requirements: - * - `tokenId` must not exist.' - * - verify(`prompt`, `aigcData`, `proof`) must return true. - * - * Optional: - * - `proof` should not include `aigcData` to save gas. + * @dev Add AIGC data to token at `tokenId` given `prompt`, `aigcData`, and `proof`. */ - function mint( - address to, + function addAigcData( + uint256 tokenId, bytes calldata prompt, bytes calldata aigcData, - string calldata uri, bytes calldata proof - ) external returns (uint256 tokenId); + ) external; /** * @dev Verify the `prompt`, `aigcData`, and `proof`. @@ -129,6 +121,7 @@ pragma solidity ^0.8.18; /** * @title ERC7007 Token Standard, optional updatable extension + * Note: the ERC-165 identifier for this interface is 0x3f37dce2. */ interface IERC7007Updatable is IERC7007 { /** @@ -136,8 +129,7 @@ interface IERC7007Updatable is IERC7007 { */ function update( bytes calldata prompt, - bytes calldata aigcData, - string calldata uri + bytes calldata aigcData ) external; /** @@ -146,8 +138,7 @@ interface IERC7007Updatable is IERC7007 { event Update( uint256 indexed tokenId, bytes indexed prompt, - bytes indexed aigcData, - string uri + bytes indexed aigcData ); } ``` @@ -156,44 +147,44 @@ interface IERC7007Updatable is IERC7007 { ```json { - "title": "AIGC Metadata", - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Identifies the asset to which this NFT represents" - }, - "description": { - "type": "string", - "description": "Describes the asset to which this NFT represents" - }, - "image": { - "type": "string", - "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." - }, - "prompt": { - "type": "string", - "description": "Identifies the prompt from which this AIGC NFT generated" - }, - "aigc_type": { - "type": "string", - "description": "image/video/audio..." - }, - "aigc_data": { - "type": "string", - "description": "A URI pointing to a resource with mime type image/* representing the asset to which this AIGC NFT represents." - }, - "proof_type": { - "type": "string", - "description": "validity (zkML) or fraud (opML)" - } + "title": "AIGC Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this NFT represents" + }, + "description": { + "type": "string", + "description": "Describes the asset to which this NFT represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." + }, + "prompt": { + "type": "string", + "description": "Identifies the prompt from which this AIGC NFT generated" + }, + "aigc_type": { + "type": "string", + "description": "image/video/audio..." + }, + "aigc_data": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which this AIGC NFT represents." + }, + "proof_type": { + "type": "string", + "description": "validity (zkML) or fraud (opML)" } + } } ``` ### ML Model Publication -While this standard does not describe the Machine Learning model publication stage, it is natural and recommended to publish the commitment of the Model to Ethereum separately, before any actual `mint` actions. The model commitment schema choice lies on the AIGC-NFT project issuer party. The commitment should be checked inside the implementation of the `verify` function. +While this standard does not describe the Machine Learning model publication stage, it is natural and recommended to publish the commitment of the Model to Ethereum separately, before any actual `addAigcData` actions. The model commitment schema choice lies on the AIGC-NFT project issuer party. The commitment should be checked inside the implementation of the `verify` function. ## Rationale @@ -203,15 +194,15 @@ This specification sets the `tokenId` to be the hash of its corresponding `promp ### Generalization to Different Proof Types -This specification accommodates two proof types: validity proofs for zkML and fraud proofs for opML. Function arguments in `mint` and `verify` are designed for generality, allowing for compatibility with both proof systems. Moreover, the specification includes an updatable extension that specifically serves the requirements of opML. +This specification accommodates two proof types: validity proofs for zkML and fraud proofs for opML. Function arguments in `addAigcData` and `verify` are designed for generality, allowing for compatibility with both proof systems. Moreover, the specification includes an updatable extension that specifically serves the requirements of opML. ### `verify` interface -We specify a `verify` interface to enforce the correctness of `aigcData`. It is defined as a view function to reduce gas cost. `verify` should return true if and only if `aigcData` is finalized in both zkML and opML. In zkML, it must verify the ZK proof, i.e. `proof`; in opML, it must make sure that the challenging period is finalized, and that the `aigcData` is up-to-date, i.e. has been updated after finalization. Additionally, `proof` can be *empty* in opML. +We specify a `verify` interface to enforce the correctness of `aigcData`. It is defined as a view function to reduce gas cost. `verify` should return true if and only if `aigcData` is finalized in both zkML and opML. In zkML, it must verify the ZK proof, i.e. `proof`; in opML, it must make sure that the challenging period is finalized, and that the `aigcData` is up-to-date, i.e. has been updated after finalization. Additionally, `proof` can be _empty_ in opML. -### `mint` interface +### `addAigcData` interface -We specify a `mint` interface to bind the prompt and `aigcData` with `tokenId`. Notably, it acts differently in zkML and opML cases. In zkML, `mint` should make sure `verify` returns `true`. While in opML, it can be called before finalization. The consideration here is that, limited by the proving difficulty, zkML usually targets simple model inference tasks in practice, making it possible to provide a proof within an acceptable time frame. On the other hand, opML enables large model inference tasks, with a cost of longer confirmation time to achieve the approximate same security level. Mint until opML finalization may not be the best practice considering the existing optimistic protocols. +We specify an `addAigcData` interface to bind the prompt and `aigcData` with `tokenId`. This function provides flexibility for different minting implementations. Notably, it acts differently in zkML and opML cases. In zkML, `addAigcData` should make sure `verify` returns `true`. While in opML, it can be called before finalization. The consideration here is that, limited by the proving difficulty, zkML usually targets simple model inference tasks in practice, making it possible to provide a proof within an acceptable time frame. On the other hand, opML enables large model inference tasks, with a cost of longer confirmation time to achieve the approximate same security level. Mint until opML finalization may not be the best practice considering the existing optimistic protocols. ### Naming Choice on `update` @@ -227,8 +218,8 @@ The reference implementation includes sample implementations of the [ERC-7007](. ## Reference Implementation -* ERC-7007 for [zkML](../assets/eip-7007/contracts/ERC7007Zkml.sol) and [opML](../assets/eip-7007/contracts/ERC7007Opml.sol) -* [ERC-7007 Enumerable Extension](../assets/eip-7007/contracts/ERC7007Enumerable.sol) +- ERC-7007 for [zkML](../assets/eip-7007/contracts/ERC7007Zkml.sol) and [opML](../assets/eip-7007/contracts/ERC7007Opml.sol) +- [ERC-7007 Enumerable Extension](../assets/eip-7007/contracts/ERC7007Enumerable.sol) ## Security Considerations @@ -242,4 +233,4 @@ In the opML scenario, it is important to consider that the `aigcData` might chan ## Copyright -Copyright and related rights waived via [CC0](../LICENSE.md). +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/assets/erc-7007/contracts/ERC7007Enumerable.sol b/assets/erc-7007/contracts/ERC7007Enumerable.sol index fe096209df..0271ab23a8 100644 --- a/assets/erc-7007/contracts/ERC7007Enumerable.sol +++ b/assets/erc-7007/contracts/ERC7007Enumerable.sol @@ -29,17 +29,14 @@ abstract contract ERC7007Enumerable is ERC7007Zkml, IERC7007Enumerable { interfaceId == type(IERC7007Enumerable).interfaceId || super.supportsInterface(interfaceId); } - - /** - * @dev See {IERC7007-mint}. - */ + function mint( address to, bytes calldata prompt_, bytes calldata aigcData, string calldata uri, bytes calldata proof - ) public virtual override(ERC7007Zkml, IERC7007) returns (uint256 tokenId_) { + ) public virtual override returns (uint256 tokenId_) { tokenId_ = ERC7007Zkml.mint(to, prompt_, aigcData, uri, proof); prompt[tokenId_] = string(prompt_); tokenId[prompt_] = tokenId_; diff --git a/assets/erc-7007/contracts/ERC7007Opml.sol b/assets/erc-7007/contracts/ERC7007Opml.sol index 40b8f12ebe..b6164d27a0 100644 --- a/assets/erc-7007/contracts/ERC7007Opml.sol +++ b/assets/erc-7007/contracts/ERC7007Opml.sol @@ -23,17 +23,14 @@ contract ERC7007Opml is ERC165, IERC7007Updatable, ERC721URIStorage { ) ERC721(name_, symbol_) { opmlLib = opmlLib_; } - - /** - * @dev See {IERC7007-mint}. - */ + function mint( address to, bytes calldata prompt, bytes calldata aigcData, string calldata uri, bytes calldata proof - ) public virtual override returns (uint256 tokenId) { + ) public returns (uint256 tokenId) { tokenId = uint256(keccak256(prompt)); _safeMint(to, tokenId); string memory tokenUri = string( @@ -48,11 +45,23 @@ contract ERC7007Opml is ERC165, IERC7007Updatable, ERC721URIStorage { ) ); _setTokenURI(tokenId, tokenUri); - + addAigcData(tokenId, prompt, aigcData, proof); + } + + /** + * @dev See {IERC7007-addAigcData}. + */ + function addAigcData( + uint256 tokenId, + bytes calldata prompt, + bytes calldata aigcData, + bytes calldata proof + ) public virtual override { + require(ownerOf(tokenId) != address(0), "ERC7007: nonexistent token"); + require(tokenIdToRequestId[tokenId] == 0, "ERC7007: requestId already exists"); tokenIdToRequestId[tokenId] = IOpmlLib(opmlLib).initOpmlRequest(prompt); IOpmlLib(opmlLib).uploadResult(tokenIdToRequestId[tokenId], aigcData); - - emit Mint(to, tokenId, prompt, aigcData, uri, proof); + emit AigcData(tokenId, prompt, aigcData, proof); } /** @@ -74,25 +83,13 @@ contract ERC7007Opml is ERC165, IERC7007Updatable, ERC721URIStorage { */ function update( bytes calldata prompt, - bytes calldata aigcData, - string calldata uri + bytes calldata aigcData ) public virtual override { require(verify(prompt, aigcData, prompt), "ERC7007: invalid aigcData"); // proof argument is not used in verify() function for opML, so we can pass prompt as proof uint256 tokenId = uint256(keccak256(prompt)); - string memory tokenUri = string( - abi.encodePacked( - "{", - uri, - ', "prompt": "', - string(prompt), - '", "aigc_data": "', - string(aigcData), - '"}' - ) - ); - require(keccak256(bytes(tokenUri)) != keccak256(bytes(tokenURI(tokenId))), "ERC7007: token uri is not changed"); - - emit Update(tokenId, prompt, aigcData, uri); + require(ownerOf(tokenId) != address(0), "ERC7007: nonexistent token"); + // TODO: should update tokenURI with new aigcData + emit Update(tokenId, prompt, aigcData); } /** diff --git a/assets/erc-7007/contracts/ERC7007Zkml.sol b/assets/erc-7007/contracts/ERC7007Zkml.sol index 2478115133..b8abf9978f 100644 --- a/assets/erc-7007/contracts/ERC7007Zkml.sol +++ b/assets/erc-7007/contracts/ERC7007Zkml.sol @@ -23,19 +23,17 @@ contract ERC7007Zkml is ERC165, IERC7007, ERC721URIStorage { verifier = verifier_; } - /** - * @dev See {IERC7007-mint}. - */ function mint( address to, bytes calldata prompt, bytes calldata aigcData, string calldata uri, bytes calldata proof - ) public virtual override returns (uint256 tokenId) { - require(verify(prompt, aigcData, proof), "ERC7007: invalid proof"); + ) public virtual returns (uint256 tokenId) { tokenId = uint256(keccak256(prompt)); _safeMint(to, tokenId); + addAigcData(tokenId, prompt, aigcData, proof); + string memory tokenUri = string( abi.encodePacked( "{", @@ -48,7 +46,20 @@ contract ERC7007Zkml is ERC165, IERC7007, ERC721URIStorage { ) ); _setTokenURI(tokenId, tokenUri); - emit Mint(to, tokenId, prompt, aigcData, uri, proof); + } + + /** + * @dev See {IERC7007-addAigcData}. + */ + function addAigcData( + uint256 tokenId, + bytes calldata prompt, + bytes calldata aigcData, + bytes calldata proof + ) public virtual override { + require(ownerOf(tokenId) != address(0), "ERC7007: nonexistent token"); + require(verify(prompt, aigcData, proof), "ERC7007: invalid proof"); + emit AigcData(tokenId, prompt, aigcData, proof); } /** diff --git a/assets/erc-7007/contracts/IERC7007.sol b/assets/erc-7007/contracts/IERC7007.sol index 6a86f49319..cdf800b6d1 100644 --- a/assets/erc-7007/contracts/IERC7007.sol +++ b/assets/erc-7007/contracts/IERC7007.sol @@ -9,34 +9,28 @@ import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; */ interface IERC7007 is IERC165, IERC721 { /** - * @dev Emitted when `tokenId` token is minted. + * @dev Emitted when AI Generated Content (AIGC) data is added to token at `tokenId`. */ - event Mint( - address indexed to, + event AigcData( uint256 indexed tokenId, bytes indexed prompt, bytes aigcData, - string uri, bytes proof ); /** - * @dev Mint token at `tokenId` given `to`, `prompt`, `aigcData`, `uri` and `proof`. `proof` means that we input the ZK proof when using zkML and byte zero when using opML as the verification method. - * - * Requirements: - * - `tokenId` must not exist.' - * - verify(`prompt`, `aigcData`, `proof`) must return true. + * @dev Add AIGC data to token at `tokenId` given `prompt`, `aigcData` and `proof`. * * Optional: * - `proof` should not include `aigcData` to save gas. + * - verify(`prompt`, `aigcData`, `proof`) should return true for zkML scenario. */ - function mint( - address to, + function addAigcData( + uint256 tokenId, bytes calldata prompt, bytes calldata aigcData, - string calldata uri, bytes calldata proof - ) external returns (uint256 tokenId); + ) external; /** * @dev Verify the `prompt`, `aigcData` and `proof`. diff --git a/assets/erc-7007/contracts/IERC7007Updatable.sol b/assets/erc-7007/contracts/IERC7007Updatable.sol index 1e1b0b11f8..bd9aa6bad9 100644 --- a/assets/erc-7007/contracts/IERC7007Updatable.sol +++ b/assets/erc-7007/contracts/IERC7007Updatable.sol @@ -12,8 +12,7 @@ interface IERC7007Updatable is IERC7007 { */ function update( bytes calldata prompt, - bytes calldata aigcData, - string calldata uri + bytes calldata aigcData ) external; /** @@ -22,7 +21,6 @@ interface IERC7007Updatable is IERC7007 { event Update( uint256 indexed tokenId, bytes indexed prompt, - bytes indexed aigcData, - string uri + bytes indexed aigcData ); } diff --git a/assets/erc-7007/package-lock.json b/assets/erc-7007/package-lock.json deleted file mode 100644 index db6776ab74..0000000000 --- a/assets/erc-7007/package-lock.json +++ /dev/null @@ -1,16534 +0,0 @@ -{ - "name": "erc7007", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "erc7007", - "version": "1.0.0", - "license": "ISC", - "devDependencies": { - "@nomicfoundation/hardhat-toolbox": "^2.0.2", - "@openzeppelin/contracts": "^4.8.3", - "hardhat": "^2.14.0" - } - }, - "node_modules/@chainsafe/as-sha256": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz", - "integrity": "sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==", - "dev": true - }, - "node_modules/@chainsafe/persistent-merkle-tree": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz", - "integrity": "sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==", - "dev": true, - "dependencies": { - "@chainsafe/as-sha256": "^0.3.1" - } - }, - "node_modules/@chainsafe/ssz": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.9.4.tgz", - "integrity": "sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==", - "dev": true, - "dependencies": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/persistent-merkle-tree": "^0.4.2", - "case": "^1.6.3" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@ethersproject/abi": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", - "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "node_modules/@ethersproject/abstract-provider": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", - "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0" - } - }, - "node_modules/@ethersproject/abstract-signer": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", - "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "node_modules/@ethersproject/address": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", - "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/rlp": "^5.7.0" - } - }, - "node_modules/@ethersproject/base64": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", - "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0" - } - }, - "node_modules/@ethersproject/basex": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", - "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "node_modules/@ethersproject/bignumber": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", - "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "bn.js": "^5.2.1" - } - }, - "node_modules/@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/constants": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", - "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bignumber": "^5.7.0" - } - }, - "node_modules/@ethersproject/contracts": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", - "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abi": "^5.7.0", - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0" - } - }, - "node_modules/@ethersproject/hash": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", - "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "node_modules/@ethersproject/hdnode": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", - "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, - "node_modules/@ethersproject/json-wallets": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", - "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "aes-js": "3.0.0", - "scrypt-js": "3.0.1" - } - }, - "node_modules/@ethersproject/keccak256": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", - "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "js-sha3": "0.8.0" - } - }, - "node_modules/@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ] - }, - "node_modules/@ethersproject/networks": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", - "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/pbkdf2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", - "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/sha2": "^5.7.0" - } - }, - "node_modules/@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/providers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", - "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0", - "bech32": "1.1.4", - "ws": "7.4.6" - } - }, - "node_modules/@ethersproject/providers/node_modules/ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@ethersproject/random": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", - "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/rlp": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", - "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/sha2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", - "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "hash.js": "1.1.7" - } - }, - "node_modules/@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "bn.js": "^5.2.1", - "elliptic": "6.5.4", - "hash.js": "1.1.7" - } - }, - "node_modules/@ethersproject/solidity": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", - "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "node_modules/@ethersproject/strings": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", - "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/transactions": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", - "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0" - } - }, - "node_modules/@ethersproject/units": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", - "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/wallet": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", - "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/json-wallets": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, - "node_modules/@ethersproject/web": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", - "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "node_modules/@ethersproject/wordlists": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", - "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true, - "peer": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@metamask/eth-sig-util": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", - "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", - "dev": true, - "dependencies": { - "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^6.2.1", - "ethjs-util": "^0.1.6", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@morgan-stanley/ts-mocking-bird": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@morgan-stanley/ts-mocking-bird/-/ts-mocking-bird-0.6.4.tgz", - "integrity": "sha512-57VJIflP8eR2xXa9cD1LUawh+Gh+BVQfVu0n6GALyg/AqV/Nz25kDRvws3i9kIe1PTrbsZZOYpsYp6bXPd6nVA==", - "dev": true, - "peer": true, - "dependencies": { - "lodash": "^4.17.16", - "uuid": "^7.0.3" - }, - "peerDependencies": { - "jasmine": "2.x || 3.x || 4.x", - "jest": "26.x || 27.x || 28.x", - "typescript": ">=4.2" - }, - "peerDependenciesMeta": { - "jasmine": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/@morgan-stanley/ts-mocking-bird/node_modules/uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "dev": true, - "peer": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@noble/hashes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", - "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/@noble/secp256k1": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", - "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "peer": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "peer": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nomicfoundation/ethereumjs-block": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.1.tgz", - "integrity": "sha512-u1Yioemi6Ckj3xspygu/SfFvm8vZEO8/Yx5a1QLzi6nVU0jz3Pg2OmHKJ5w+D9Ogk1vhwRiqEBAqcb0GVhCyHw==", - "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "ethereum-cryptography": "0.1.3", - "ethers": "^5.7.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-block/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-blockchain": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.1.tgz", - "integrity": "sha512-NhzndlGg829XXbqJEYrF1VeZhAwSPgsK/OB7TVrdzft3y918hW5KNd7gIZ85sn6peDZOdjBsAXIpXZ38oBYE5A==", - "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-ethash": "3.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "abstract-level": "^1.0.3", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "level": "^8.0.0", - "lru-cache": "^5.1.1", - "memory-level": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-blockchain/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-common": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.1.tgz", - "integrity": "sha512-OBErlkfp54GpeiE06brBW/TTbtbuBJV5YI5Nz/aB2evTDo+KawyEzPjBlSr84z/8MFfj8wS2wxzQX1o32cev5g==", - "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-util": "9.0.1", - "crc-32": "^1.2.0" - } - }, - "node_modules/@nomicfoundation/ethereumjs-ethash": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.1.tgz", - "integrity": "sha512-KDjGIB5igzWOp8Ik5I6QiRH5DH+XgILlplsHR7TEuWANZA759G6krQ6o8bvj+tRUz08YygMQu/sGd9mJ1DYT8w==", - "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "abstract-level": "^1.0.3", - "bigint-crypto-utils": "^3.0.23", - "ethereum-cryptography": "0.1.3" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-ethash/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-evm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.1.tgz", - "integrity": "sha512-oL8vJcnk0Bx/onl+TgQOQ1t/534GKFaEG17fZmwtPFeH8S5soiBYPCLUrvANOl4sCp9elYxIMzIiTtMtNNN8EQ==", - "dev": true, - "dependencies": { - "@ethersproject/providers": "^5.7.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "mcl-wasm": "^0.7.1", - "rustbn.js": "~0.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-evm/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-rlp": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.1.tgz", - "integrity": "sha512-xtxrMGa8kP4zF5ApBQBtjlSbN5E2HI8m8FYgVSYAnO6ssUoY5pVPGy2H8+xdf/bmMa22Ce8nWMH3aEW8CcqMeQ==", - "dev": true, - "bin": { - "rlp": "bin/rlp" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-statemanager": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.1.tgz", - "integrity": "sha512-B5ApMOnlruVOR7gisBaYwFX+L/AP7i/2oAahatssjPIBVDF6wTX1K7Qpa39E/nzsH8iYuL3krkYeUFIdO3EMUQ==", - "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "ethers": "^5.7.1", - "js-sdsl": "^4.1.4" - } - }, - "node_modules/@nomicfoundation/ethereumjs-statemanager/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-trie": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.1.tgz", - "integrity": "sha512-A64It/IMpDVODzCgxDgAAla8jNjNtsoQZIzZUfIV5AY6Coi4nvn7+VReBn5itlxMiL2yaTlQr9TRWp3CSI6VoA==", - "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "@types/readable-stream": "^2.3.13", - "ethereum-cryptography": "0.1.3", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-trie/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-tx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.1.tgz", - "integrity": "sha512-0HwxUF2u2hrsIM1fsasjXvlbDOq1ZHFV2dd1yGq8CA+MEYhaxZr8OTScpVkkxqMwBcc5y83FyPl0J9MZn3kY0w==", - "dev": true, - "dependencies": { - "@chainsafe/ssz": "^0.9.2", - "@ethersproject/providers": "^5.7.2", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "ethereum-cryptography": "0.1.3" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-util": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.1.tgz", - "integrity": "sha512-TwbhOWQ8QoSCFhV/DDfSmyfFIHjPjFBj957219+V3jTZYZ2rf9PmDtNOeZWAE3p3vlp8xb02XGpd0v6nTUPbsA==", - "dev": true, - "dependencies": { - "@chainsafe/ssz": "^0.10.0", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "ethereum-cryptography": "0.1.3" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/@chainsafe/persistent-merkle-tree": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.5.0.tgz", - "integrity": "sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw==", - "dev": true, - "dependencies": { - "@chainsafe/as-sha256": "^0.3.1" - } - }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/@chainsafe/ssz": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.10.2.tgz", - "integrity": "sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg==", - "dev": true, - "dependencies": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/persistent-merkle-tree": "^0.5.0" - } - }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-vm": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.1.tgz", - "integrity": "sha512-rArhyn0jPsS/D+ApFsz3yVJMQ29+pVzNZ0VJgkzAZ+7FqXSRtThl1C1prhmlVr3YNUlfpZ69Ak+RUT4g7VoOuQ==", - "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-blockchain": "7.0.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-evm": "2.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-statemanager": "2.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "mcl-wasm": "^0.7.1", - "rustbn.js": "~0.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-vm/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/hardhat-chai-matchers": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.6.tgz", - "integrity": "sha512-f5ZMNmabZeZegEfuxn/0kW+mm7+yV7VNDxLpMOMGXWFJ2l/Ct3QShujzDRF9cOkK9Ui/hbDeOWGZqyQALDXVCQ==", - "dev": true, - "peer": true, - "dependencies": { - "@ethersproject/abi": "^5.1.2", - "@types/chai-as-promised": "^7.1.3", - "chai-as-promised": "^7.1.1", - "deep-eql": "^4.0.1", - "ordinal": "^1.0.3" - }, - "peerDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.0", - "chai": "^4.2.0", - "ethers": "^5.0.0", - "hardhat": "^2.9.4" - } - }, - "node_modules/@nomicfoundation/hardhat-network-helpers": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.8.tgz", - "integrity": "sha512-MNqQbzUJZnCMIYvlniC3U+kcavz/PhhQSsY90tbEtUyMj/IQqsLwIRZa4ctjABh3Bz0KCh9OXUZ7Yk/d9hr45Q==", - "dev": true, - "peer": true, - "dependencies": { - "ethereumjs-util": "^7.1.4" - }, - "peerDependencies": { - "hardhat": "^2.9.5" - } - }, - "node_modules/@nomicfoundation/hardhat-network-helpers/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/hardhat-network-helpers/node_modules/ethereumjs-util": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", - "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", - "dev": true, - "peer": true, - "dependencies": { - "@types/bn.js": "^5.1.0", - "bn.js": "^5.1.2", - "create-hash": "^1.1.2", - "ethereum-cryptography": "^0.1.3", - "rlp": "^2.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@nomicfoundation/hardhat-toolbox": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-2.0.2.tgz", - "integrity": "sha512-vnN1AzxbvpSx9pfdRHbUzTRIXpMLPXnUlkW855VaDk6N1pwRaQ2gNzEmFAABk4lWf11E00PKwFd/q27HuwYrYg==", - "dev": true, - "peerDependencies": { - "@ethersproject/abi": "^5.4.7", - "@ethersproject/providers": "^5.4.7", - "@nomicfoundation/hardhat-chai-matchers": "^1.0.0", - "@nomicfoundation/hardhat-network-helpers": "^1.0.0", - "@nomiclabs/hardhat-ethers": "^2.0.0", - "@nomiclabs/hardhat-etherscan": "^3.0.0", - "@typechain/ethers-v5": "^10.1.0", - "@typechain/hardhat": "^6.1.2", - "@types/chai": "^4.2.0", - "@types/mocha": ">=9.1.0", - "@types/node": ">=12.0.0", - "chai": "^4.2.0", - "ethers": "^5.4.7", - "hardhat": "^2.11.0", - "hardhat-gas-reporter": "^1.0.8", - "solidity-coverage": "^0.8.1", - "ts-node": ">=8.0.0", - "typechain": "^8.1.0", - "typescript": ">=4.5.0" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz", - "integrity": "sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg==", - "dev": true, - "engines": { - "node": ">= 12" - }, - "optionalDependencies": { - "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.1", - "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.1", - "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.1", - "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.1.1", - "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.1.1", - "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.1" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz", - "integrity": "sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.1.tgz", - "integrity": "sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-freebsd-x64": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.1.tgz", - "integrity": "sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.1.tgz", - "integrity": "sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.1.tgz", - "integrity": "sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.1.tgz", - "integrity": "sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.1.tgz", - "integrity": "sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-win32-arm64-msvc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.1.tgz", - "integrity": "sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-win32-ia32-msvc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.1.tgz", - "integrity": "sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.1.tgz", - "integrity": "sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nomiclabs/hardhat-ethers": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz", - "integrity": "sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==", - "dev": true, - "peer": true, - "peerDependencies": { - "ethers": "^5.0.0", - "hardhat": "^2.0.0" - } - }, - "node_modules/@nomiclabs/hardhat-etherscan": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.7.tgz", - "integrity": "sha512-tZ3TvSgpvsQ6B6OGmo1/Au6u8BrAkvs1mIC/eURA3xgIfznUZBhmpne8hv7BXUzw9xNL3fXdpOYgOQlVMTcoHQ==", - "dev": true, - "peer": true, - "dependencies": { - "@ethersproject/abi": "^5.1.2", - "@ethersproject/address": "^5.0.2", - "cbor": "^8.1.0", - "chalk": "^2.4.2", - "debug": "^4.1.1", - "fs-extra": "^7.0.1", - "lodash": "^4.17.11", - "semver": "^6.3.0", - "table": "^6.8.0", - "undici": "^5.14.0" - }, - "peerDependencies": { - "hardhat": "^2.0.4" - } - }, - "node_modules/@openzeppelin/contracts": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.8.3.tgz", - "integrity": "sha512-bQHV8R9Me8IaJoJ2vPG4rXcL7seB7YVuskr4f+f5RyOStSZetwzkWtoqDMl5erkBJy0lDRUnIR2WIkPiC0GJlg==", - "dev": true - }, - "node_modules/@scure/base": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/@scure/bip32": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", - "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "@noble/hashes": "~1.2.0", - "@noble/secp256k1": "~1.7.0", - "@scure/base": "~1.1.0" - } - }, - "node_modules/@scure/bip39": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", - "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "@noble/hashes": "~1.2.0", - "@scure/base": "~1.1.0" - } - }, - "node_modules/@sentry/core": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", - "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", - "dev": true, - "dependencies": { - "@sentry/hub": "5.30.0", - "@sentry/minimal": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/hub": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", - "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", - "dev": true, - "dependencies": { - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/minimal": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", - "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", - "dev": true, - "dependencies": { - "@sentry/hub": "5.30.0", - "@sentry/types": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/node": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", - "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", - "dev": true, - "dependencies": { - "@sentry/core": "5.30.0", - "@sentry/hub": "5.30.0", - "@sentry/tracing": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "cookie": "^0.4.1", - "https-proxy-agent": "^5.0.0", - "lru_map": "^0.3.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/tracing": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", - "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", - "dev": true, - "dependencies": { - "@sentry/hub": "5.30.0", - "@sentry/minimal": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/types": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", - "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/utils": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", - "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", - "dev": true, - "dependencies": { - "@sentry/types": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@solidity-parser/parser": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", - "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", - "dev": true, - "peer": true, - "dependencies": { - "antlr4ts": "^0.5.0-alpha.4" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true, - "peer": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "peer": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "peer": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true, - "peer": true - }, - "node_modules/@typechain/ethers-v5": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.2.0.tgz", - "integrity": "sha512-ikaq0N/w9fABM+G01OFmU3U3dNnyRwEahkdvi9mqy1a3XwKiPZaF/lu54OcNaEWnpvEYyhhS0N7buCtLQqC92w==", - "dev": true, - "peer": true, - "dependencies": { - "lodash": "^4.17.15", - "ts-essentials": "^7.0.1" - }, - "peerDependencies": { - "@ethersproject/abi": "^5.0.0", - "@ethersproject/bytes": "^5.0.0", - "@ethersproject/providers": "^5.0.0", - "ethers": "^5.1.3", - "typechain": "^8.1.1", - "typescript": ">=4.3.0" - } - }, - "node_modules/@typechain/hardhat": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-6.1.5.tgz", - "integrity": "sha512-lg7LW4qDZpxFMknp3Xool61Fg6Lays8F8TXdFGBG+MxyYcYU5795P1U2XdStuzGq9S2Dzdgh+1jGww9wvZ6r4Q==", - "dev": true, - "peer": true, - "dependencies": { - "fs-extra": "^9.1.0" - }, - "peerDependencies": { - "@ethersproject/abi": "^5.4.7", - "@ethersproject/providers": "^5.4.7", - "@typechain/ethers-v5": "^10.2.0", - "ethers": "^5.4.7", - "hardhat": "^2.9.9", - "typechain": "^8.1.1" - } - }, - "node_modules/@typechain/hardhat/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "peer": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typechain/hardhat/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "peer": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@typechain/hardhat/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", - "dev": true, - "peer": true - }, - "node_modules/@types/chai-as-promised": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz", - "integrity": "sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/chai": "*" - } - }, - "node_modules/@types/concat-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", - "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", - "dev": true, - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/form-data": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", - "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", - "dev": true, - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "peer": true, - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "node_modules/@types/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", - "dev": true - }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "peer": true - }, - "node_modules/@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", - "dev": true, - "peer": true - }, - "node_modules/@types/node": { - "version": "18.16.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.3.tgz", - "integrity": "sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==", - "dev": true - }, - "node_modules/@types/pbkdf2": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", - "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true, - "peer": true - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true, - "peer": true - }, - "node_modules/@types/readable-stream": { - "version": "2.3.15", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz", - "integrity": "sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "safe-buffer": "~5.1.1" - } - }, - "node_modules/@types/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", - "dev": true, - "peer": true - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/abstract-level": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", - "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "catering": "^2.1.0", - "is-buffer": "^2.0.5", - "level-supports": "^4.0.0", - "level-transcoder": "^1.0.1", - "module-error": "^1.0.1", - "queue-microtask": "^1.2.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", - "dev": true, - "engines": { - "node": ">=0.3.0" - } - }, - "node_modules/aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", - "dev": true - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "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, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=0.4.2" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/antlr4ts": { - "version": "0.5.0-alpha.4", - "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", - "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", - "dev": true, - "peer": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "peer": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array.prototype.reduce": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", - "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "peer": true - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "peer": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", - "dev": true, - "peer": true - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "peer": true - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true, - "peer": true - }, - "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 - }, - "node_modules/base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "peer": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/bcrypt-pbkdf/node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "peer": true - }, - "node_modules/bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", - "dev": true - }, - "node_modules/bigint-crypto-utils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.2.2.tgz", - "integrity": "sha512-U1RbE3aX9ayCUVcIPHuPDPKcK3SFOXf93J1UK/iHlJuQB7bhagPIX06/CLpLEsDThJ7KA4Dhrnzynl+d2weTiw==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/blakejs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", - "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", - "dev": true - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true - }, - "node_modules/browser-level": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", - "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", - "dev": true, - "dependencies": { - "abstract-level": "^1.0.2", - "catering": "^2.1.1", - "module-error": "^1.0.2", - "run-parallel-limit": "^1.1.0" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "dev": true, - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "dev": true, - "dependencies": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dev": true, - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/case": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", - "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true, - "peer": true - }, - "node_modules/catering": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", - "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cbor": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", - "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", - "dev": true, - "peer": true, - "dependencies": { - "nofilter": "^3.1.0" - }, - "engines": { - "node": ">=12.19" - } - }, - "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", - "dev": true, - "peer": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", - "dev": true, - "peer": true, - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 5" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/classic-level": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.3.0.tgz", - "integrity": "sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "abstract-level": "^1.0.2", - "catering": "^2.1.0", - "module-error": "^1.0.1", - "napi-macros": "^2.2.2", - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=12" - } - }, - "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, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-table3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", - "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", - "dev": true, - "peer": true, - "dependencies": { - "object-assign": "^4.1.0", - "string-width": "^2.1.1" - }, - "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "colors": "^1.1.2" - } - }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "peer": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "peer": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/command-exists": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", - "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", - "dev": true - }, - "node_modules/command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", - "dev": true, - "peer": true, - "dependencies": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", - "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-usage": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", - "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", - "dev": true, - "peer": true, - "dependencies": { - "array-back": "^4.0.2", - "chalk": "^2.4.2", - "table-layout": "^1.0.2", - "typical": "^5.2.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/command-line-usage/node_modules/array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/command-line-usage/node_modules/typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/commander": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "peer": true, - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "peer": true - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "peer": true - }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/death": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", - "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", - "dev": true, - "peer": true - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "peer": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "peer": true - }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "peer": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/detect-port": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", - "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", - "dev": true, - "peer": true, - "dependencies": { - "address": "^1.0.1", - "debug": "4" - }, - "bin": { - "detect": "bin/detect-port.js", - "detect-port": "bin/detect-port.js" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/difflib": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", - "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", - "dev": true, - "peer": true, - "dependencies": { - "heap": ">= 0.2.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "peer": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "peer": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "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 - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "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, - "engines": { - "node": ">=6" - } - }, - "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "peer": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-abstract/node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true, - "peer": true - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "peer": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "peer": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", - "dev": true, - "peer": true, - "dependencies": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=0.12.0" - }, - "optionalDependencies": { - "source-map": "~0.2.0" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", - "dev": true, - "peer": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eth-gas-reporter": { - "version": "0.2.25", - "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz", - "integrity": "sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ==", - "dev": true, - "peer": true, - "dependencies": { - "@ethersproject/abi": "^5.0.0-beta.146", - "@solidity-parser/parser": "^0.14.0", - "cli-table3": "^0.5.0", - "colors": "1.4.0", - "ethereum-cryptography": "^1.0.3", - "ethers": "^4.0.40", - "fs-readdir-recursive": "^1.1.0", - "lodash": "^4.17.14", - "markdown-table": "^1.1.3", - "mocha": "^7.1.1", - "req-cwd": "^2.0.0", - "request": "^2.88.0", - "request-promise-native": "^1.0.5", - "sha1": "^1.1.1", - "sync-request": "^6.0.0" - }, - "peerDependencies": { - "@codechecks/client": "^0.1.0" - }, - "peerDependenciesMeta": { - "@codechecks/client": { - "optional": true - } - } - }, - "node_modules/eth-gas-reporter/node_modules/ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "peer": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/eth-gas-reporter/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true - }, - "node_modules/eth-gas-reporter/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "peer": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.1.1" - } - }, - "node_modules/eth-gas-reporter/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "peer": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/eth-gas-reporter/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dev": true, - "peer": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eth-gas-reporter/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eth-gas-reporter/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/eth-gas-reporter/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true, - "peer": true - }, - "node_modules/eth-gas-reporter/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "peer": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eth-gas-reporter/node_modules/ethers": { - "version": "4.0.49", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.49.tgz", - "integrity": "sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==", - "dev": true, - "peer": true, - "dependencies": { - "aes-js": "3.0.0", - "bn.js": "^4.11.9", - "elliptic": "6.5.4", - "hash.js": "1.1.3", - "js-sha3": "0.5.7", - "scrypt-js": "2.0.4", - "setimmediate": "1.0.4", - "uuid": "2.0.1", - "xmlhttprequest": "1.8.0" - } - }, - "node_modules/eth-gas-reporter/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "peer": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", - "dev": true, - "peer": true, - "dependencies": { - "is-buffer": "~2.0.3" - }, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/eth-gas-reporter/node_modules/fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "deprecated": "\"Please update to latest v2.3 or v2.2\"", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/eth-gas-reporter/node_modules/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eth-gas-reporter/node_modules/hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "peer": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/eth-gas-reporter/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eth-gas-reporter/node_modules/js-sha3": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==", - "dev": true, - "peer": true - }, - "node_modules/eth-gas-reporter/node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "peer": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eth-gas-reporter/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "peer": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "peer": true, - "dependencies": { - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eth-gas-reporter/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eth-gas-reporter/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "peer": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/eth-gas-reporter/node_modules/mocha": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", - "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/eth-gas-reporter/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true, - "peer": true - }, - "node_modules/eth-gas-reporter/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "peer": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eth-gas-reporter/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "peer": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "peer": true, - "dependencies": { - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/eth-gas-reporter/node_modules/scrypt-js": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", - "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==", - "dev": true, - "peer": true - }, - "node_modules/eth-gas-reporter/node_modules/setimmediate": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", - "integrity": "sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog==", - "dev": true, - "peer": true - }, - "node_modules/eth-gas-reporter/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "peer": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eth-gas-reporter/node_modules/supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/uuid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", - "integrity": "sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "peer": true - }, - "node_modules/eth-gas-reporter/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eth-gas-reporter/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "peer": true - }, - "node_modules/eth-gas-reporter/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "peer": true, - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/eth-gas-reporter/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "peer": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/eth-gas-reporter/node_modules/yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "peer": true, - "dependencies": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ethereum-bloom-filters": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", - "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", - "dev": true, - "peer": true, - "dependencies": { - "js-sha3": "^0.8.0" - } - }, - "node_modules/ethereum-cryptography": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", - "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", - "dev": true, - "dependencies": { - "@noble/hashes": "1.2.0", - "@noble/secp256k1": "1.7.1", - "@scure/bip32": "1.1.5", - "@scure/bip39": "1.1.1" - } - }, - "node_modules/ethereumjs-abi": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", - "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", - "dev": true, - "dependencies": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" - } - }, - "node_modules/ethereumjs-abi/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/ethereumjs-util": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", - "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", - "dev": true, - "dependencies": { - "@types/bn.js": "^4.11.3", - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "0.1.6", - "rlp": "^2.2.3" - } - }, - "node_modules/ethereumjs-util/node_modules/@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/ethereumjs-util/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" - } - }, - "node_modules/ethjs-unit": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", - "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", - "dev": true, - "peer": true, - "dependencies": { - "bn.js": "4.11.6", - "number-to-bn": "1.7.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/ethjs-unit/node_modules/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", - "dev": true, - "peer": true - }, - "node_modules/ethjs-util": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", - "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", - "dev": true, - "dependencies": { - "is-hex-prefixed": "1.0.0", - "strip-hex-prefix": "1.0.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true, - "peer": true - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "peer": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "peer": true - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "peer": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "peer": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "peer": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "peer": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "dev": true, - "peer": true, - "dependencies": { - "array-back": "^3.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "peer": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "peer": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/fp-ts": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", - "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true, - "peer": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-port": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", - "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/ghost-testrpc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", - "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", - "dev": true, - "peer": true, - "dependencies": { - "chalk": "^2.4.2", - "node-emoji": "^1.10.0" - }, - "bin": { - "testrpc-sc": "index.js" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "peer": true, - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "peer": true, - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "peer": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", - "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", - "dev": true, - "peer": true, - "dependencies": { - "@types/glob": "^7.1.1", - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "peer": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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 - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "peer": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "peer": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/hardhat": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.14.0.tgz", - "integrity": "sha512-73jsInY4zZahMSVFurSK+5TNCJTXMv+vemvGia0Ac34Mm19fYp6vEPVGF3sucbumszsYxiTT2TbS8Ii2dsDSoQ==", - "dev": true, - "dependencies": { - "@ethersproject/abi": "^5.1.2", - "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-blockchain": "7.0.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-evm": "2.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-statemanager": "2.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "@nomicfoundation/ethereumjs-vm": "7.0.1", - "@nomicfoundation/solidity-analyzer": "^0.1.0", - "@sentry/node": "^5.18.1", - "@types/bn.js": "^5.1.0", - "@types/lru-cache": "^5.1.0", - "abort-controller": "^3.0.0", - "adm-zip": "^0.4.16", - "aggregate-error": "^3.0.0", - "ansi-escapes": "^4.3.0", - "chalk": "^2.4.2", - "chokidar": "^3.4.0", - "ci-info": "^2.0.0", - "debug": "^4.1.1", - "enquirer": "^2.3.0", - "env-paths": "^2.2.0", - "ethereum-cryptography": "^1.0.3", - "ethereumjs-abi": "^0.6.8", - "find-up": "^2.1.0", - "fp-ts": "1.19.3", - "fs-extra": "^7.0.1", - "glob": "7.2.0", - "immutable": "^4.0.0-rc.12", - "io-ts": "1.10.4", - "keccak": "^3.0.2", - "lodash": "^4.17.11", - "mnemonist": "^0.38.0", - "mocha": "^10.0.0", - "p-map": "^4.0.0", - "qs": "^6.7.0", - "raw-body": "^2.4.1", - "resolve": "1.17.0", - "semver": "^6.3.0", - "solc": "0.7.3", - "source-map-support": "^0.5.13", - "stacktrace-parser": "^0.1.10", - "tsort": "0.0.1", - "undici": "^5.14.0", - "uuid": "^8.3.2", - "ws": "^7.4.6" - }, - "bin": { - "hardhat": "internal/cli/bootstrap.js" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "ts-node": "*", - "typescript": "*" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/hardhat-gas-reporter": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz", - "integrity": "sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==", - "dev": true, - "peer": true, - "dependencies": { - "array-uniq": "1.0.3", - "eth-gas-reporter": "^0.2.25", - "sha1": "^1.1.1" - }, - "peerDependencies": { - "hardhat": "^2.0.2" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "peer": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "peer": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash-base/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", - "dev": true, - "peer": true - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "dev": true, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/http-basic": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", - "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", - "dev": true, - "peer": true, - "dependencies": { - "caseless": "^0.12.0", - "concat-stream": "^1.6.2", - "http-response-object": "^3.0.1", - "parse-cache-control": "^1.0.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-response-object": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", - "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", - "dev": true, - "peer": true, - "dependencies": { - "@types/node": "^10.0.3" - } - }, - "node_modules/http-response-object/node_modules/@types/node": { - "version": "10.17.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", - "dev": true, - "peer": true - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/immutable": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", - "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", - "dev": true - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "peer": true - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "peer": true, - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/io-ts": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", - "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", - "dev": true, - "dependencies": { - "fp-ts": "^1.0.0" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "peer": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "peer": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hex-prefixed": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", - "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", - "dev": true, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "peer": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "peer": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "peer": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "peer": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "peer": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "peer": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "peer": true - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true, - "peer": true - }, - "node_modules/js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true, - "peer": true - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "peer": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "peer": true - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonschema": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", - "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "peer": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/keccak": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.3.tgz", - "integrity": "sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.9" - } - }, - "node_modules/level": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", - "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", - "dev": true, - "dependencies": { - "browser-level": "^1.0.1", - "classic-level": "^1.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/level" - } - }, - "node_modules/level-supports": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", - "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/level-transcoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", - "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "module-error": "^1.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "peer": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, - "peer": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true, - "peer": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/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 - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", - "dev": true, - "peer": true, - "dependencies": { - "get-func-name": "^2.0.0" - } - }, - "node_modules/lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "peer": true - }, - "node_modules/markdown-table": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", - "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", - "dev": true, - "peer": true - }, - "node_modules/mcl-wasm": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", - "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==", - "dev": true, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/memory-level": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", - "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", - "dev": true, - "dependencies": { - "abstract-level": "^1.0.0", - "functional-red-black-tree": "^1.0.1", - "module-error": "^1.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "peer": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "peer": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "peer": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mnemonist": { - "version": "0.38.5", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", - "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", - "dev": true, - "dependencies": { - "obliterator": "^2.0.0" - } - }, - "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/mocha/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, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/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 - }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/module-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", - "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/napi-macros": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", - "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true - }, - "node_modules/node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", - "dev": true - }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "peer": true, - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "peer": true, - "dependencies": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "node_modules/node-environment-flags/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "peer": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "dev": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/nofilter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", - "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=12.19" - } - }, - "node_modules/nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", - "dev": true, - "peer": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/number-to-bn": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", - "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", - "dev": true, - "peer": true, - "dependencies": { - "bn.js": "4.11.6", - "strip-hex-prefix": "1.0.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/number-to-bn/node_modules/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", - "dev": true, - "peer": true - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "peer": true, - "dependencies": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz", - "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==", - "dev": true, - "peer": true, - "dependencies": { - "array.prototype.reduce": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.21.2", - "safe-array-concat": "^1.0.0" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", - "dev": true - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "peer": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ordinal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", - "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", - "dev": true, - "peer": true - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "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, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/parse-cache-control": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", - "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", - "dev": true, - "peer": true - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true, - "peer": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "peer": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "peer": true - }, - "node_modules/promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "dev": true, - "peer": true, - "dependencies": { - "asap": "~2.0.6" - } - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true, - "peer": true - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", - "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "peer": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "dev": true, - "peer": true, - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/reduce-flatten": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", - "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/req-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", - "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", - "dev": true, - "peer": true, - "dependencies": { - "req-from": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/req-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", - "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", - "dev": true, - "peer": true, - "dependencies": { - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "peer": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "dev": true, - "peer": true, - "dependencies": { - "lodash": "^4.17.19" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" - } - }, - "node_modules/request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", - "dev": true, - "peer": true, - "dependencies": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "engines": { - "node": ">=0.12.0" - }, - "peerDependencies": { - "request": "^2.34" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "peer": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true, - "peer": true - }, - "node_modules/resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "dependencies": { - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "peer": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/rlp": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", - "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", - "dev": true, - "dependencies": { - "bn.js": "^5.2.0" - }, - "bin": { - "rlp": "bin/rlp" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true, - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/run-parallel-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", - "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rustbn.js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", - "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==", - "dev": true - }, - "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "peer": true - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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 - }, - "node_modules/sc-istanbul": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", - "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", - "dev": true, - "peer": true, - "dependencies": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "istanbul": "lib/cli.js" - } - }, - "node_modules/sc-istanbul/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "peer": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/sc-istanbul/node_modules/glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", - "dev": true, - "peer": true, - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/sc-istanbul/node_modules/has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sc-istanbul/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "peer": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/sc-istanbul/node_modules/js-yaml/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "peer": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/sc-istanbul/node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", - "dev": true, - "peer": true - }, - "node_modules/sc-istanbul/node_modules/supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^1.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", - "dev": true - }, - "node_modules/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "elliptic": "^6.5.4", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "peer": true - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/sha1": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", - "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", - "dev": true, - "peer": true, - "dependencies": { - "charenc": ">= 0.0.1", - "crypt": ">= 0.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "peer": true, - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/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, - "peer": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/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, - "peer": true - }, - "node_modules/solc": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz", - "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==", - "dev": true, - "dependencies": { - "command-exists": "^1.2.8", - "commander": "3.0.2", - "follow-redirects": "^1.12.1", - "fs-extra": "^0.30.0", - "js-sha3": "0.8.0", - "memorystream": "^0.3.1", - "require-from-string": "^2.0.0", - "semver": "^5.5.0", - "tmp": "0.0.33" - }, - "bin": { - "solcjs": "solcjs" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/solc/node_modules/fs-extra": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", - "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0", - "path-is-absolute": "^1.0.0", - "rimraf": "^2.2.8" - } - }, - "node_modules/solc/node_modules/jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/solc/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/solidity-coverage": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.2.tgz", - "integrity": "sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==", - "dev": true, - "peer": true, - "dependencies": { - "@ethersproject/abi": "^5.0.9", - "@solidity-parser/parser": "^0.14.1", - "chalk": "^2.4.2", - "death": "^1.1.0", - "detect-port": "^1.3.0", - "difflib": "^0.2.4", - "fs-extra": "^8.1.0", - "ghost-testrpc": "^0.0.2", - "global-modules": "^2.0.0", - "globby": "^10.0.1", - "jsonschema": "^1.2.4", - "lodash": "^4.17.15", - "mocha": "7.1.2", - "node-emoji": "^1.10.0", - "pify": "^4.0.1", - "recursive-readdir": "^2.2.2", - "sc-istanbul": "^0.4.5", - "semver": "^7.3.4", - "shelljs": "^0.8.3", - "web3-utils": "^1.3.6" - }, - "bin": { - "solidity-coverage": "plugins/bin.js" - }, - "peerDependencies": { - "hardhat": "^2.11.0" - } - }, - "node_modules/solidity-coverage/node_modules/ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "peer": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/solidity-coverage/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "peer": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.1.1" - } - }, - "node_modules/solidity-coverage/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "peer": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/solidity-coverage/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dev": true, - "peer": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/solidity-coverage/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/solidity-coverage/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/solidity-coverage/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true, - "peer": true - }, - "node_modules/solidity-coverage/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "peer": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/solidity-coverage/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "peer": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", - "dev": true, - "peer": true, - "dependencies": { - "is-buffer": "~2.0.3" - }, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/solidity-coverage/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/solidity-coverage/node_modules/fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "deprecated": "\"Please update to latest v2.3 or v2.2\"", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/solidity-coverage/node_modules/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/solidity-coverage/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/solidity-coverage/node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "peer": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/solidity-coverage/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "peer": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "peer": true, - "dependencies": { - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/solidity-coverage/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/solidity-coverage/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/solidity-coverage/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "peer": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/solidity-coverage/node_modules/mocha": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", - "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/solidity-coverage/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true, - "peer": true - }, - "node_modules/solidity-coverage/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "peer": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/solidity-coverage/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "peer": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "peer": true, - "dependencies": { - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/solidity-coverage/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", - "dev": true, - "peer": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/solidity-coverage/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "peer": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/solidity-coverage/node_modules/supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "peer": true - }, - "node_modules/solidity-coverage/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, - "peer": true - }, - "node_modules/solidity-coverage/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "peer": true, - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/solidity-coverage/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "peer": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/solidity-coverage/node_modules/yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "peer": true, - "dependencies": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "peer": true - }, - "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "dev": true, - "peer": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sshpk/node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "peer": true - }, - "node_modules/stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", - "dev": true, - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/string-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", - "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", - "dev": true, - "peer": true - }, - "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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-hex-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", - "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", - "dev": true, - "dependencies": { - "is-hex-prefixed": "1.0.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/sync-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", - "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", - "dev": true, - "peer": true, - "dependencies": { - "http-response-object": "^3.0.1", - "sync-rpc": "^1.2.1", - "then-request": "^6.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/sync-rpc": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", - "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", - "dev": true, - "peer": true, - "dependencies": { - "get-port": "^3.1.0" - } - }, - "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "peer": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table-layout": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", - "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", - "dev": true, - "peer": true, - "dependencies": { - "array-back": "^4.0.1", - "deep-extend": "~0.6.0", - "typical": "^5.2.0", - "wordwrapjs": "^4.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/table-layout/node_modules/array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/table-layout/node_modules/typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "peer": true - }, - "node_modules/then-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", - "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", - "dev": true, - "peer": true, - "dependencies": { - "@types/concat-stream": "^1.6.0", - "@types/form-data": "0.0.33", - "@types/node": "^8.0.0", - "@types/qs": "^6.2.31", - "caseless": "~0.12.0", - "concat-stream": "^1.6.0", - "form-data": "^2.2.0", - "http-basic": "^8.1.1", - "http-response-object": "^3.0.1", - "promise": "^8.0.0", - "qs": "^6.4.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/then-request/node_modules/@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", - "dev": true, - "peer": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "peer": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/ts-command-line-args": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.0.tgz", - "integrity": "sha512-Ff7Xt04WWCjj/cmPO9eWTJX3qpBZWuPWyQYG1vnxJao+alWWYjwJBc5aYz3h5p5dE08A6AnpkgiCtP/0KXXBYw==", - "dev": true, - "peer": true, - "dependencies": { - "@morgan-stanley/ts-mocking-bird": "^0.6.2", - "chalk": "^4.1.0", - "command-line-args": "^5.1.1", - "command-line-usage": "^6.1.0", - "string-format": "^2.0.0" - }, - "bin": { - "write-markdown": "dist/write-markdown.js" - } - }, - "node_modules/ts-command-line-args/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ts-command-line-args/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ts-command-line-args/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, - "peer": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ts-command-line-args/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, - "peer": true - }, - "node_modules/ts-command-line-args/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-command-line-args/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-essentials": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", - "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", - "dev": true, - "peer": true, - "peerDependencies": { - "typescript": ">=3.7.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "peer": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsort": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", - "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", - "dev": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "peer": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "dev": true - }, - "node_modules/tweetnacl-util": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "peer": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typechain": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.1.1.tgz", - "integrity": "sha512-uF/sUvnXTOVF2FHKhQYnxHk4su4JjZR8vr4mA2mBaRwHTbwh0jIlqARz9XJr1tA0l7afJGvEa1dTSi4zt039LQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/prettier": "^2.1.1", - "debug": "^4.3.1", - "fs-extra": "^7.0.0", - "glob": "7.1.7", - "js-sha3": "^0.8.0", - "lodash": "^4.17.15", - "mkdirp": "^1.0.4", - "prettier": "^2.3.1", - "ts-command-line-args": "^2.2.0", - "ts-essentials": "^7.0.1" - }, - "bin": { - "typechain": "dist/cli/cli.js" - }, - "peerDependencies": { - "typescript": ">=4.3.0" - } - }, - "node_modules/typechain/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typechain/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "peer": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true, - "peer": true - }, - "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, - "node_modules/typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz", - "integrity": "sha512-fR9RXCc+6Dxav4P9VV/sp5w3eFiSdOjJYsbtWfd4s5L5C4ogyuVpdKIVHeW0vV1MloM65/f7W45nR9ZxwVdyiA==", - "dev": true, - "dependencies": { - "busboy": "^1.6.0" - }, - "engines": { - "node": ">=14.0" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", - "dev": true, - "peer": true - }, - "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 - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "peer": true - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/web3-utils": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.9.0.tgz", - "integrity": "sha512-p++69rCNNfu2jM9n5+VD/g26l+qkEOQ1m6cfRQCbH8ZRrtquTmrirJMgTmyOoax5a5XRYOuws14aypCOs51pdQ==", - "dev": true, - "peer": true, - "dependencies": { - "bn.js": "^5.2.1", - "ethereum-bloom-filters": "^1.0.6", - "ethereumjs-util": "^7.1.0", - "ethjs-unit": "0.1.6", - "number-to-bn": "1.7.0", - "randombytes": "^2.1.0", - "utf8": "3.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-utils/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/web3-utils/node_modules/ethereumjs-util": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", - "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", - "dev": true, - "peer": true, - "dependencies": { - "@types/bn.js": "^5.1.0", - "bn.js": "^5.1.2", - "create-hash": "^1.1.2", - "ethereum-cryptography": "^0.1.3", - "rlp": "^2.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "peer": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true, - "peer": true - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "peer": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "peer": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "peer": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "peer": true - }, - "node_modules/wordwrapjs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", - "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", - "dev": true, - "peer": true, - "dependencies": { - "reduce-flatten": "^2.0.0", - "typical": "^5.2.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/wordwrapjs/node_modules/typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "node_modules/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, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/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 - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@chainsafe/as-sha256": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz", - "integrity": "sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==", - "dev": true - }, - "@chainsafe/persistent-merkle-tree": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz", - "integrity": "sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==", - "dev": true, - "requires": { - "@chainsafe/as-sha256": "^0.3.1" - } - }, - "@chainsafe/ssz": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.9.4.tgz", - "integrity": "sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==", - "dev": true, - "requires": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/persistent-merkle-tree": "^0.4.2", - "case": "^1.6.3" - } - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@ethersproject/abi": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", - "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", - "dev": true, - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/abstract-provider": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", - "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0" - } - }, - "@ethersproject/abstract-signer": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", - "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", - "dev": true, - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "@ethersproject/address": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", - "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/rlp": "^5.7.0" - } - }, - "@ethersproject/base64": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", - "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0" - } - }, - "@ethersproject/basex": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", - "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "@ethersproject/bignumber": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", - "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "bn.js": "^5.2.1" - } - }, - "@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", - "dev": true, - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/constants": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", - "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0" - } - }, - "@ethersproject/contracts": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", - "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", - "dev": true, - "requires": { - "@ethersproject/abi": "^5.7.0", - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0" - } - }, - "@ethersproject/hash": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", - "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", - "dev": true, - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/hdnode": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", - "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", - "dev": true, - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, - "@ethersproject/json-wallets": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", - "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", - "dev": true, - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "aes-js": "3.0.0", - "scrypt-js": "3.0.1" - } - }, - "@ethersproject/keccak256": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", - "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "js-sha3": "0.8.0" - } - }, - "@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", - "dev": true - }, - "@ethersproject/networks": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", - "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", - "dev": true, - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/pbkdf2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", - "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/sha2": "^5.7.0" - } - }, - "@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", - "dev": true, - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/providers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", - "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", - "dev": true, - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0", - "bech32": "1.1.4", - "ws": "7.4.6" - }, - "dependencies": { - "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true, - "requires": {} - } - } - }, - "@ethersproject/random": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", - "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/rlp": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", - "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/sha2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", - "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "hash.js": "1.1.7" - } - }, - "@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "bn.js": "^5.2.1", - "elliptic": "6.5.4", - "hash.js": "1.1.7" - } - }, - "@ethersproject/solidity": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", - "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/strings": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", - "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/transactions": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", - "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", - "dev": true, - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0" - } - }, - "@ethersproject/units": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", - "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/wallet": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", - "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", - "dev": true, - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/json-wallets": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, - "@ethersproject/web": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", - "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", - "dev": true, - "requires": { - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/wordlists": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", - "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "peer": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true, - "peer": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@metamask/eth-sig-util": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", - "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", - "dev": true, - "requires": { - "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^6.2.1", - "ethjs-util": "^0.1.6", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1" - } - }, - "@morgan-stanley/ts-mocking-bird": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@morgan-stanley/ts-mocking-bird/-/ts-mocking-bird-0.6.4.tgz", - "integrity": "sha512-57VJIflP8eR2xXa9cD1LUawh+Gh+BVQfVu0n6GALyg/AqV/Nz25kDRvws3i9kIe1PTrbsZZOYpsYp6bXPd6nVA==", - "dev": true, - "peer": true, - "requires": { - "lodash": "^4.17.16", - "uuid": "^7.0.3" - }, - "dependencies": { - "uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "dev": true, - "peer": true - } - } - }, - "@noble/hashes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", - "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", - "dev": true - }, - "@noble/secp256k1": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", - "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", - "dev": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "peer": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "peer": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "peer": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@nomicfoundation/ethereumjs-block": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.1.tgz", - "integrity": "sha512-u1Yioemi6Ckj3xspygu/SfFvm8vZEO8/Yx5a1QLzi6nVU0jz3Pg2OmHKJ5w+D9Ogk1vhwRiqEBAqcb0GVhCyHw==", - "dev": true, - "requires": { - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "ethereum-cryptography": "0.1.3", - "ethers": "^5.7.1" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/ethereumjs-blockchain": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.1.tgz", - "integrity": "sha512-NhzndlGg829XXbqJEYrF1VeZhAwSPgsK/OB7TVrdzft3y918hW5KNd7gIZ85sn6peDZOdjBsAXIpXZ38oBYE5A==", - "dev": true, - "requires": { - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-ethash": "3.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "abstract-level": "^1.0.3", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "level": "^8.0.0", - "lru-cache": "^5.1.1", - "memory-level": "^1.0.0" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/ethereumjs-common": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.1.tgz", - "integrity": "sha512-OBErlkfp54GpeiE06brBW/TTbtbuBJV5YI5Nz/aB2evTDo+KawyEzPjBlSr84z/8MFfj8wS2wxzQX1o32cev5g==", - "dev": true, - "requires": { - "@nomicfoundation/ethereumjs-util": "9.0.1", - "crc-32": "^1.2.0" - } - }, - "@nomicfoundation/ethereumjs-ethash": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.1.tgz", - "integrity": "sha512-KDjGIB5igzWOp8Ik5I6QiRH5DH+XgILlplsHR7TEuWANZA759G6krQ6o8bvj+tRUz08YygMQu/sGd9mJ1DYT8w==", - "dev": true, - "requires": { - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "abstract-level": "^1.0.3", - "bigint-crypto-utils": "^3.0.23", - "ethereum-cryptography": "0.1.3" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/ethereumjs-evm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.1.tgz", - "integrity": "sha512-oL8vJcnk0Bx/onl+TgQOQ1t/534GKFaEG17fZmwtPFeH8S5soiBYPCLUrvANOl4sCp9elYxIMzIiTtMtNNN8EQ==", - "dev": true, - "requires": { - "@ethersproject/providers": "^5.7.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "mcl-wasm": "^0.7.1", - "rustbn.js": "~0.2.0" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/ethereumjs-rlp": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.1.tgz", - "integrity": "sha512-xtxrMGa8kP4zF5ApBQBtjlSbN5E2HI8m8FYgVSYAnO6ssUoY5pVPGy2H8+xdf/bmMa22Ce8nWMH3aEW8CcqMeQ==", - "dev": true - }, - "@nomicfoundation/ethereumjs-statemanager": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.1.tgz", - "integrity": "sha512-B5ApMOnlruVOR7gisBaYwFX+L/AP7i/2oAahatssjPIBVDF6wTX1K7Qpa39E/nzsH8iYuL3krkYeUFIdO3EMUQ==", - "dev": true, - "requires": { - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "ethers": "^5.7.1", - "js-sdsl": "^4.1.4" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/ethereumjs-trie": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.1.tgz", - "integrity": "sha512-A64It/IMpDVODzCgxDgAAla8jNjNtsoQZIzZUfIV5AY6Coi4nvn7+VReBn5itlxMiL2yaTlQr9TRWp3CSI6VoA==", - "dev": true, - "requires": { - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "@types/readable-stream": "^2.3.13", - "ethereum-cryptography": "0.1.3", - "readable-stream": "^3.6.0" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/ethereumjs-tx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.1.tgz", - "integrity": "sha512-0HwxUF2u2hrsIM1fsasjXvlbDOq1ZHFV2dd1yGq8CA+MEYhaxZr8OTScpVkkxqMwBcc5y83FyPl0J9MZn3kY0w==", - "dev": true, - "requires": { - "@chainsafe/ssz": "^0.9.2", - "@ethersproject/providers": "^5.7.2", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "ethereum-cryptography": "0.1.3" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/ethereumjs-util": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.1.tgz", - "integrity": "sha512-TwbhOWQ8QoSCFhV/DDfSmyfFIHjPjFBj957219+V3jTZYZ2rf9PmDtNOeZWAE3p3vlp8xb02XGpd0v6nTUPbsA==", - "dev": true, - "requires": { - "@chainsafe/ssz": "^0.10.0", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "ethereum-cryptography": "0.1.3" - }, - "dependencies": { - "@chainsafe/persistent-merkle-tree": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.5.0.tgz", - "integrity": "sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw==", - "dev": true, - "requires": { - "@chainsafe/as-sha256": "^0.3.1" - } - }, - "@chainsafe/ssz": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.10.2.tgz", - "integrity": "sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg==", - "dev": true, - "requires": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/persistent-merkle-tree": "^0.5.0" - } - }, - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/ethereumjs-vm": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.1.tgz", - "integrity": "sha512-rArhyn0jPsS/D+ApFsz3yVJMQ29+pVzNZ0VJgkzAZ+7FqXSRtThl1C1prhmlVr3YNUlfpZ69Ak+RUT4g7VoOuQ==", - "dev": true, - "requires": { - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-blockchain": "7.0.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-evm": "2.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-statemanager": "2.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "mcl-wasm": "^0.7.1", - "rustbn.js": "~0.2.0" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "@nomicfoundation/hardhat-chai-matchers": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.6.tgz", - "integrity": "sha512-f5ZMNmabZeZegEfuxn/0kW+mm7+yV7VNDxLpMOMGXWFJ2l/Ct3QShujzDRF9cOkK9Ui/hbDeOWGZqyQALDXVCQ==", - "dev": true, - "peer": true, - "requires": { - "@ethersproject/abi": "^5.1.2", - "@types/chai-as-promised": "^7.1.3", - "chai-as-promised": "^7.1.1", - "deep-eql": "^4.0.1", - "ordinal": "^1.0.3" - } - }, - "@nomicfoundation/hardhat-network-helpers": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.8.tgz", - "integrity": "sha512-MNqQbzUJZnCMIYvlniC3U+kcavz/PhhQSsY90tbEtUyMj/IQqsLwIRZa4ctjABh3Bz0KCh9OXUZ7Yk/d9hr45Q==", - "dev": true, - "peer": true, - "requires": { - "ethereumjs-util": "^7.1.4" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "ethereumjs-util": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", - "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", - "dev": true, - "peer": true, - "requires": { - "@types/bn.js": "^5.1.0", - "bn.js": "^5.1.2", - "create-hash": "^1.1.2", - "ethereum-cryptography": "^0.1.3", - "rlp": "^2.2.4" - } - } - } - }, - "@nomicfoundation/hardhat-toolbox": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-2.0.2.tgz", - "integrity": "sha512-vnN1AzxbvpSx9pfdRHbUzTRIXpMLPXnUlkW855VaDk6N1pwRaQ2gNzEmFAABk4lWf11E00PKwFd/q27HuwYrYg==", - "dev": true, - "requires": {} - }, - "@nomicfoundation/solidity-analyzer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz", - "integrity": "sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg==", - "dev": true, - "requires": { - "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.1", - "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.1", - "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.1", - "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.1.1", - "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.1.1", - "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.1" - } - }, - "@nomicfoundation/solidity-analyzer-darwin-arm64": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz", - "integrity": "sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w==", - "dev": true, - "optional": true - }, - "@nomicfoundation/solidity-analyzer-darwin-x64": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.1.tgz", - "integrity": "sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA==", - "dev": true, - "optional": true - }, - "@nomicfoundation/solidity-analyzer-freebsd-x64": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.1.tgz", - "integrity": "sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA==", - "dev": true, - "optional": true - }, - "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.1.tgz", - "integrity": "sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg==", - "dev": true, - "optional": true - }, - "@nomicfoundation/solidity-analyzer-linux-arm64-musl": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.1.tgz", - "integrity": "sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w==", - "dev": true, - "optional": true - }, - "@nomicfoundation/solidity-analyzer-linux-x64-gnu": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.1.tgz", - "integrity": "sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA==", - "dev": true, - "optional": true - }, - "@nomicfoundation/solidity-analyzer-linux-x64-musl": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.1.tgz", - "integrity": "sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w==", - "dev": true, - "optional": true - }, - "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.1.tgz", - "integrity": "sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg==", - "dev": true, - "optional": true - }, - "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.1.tgz", - "integrity": "sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ==", - "dev": true, - "optional": true - }, - "@nomicfoundation/solidity-analyzer-win32-x64-msvc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.1.tgz", - "integrity": "sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw==", - "dev": true, - "optional": true - }, - "@nomiclabs/hardhat-ethers": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz", - "integrity": "sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==", - "dev": true, - "peer": true, - "requires": {} - }, - "@nomiclabs/hardhat-etherscan": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.7.tgz", - "integrity": "sha512-tZ3TvSgpvsQ6B6OGmo1/Au6u8BrAkvs1mIC/eURA3xgIfznUZBhmpne8hv7BXUzw9xNL3fXdpOYgOQlVMTcoHQ==", - "dev": true, - "peer": true, - "requires": { - "@ethersproject/abi": "^5.1.2", - "@ethersproject/address": "^5.0.2", - "cbor": "^8.1.0", - "chalk": "^2.4.2", - "debug": "^4.1.1", - "fs-extra": "^7.0.1", - "lodash": "^4.17.11", - "semver": "^6.3.0", - "table": "^6.8.0", - "undici": "^5.14.0" - } - }, - "@openzeppelin/contracts": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.8.3.tgz", - "integrity": "sha512-bQHV8R9Me8IaJoJ2vPG4rXcL7seB7YVuskr4f+f5RyOStSZetwzkWtoqDMl5erkBJy0lDRUnIR2WIkPiC0GJlg==", - "dev": true - }, - "@scure/base": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", - "dev": true - }, - "@scure/bip32": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", - "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", - "dev": true, - "requires": { - "@noble/hashes": "~1.2.0", - "@noble/secp256k1": "~1.7.0", - "@scure/base": "~1.1.0" - } - }, - "@scure/bip39": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", - "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", - "dev": true, - "requires": { - "@noble/hashes": "~1.2.0", - "@scure/base": "~1.1.0" - } - }, - "@sentry/core": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", - "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", - "dev": true, - "requires": { - "@sentry/hub": "5.30.0", - "@sentry/minimal": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - } - }, - "@sentry/hub": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", - "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", - "dev": true, - "requires": { - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - } - }, - "@sentry/minimal": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", - "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", - "dev": true, - "requires": { - "@sentry/hub": "5.30.0", - "@sentry/types": "5.30.0", - "tslib": "^1.9.3" - } - }, - "@sentry/node": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", - "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", - "dev": true, - "requires": { - "@sentry/core": "5.30.0", - "@sentry/hub": "5.30.0", - "@sentry/tracing": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "cookie": "^0.4.1", - "https-proxy-agent": "^5.0.0", - "lru_map": "^0.3.3", - "tslib": "^1.9.3" - } - }, - "@sentry/tracing": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", - "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", - "dev": true, - "requires": { - "@sentry/hub": "5.30.0", - "@sentry/minimal": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - } - }, - "@sentry/types": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", - "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", - "dev": true - }, - "@sentry/utils": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", - "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", - "dev": true, - "requires": { - "@sentry/types": "5.30.0", - "tslib": "^1.9.3" - } - }, - "@solidity-parser/parser": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", - "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", - "dev": true, - "peer": true, - "requires": { - "antlr4ts": "^0.5.0-alpha.4" - } - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true, - "peer": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "peer": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "peer": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true, - "peer": true - }, - "@typechain/ethers-v5": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.2.0.tgz", - "integrity": "sha512-ikaq0N/w9fABM+G01OFmU3U3dNnyRwEahkdvi9mqy1a3XwKiPZaF/lu54OcNaEWnpvEYyhhS0N7buCtLQqC92w==", - "dev": true, - "peer": true, - "requires": { - "lodash": "^4.17.15", - "ts-essentials": "^7.0.1" - } - }, - "@typechain/hardhat": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-6.1.5.tgz", - "integrity": "sha512-lg7LW4qDZpxFMknp3Xool61Fg6Lays8F8TXdFGBG+MxyYcYU5795P1U2XdStuzGq9S2Dzdgh+1jGww9wvZ6r4Q==", - "dev": true, - "peer": true, - "requires": { - "fs-extra": "^9.1.0" - }, - "dependencies": { - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "peer": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "peer": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "peer": true - } - } - }, - "@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", - "dev": true, - "peer": true - }, - "@types/chai-as-promised": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz", - "integrity": "sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==", - "dev": true, - "peer": true, - "requires": { - "@types/chai": "*" - } - }, - "@types/concat-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", - "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", - "dev": true, - "peer": true, - "requires": { - "@types/node": "*" - } - }, - "@types/form-data": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", - "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", - "dev": true, - "peer": true, - "requires": { - "@types/node": "*" - } - }, - "@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "peer": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", - "dev": true - }, - "@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "peer": true - }, - "@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", - "dev": true, - "peer": true - }, - "@types/node": { - "version": "18.16.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.3.tgz", - "integrity": "sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==", - "dev": true - }, - "@types/pbkdf2": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", - "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true, - "peer": true - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true, - "peer": true - }, - "@types/readable-stream": { - "version": "2.3.15", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz", - "integrity": "sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==", - "dev": true, - "requires": { - "@types/node": "*", - "safe-buffer": "~5.1.1" - } - }, - "@types/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", - "dev": true, - "peer": true - }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "abstract-level": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", - "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", - "dev": true, - "requires": { - "buffer": "^6.0.3", - "catering": "^2.1.0", - "is-buffer": "^2.0.5", - "level-supports": "^4.0.0", - "level-transcoder": "^1.0.1", - "module-error": "^1.0.1", - "queue-microtask": "^1.2.3" - } - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "peer": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "peer": true - }, - "address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "dev": true, - "peer": true - }, - "adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", - "dev": true - }, - "aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "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, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", - "dev": true, - "optional": true, - "peer": true - }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "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 - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "antlr4ts": { - "version": "0.5.0-alpha.4", - "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", - "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", - "dev": true, - "peer": true - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "peer": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "peer": true - }, - "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "peer": true - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true, - "peer": true - }, - "array.prototype.reduce": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", - "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - } - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "peer": true - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "peer": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "peer": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "peer": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "peer": true - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", - "dev": true, - "peer": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "peer": true - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "peer": true - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "peer": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "peer": true - }, - "aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true, - "peer": true - }, - "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 - }, - "base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "peer": true, - "requires": { - "tweetnacl": "^0.14.3" - }, - "dependencies": { - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "peer": true - } - } - }, - "bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", - "dev": true - }, - "bigint-crypto-utils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.2.2.tgz", - "integrity": "sha512-U1RbE3aX9ayCUVcIPHuPDPKcK3SFOXf93J1UK/iHlJuQB7bhagPIX06/CLpLEsDThJ7KA4Dhrnzynl+d2weTiw==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "blakejs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", - "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", - "dev": true - }, - "bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true - }, - "browser-level": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", - "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", - "dev": true, - "requires": { - "abstract-level": "^1.0.2", - "catering": "^2.1.1", - "module-error": "^1.0.2", - "run-parallel-limit": "^1.1.0" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "dev": true, - "requires": { - "base-x": "^3.0.2" - } - }, - "bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "dev": true, - "requires": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true - }, - "busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dev": true, - "requires": { - "streamsearch": "^1.1.0" - } - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "case": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", - "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true, - "peer": true - }, - "catering": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", - "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", - "dev": true - }, - "cbor": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", - "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", - "dev": true, - "peer": true, - "requires": { - "nofilter": "^3.1.0" - } - }, - "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", - "dev": true, - "peer": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, - "chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", - "dev": true, - "peer": true, - "requires": { - "check-error": "^1.0.2" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "dev": true, - "peer": true - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true, - "peer": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "classic-level": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.3.0.tgz", - "integrity": "sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==", - "dev": true, - "requires": { - "abstract-level": "^1.0.2", - "catering": "^2.1.0", - "module-error": "^1.0.1", - "napi-macros": "^2.2.2", - "node-gyp-build": "^4.3.0" - } - }, - "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 - }, - "cli-table3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", - "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", - "dev": true, - "peer": true, - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "peer": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "peer": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "peer": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "peer": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "peer": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "command-exists": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", - "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", - "dev": true - }, - "command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", - "dev": true, - "peer": true, - "requires": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", - "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" - } - }, - "command-line-usage": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", - "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", - "dev": true, - "peer": true, - "requires": { - "array-back": "^4.0.2", - "chalk": "^2.4.2", - "table-layout": "^1.0.2", - "typical": "^5.2.0" - }, - "dependencies": { - "array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", - "dev": true, - "peer": true - }, - "typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "peer": true - } - } - }, - "commander": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "peer": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "peer": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "peer": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "peer": true - }, - "crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "peer": true - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "dev": true, - "peer": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "peer": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "death": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", - "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", - "dev": true, - "peer": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "peer": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "peer": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "peer": true - }, - "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "peer": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "peer": true - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "detect-port": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", - "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", - "dev": true, - "peer": true, - "requires": { - "address": "^1.0.1", - "debug": "4" - } - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "difflib": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", - "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", - "dev": true, - "peer": true, - "requires": { - "heap": ">= 0.2.0" - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "peer": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "peer": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "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 - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "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 - }, - "es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "peer": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "dependencies": { - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - } - } - }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true, - "peer": true - }, - "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "peer": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "peer": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", - "dev": true, - "peer": true, - "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" - }, - "dependencies": { - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", - "dev": true, - "peer": true - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", - "dev": true, - "peer": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "peer": true - }, - "eth-gas-reporter": { - "version": "0.2.25", - "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz", - "integrity": "sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ==", - "dev": true, - "peer": true, - "requires": { - "@ethersproject/abi": "^5.0.0-beta.146", - "@solidity-parser/parser": "^0.14.0", - "cli-table3": "^0.5.0", - "colors": "1.4.0", - "ethereum-cryptography": "^1.0.3", - "ethers": "^4.0.40", - "fs-readdir-recursive": "^1.1.0", - "lodash": "^4.17.14", - "markdown-table": "^1.1.3", - "mocha": "^7.1.1", - "req-cwd": "^2.0.0", - "request": "^2.88.0", - "request-promise-native": "^1.0.5", - "sha1": "^1.1.1", - "sync-request": "^6.0.0" - }, - "dependencies": { - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true, - "peer": true - }, - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "peer": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "peer": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "peer": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "peer": true - }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "peer": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "peer": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "peer": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "peer": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "peer": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true, - "peer": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "peer": true - }, - "ethers": { - "version": "4.0.49", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.49.tgz", - "integrity": "sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==", - "dev": true, - "peer": true, - "requires": { - "aes-js": "3.0.0", - "bn.js": "^4.11.9", - "elliptic": "6.5.4", - "hash.js": "1.1.3", - "js-sha3": "0.5.7", - "scrypt-js": "2.0.4", - "setimmediate": "1.0.4", - "uuid": "2.0.1", - "xmlhttprequest": "1.8.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "peer": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", - "dev": true, - "peer": true, - "requires": { - "is-buffer": "~2.0.3" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true, - "peer": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "peer": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "peer": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true - }, - "js-sha3": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==", - "dev": true, - "peer": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "peer": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "peer": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "peer": true, - "requires": { - "chalk": "^2.4.2" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "peer": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "peer": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mocha": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", - "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", - "dev": true, - "peer": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true, - "peer": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "peer": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "peer": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "peer": true - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "peer": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "scrypt-js": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", - "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==", - "dev": true, - "peer": true - }, - "setimmediate": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", - "integrity": "sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog==", - "dev": true, - "peer": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "peer": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "peer": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "peer": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "peer": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "uuid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", - "integrity": "sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg==", - "dev": true, - "peer": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "peer": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "peer": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "peer": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "peer": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "peer": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - } - } - } - }, - "ethereum-bloom-filters": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", - "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", - "dev": true, - "peer": true, - "requires": { - "js-sha3": "^0.8.0" - } - }, - "ethereum-cryptography": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", - "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", - "dev": true, - "requires": { - "@noble/hashes": "1.2.0", - "@noble/secp256k1": "1.7.1", - "@scure/bip32": "1.1.5", - "@scure/bip39": "1.1.1" - } - }, - "ethereumjs-abi": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", - "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", - "dev": true, - "requires": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "ethereumjs-util": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", - "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", - "dev": true, - "requires": { - "@types/bn.js": "^4.11.3", - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "0.1.6", - "rlp": "^2.2.3" - }, - "dependencies": { - "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - } - } - }, - "ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", - "dev": true, - "requires": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" - } - }, - "ethjs-unit": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", - "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", - "dev": true, - "peer": true, - "requires": { - "bn.js": "4.11.6", - "number-to-bn": "1.7.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", - "dev": true, - "peer": true - } - } - }, - "ethjs-util": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", - "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", - "dev": true, - "requires": { - "is-hex-prefixed": "1.0.0", - "strip-hex-prefix": "1.0.0" - } - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true, - "peer": true - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "peer": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "peer": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "peer": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "peer": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "peer": true - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "peer": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "dev": true, - "peer": true, - "requires": { - "array-back": "^3.0.1" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "dev": true - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "peer": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "peer": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "peer": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fp-ts": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", - "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", - "dev": true - }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true, - "peer": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "peer": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true, - "peer": true - }, - "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-port": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", - "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", - "dev": true, - "peer": true - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "peer": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "ghost-testrpc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", - "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", - "dev": true, - "peer": true, - "requires": { - "chalk": "^2.4.2", - "node-emoji": "^1.10.0" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "peer": true, - "requires": { - "global-prefix": "^3.0.0" - } - }, - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "peer": true, - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - } - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "peer": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "globby": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", - "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", - "dev": true, - "peer": true, - "requires": { - "@types/glob": "^7.1.1", - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", - "slash": "^3.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "peer": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "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 - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "peer": true - }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "peer": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "peer": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "peer": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "hardhat": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.14.0.tgz", - "integrity": "sha512-73jsInY4zZahMSVFurSK+5TNCJTXMv+vemvGia0Ac34Mm19fYp6vEPVGF3sucbumszsYxiTT2TbS8Ii2dsDSoQ==", - "dev": true, - "requires": { - "@ethersproject/abi": "^5.1.2", - "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-blockchain": "7.0.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-evm": "2.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-statemanager": "2.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "@nomicfoundation/ethereumjs-vm": "7.0.1", - "@nomicfoundation/solidity-analyzer": "^0.1.0", - "@sentry/node": "^5.18.1", - "@types/bn.js": "^5.1.0", - "@types/lru-cache": "^5.1.0", - "abort-controller": "^3.0.0", - "adm-zip": "^0.4.16", - "aggregate-error": "^3.0.0", - "ansi-escapes": "^4.3.0", - "chalk": "^2.4.2", - "chokidar": "^3.4.0", - "ci-info": "^2.0.0", - "debug": "^4.1.1", - "enquirer": "^2.3.0", - "env-paths": "^2.2.0", - "ethereum-cryptography": "^1.0.3", - "ethereumjs-abi": "^0.6.8", - "find-up": "^2.1.0", - "fp-ts": "1.19.3", - "fs-extra": "^7.0.1", - "glob": "7.2.0", - "immutable": "^4.0.0-rc.12", - "io-ts": "1.10.4", - "keccak": "^3.0.2", - "lodash": "^4.17.11", - "mnemonist": "^0.38.0", - "mocha": "^10.0.0", - "p-map": "^4.0.0", - "qs": "^6.7.0", - "raw-body": "^2.4.1", - "resolve": "1.17.0", - "semver": "^6.3.0", - "solc": "0.7.3", - "source-map-support": "^0.5.13", - "stacktrace-parser": "^0.1.10", - "tsort": "0.0.1", - "undici": "^5.14.0", - "uuid": "^8.3.2", - "ws": "^7.4.6" - } - }, - "hardhat-gas-reporter": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz", - "integrity": "sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==", - "dev": true, - "peer": true, - "requires": { - "array-uniq": "1.0.3", - "eth-gas-reporter": "^0.2.25", - "sha1": "^1.1.1" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "peer": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "peer": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "peer": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "peer": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", - "dev": true, - "peer": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "http-basic": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", - "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", - "dev": true, - "peer": true, - "requires": { - "caseless": "^0.12.0", - "concat-stream": "^1.6.2", - "http-response-object": "^3.0.1", - "parse-cache-control": "^1.0.1" - } - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-response-object": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", - "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", - "dev": true, - "peer": true, - "requires": { - "@types/node": "^10.0.3" - }, - "dependencies": { - "@types/node": { - "version": "10.17.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", - "dev": true, - "peer": true - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "peer": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "peer": true - }, - "immutable": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", - "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", - "dev": true - }, - "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 - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "peer": true - }, - "internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "peer": true, - "requires": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "peer": true - }, - "io-ts": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", - "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", - "dev": true, - "requires": { - "fp-ts": "^1.0.0" - } - }, - "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - } - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "peer": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "peer": true - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "peer": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "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 - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-hex-prefixed": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", - "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "peer": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "peer": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "peer": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "peer": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "peer": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "peer": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "peer": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "peer": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true, - "peer": true - }, - "js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "dev": true - }, - "js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true, - "peer": true - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "peer": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "peer": true - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonschema": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", - "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", - "dev": true, - "peer": true - }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "peer": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "keccak": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.3.tgz", - "integrity": "sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ==", - "dev": true, - "requires": { - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0", - "readable-stream": "^3.6.0" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "peer": true - }, - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.9" - } - }, - "level": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", - "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", - "dev": true, - "requires": { - "browser-level": "^1.0.1", - "classic-level": "^1.2.0" - } - }, - "level-supports": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", - "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", - "dev": true - }, - "level-transcoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", - "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", - "dev": true, - "requires": { - "buffer": "^6.0.3", - "module-error": "^1.0.1" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "peer": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, - "peer": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true, - "peer": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", - "dev": true, - "peer": true, - "requires": { - "get-func-name": "^2.0.0" - } - }, - "lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "peer": true - }, - "markdown-table": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", - "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", - "dev": true, - "peer": true - }, - "mcl-wasm": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", - "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==", - "dev": true - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "memory-level": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", - "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", - "dev": true, - "requires": { - "abstract-level": "^1.0.0", - "functional-red-black-tree": "^1.0.1", - "module-error": "^1.0.1" - } - }, - "memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "peer": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "peer": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "peer": true - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "peer": true, - "requires": { - "mime-db": "1.52.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "peer": true - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "peer": true, - "requires": { - "minimist": "^1.2.6" - } - }, - "mnemonist": { - "version": "0.38.5", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", - "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", - "dev": true, - "requires": { - "obliterator": "^2.0.0" - } - }, - "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "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, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "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 - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "module-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", - "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, - "napi-macros": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", - "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true - }, - "node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", - "dev": true - }, - "node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "peer": true, - "requires": { - "lodash": "^4.17.21" - } - }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "peer": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "peer": true - } - } - }, - "node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "dev": true - }, - "nofilter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", - "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", - "dev": true, - "peer": true - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", - "dev": true, - "peer": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "number-to-bn": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", - "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", - "dev": true, - "peer": true, - "requires": { - "bn.js": "4.11.6", - "strip-hex-prefix": "1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", - "dev": true, - "peer": true - } - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "peer": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "peer": true - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "peer": true - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "peer": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz", - "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==", - "dev": true, - "peer": true, - "requires": { - "array.prototype.reduce": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.21.2", - "safe-array-concat": "^1.0.0" - } - }, - "obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "peer": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "ordinal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", - "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", - "dev": true, - "peer": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "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, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true - }, - "parse-cache-control": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", - "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", - "dev": true, - "peer": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "peer": true - }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "peer": true - }, - "pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true, - "peer": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "peer": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "peer": true - }, - "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "peer": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "peer": true - }, - "promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "dev": true, - "peer": true, - "requires": { - "asap": "~2.0.6" - } - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true, - "peer": true - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "peer": true - }, - "qs": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", - "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "peer": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "dev": true, - "peer": true, - "requires": { - "minimatch": "^3.0.5" - } - }, - "reduce-flatten": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", - "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", - "dev": true, - "peer": true - }, - "regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - } - }, - "req-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", - "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", - "dev": true, - "peer": true, - "requires": { - "req-from": "^2.0.0" - } - }, - "req-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", - "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", - "dev": true, - "peer": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "peer": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "peer": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "peer": true - } - } - }, - "request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "dev": true, - "peer": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "dev": true, - "peer": true, - "requires": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true, - "peer": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", - "dev": true, - "peer": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "peer": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rlp": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", - "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", - "dev": true, - "requires": { - "bn.js": "^5.2.0" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "peer": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "run-parallel-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", - "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rustbn.js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", - "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==", - "dev": true - }, - "safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "peer": true - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sc-istanbul": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", - "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", - "dev": true, - "peer": true, - "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "peer": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", - "dev": true, - "peer": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", - "dev": true, - "peer": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "peer": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "peer": true - } - } - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", - "dev": true, - "peer": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", - "dev": true, - "peer": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", - "dev": true - }, - "secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", - "dev": true, - "requires": { - "elliptic": "^6.5.4", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "peer": true - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "sha1": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", - "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", - "dev": true, - "peer": true, - "requires": { - "charenc": ">= 0.0.1", - "crypt": ">= 0.0.1" - } - }, - "shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "peer": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "peer": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "peer": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "peer": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "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, - "peer": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "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, - "peer": true - } - } - }, - "solc": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz", - "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==", - "dev": true, - "requires": { - "command-exists": "^1.2.8", - "commander": "3.0.2", - "follow-redirects": "^1.12.1", - "fs-extra": "^0.30.0", - "js-sha3": "0.8.0", - "memorystream": "^0.3.1", - "require-from-string": "^2.0.0", - "semver": "^5.5.0", - "tmp": "0.0.33" - }, - "dependencies": { - "fs-extra": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", - "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0", - "path-is-absolute": "^1.0.0", - "rimraf": "^2.2.8" - } - }, - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "solidity-coverage": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.2.tgz", - "integrity": "sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==", - "dev": true, - "peer": true, - "requires": { - "@ethersproject/abi": "^5.0.9", - "@solidity-parser/parser": "^0.14.1", - "chalk": "^2.4.2", - "death": "^1.1.0", - "detect-port": "^1.3.0", - "difflib": "^0.2.4", - "fs-extra": "^8.1.0", - "ghost-testrpc": "^0.0.2", - "global-modules": "^2.0.0", - "globby": "^10.0.1", - "jsonschema": "^1.2.4", - "lodash": "^4.17.15", - "mocha": "7.1.2", - "node-emoji": "^1.10.0", - "pify": "^4.0.1", - "recursive-readdir": "^2.2.2", - "sc-istanbul": "^0.4.5", - "semver": "^7.3.4", - "shelljs": "^0.8.3", - "web3-utils": "^1.3.6" - }, - "dependencies": { - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true, - "peer": true - }, - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "peer": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "peer": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "peer": true - }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "peer": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "peer": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "peer": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "peer": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "peer": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true, - "peer": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "peer": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "peer": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", - "dev": true, - "peer": true, - "requires": { - "is-buffer": "~2.0.3" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "peer": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true, - "peer": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "peer": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "peer": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "peer": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "peer": true, - "requires": { - "chalk": "^2.4.2" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "peer": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "peer": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "peer": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mocha": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", - "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", - "dev": true, - "peer": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true, - "peer": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "peer": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "peer": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "peer": true - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "peer": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", - "dev": true, - "peer": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "peer": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "peer": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "peer": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "peer": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "peer": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "peer": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "peer": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "peer": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "peer": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "peer": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "peer": true - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "dev": true, - "peer": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "dependencies": { - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "peer": true - } - } - }, - "stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", - "dev": true, - "requires": { - "type-fest": "^0.7.1" - }, - "dependencies": { - "type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "dev": true - } - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", - "dev": true, - "peer": true - }, - "streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "string-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", - "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", - "dev": true, - "peer": true - }, - "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, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "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, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-hex-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", - "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", - "dev": true, - "requires": { - "is-hex-prefixed": "1.0.0" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "sync-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", - "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", - "dev": true, - "peer": true, - "requires": { - "http-response-object": "^3.0.1", - "sync-rpc": "^1.2.1", - "then-request": "^6.0.0" - } - }, - "sync-rpc": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", - "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", - "dev": true, - "peer": true, - "requires": { - "get-port": "^3.1.0" - } - }, - "table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "peer": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "peer": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "peer": true - } - } - }, - "table-layout": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", - "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", - "dev": true, - "peer": true, - "requires": { - "array-back": "^4.0.1", - "deep-extend": "~0.6.0", - "typical": "^5.2.0", - "wordwrapjs": "^4.0.0" - }, - "dependencies": { - "array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", - "dev": true, - "peer": true - }, - "typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "peer": true - } - } - }, - "then-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", - "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", - "dev": true, - "peer": true, - "requires": { - "@types/concat-stream": "^1.6.0", - "@types/form-data": "0.0.33", - "@types/node": "^8.0.0", - "@types/qs": "^6.2.31", - "caseless": "~0.12.0", - "concat-stream": "^1.6.0", - "form-data": "^2.2.0", - "http-basic": "^8.1.1", - "http-response-object": "^3.0.1", - "promise": "^8.0.0", - "qs": "^6.4.0" - }, - "dependencies": { - "@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", - "dev": true, - "peer": true - } - } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "peer": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "ts-command-line-args": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.0.tgz", - "integrity": "sha512-Ff7Xt04WWCjj/cmPO9eWTJX3qpBZWuPWyQYG1vnxJao+alWWYjwJBc5aYz3h5p5dE08A6AnpkgiCtP/0KXXBYw==", - "dev": true, - "peer": true, - "requires": { - "@morgan-stanley/ts-mocking-bird": "^0.6.2", - "chalk": "^4.1.0", - "command-line-args": "^5.1.1", - "command-line-usage": "^6.1.0", - "string-format": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "peer": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "peer": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "peer": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "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, - "peer": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "peer": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "ts-essentials": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", - "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", - "dev": true, - "peer": true, - "requires": {} - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "peer": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "peer": true - } - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsort": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", - "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "peer": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "dev": true - }, - "tweetnacl-util": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "peer": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "peer": true - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, - "typechain": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.1.1.tgz", - "integrity": "sha512-uF/sUvnXTOVF2FHKhQYnxHk4su4JjZR8vr4mA2mBaRwHTbwh0jIlqARz9XJr1tA0l7afJGvEa1dTSi4zt039LQ==", - "dev": true, - "peer": true, - "requires": { - "@types/prettier": "^2.1.1", - "debug": "^4.3.1", - "fs-extra": "^7.0.0", - "glob": "7.1.7", - "js-sha3": "^0.8.0", - "lodash": "^4.17.15", - "mkdirp": "^1.0.4", - "prettier": "^2.3.1", - "ts-command-line-args": "^2.2.0", - "ts-essentials": "^7.0.1" - }, - "dependencies": { - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "peer": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "peer": true - } - } - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true, - "peer": true - }, - "typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "peer": true - }, - "typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "dev": true, - "peer": true - }, - "uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "optional": true, - "peer": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "undici": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz", - "integrity": "sha512-fR9RXCc+6Dxav4P9VV/sp5w3eFiSdOjJYsbtWfd4s5L5C4ogyuVpdKIVHeW0vV1MloM65/f7W45nR9ZxwVdyiA==", - "dev": true, - "requires": { - "busboy": "^1.6.0" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "peer": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", - "dev": true, - "peer": true - }, - "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 - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "peer": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "peer": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "web3-utils": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.9.0.tgz", - "integrity": "sha512-p++69rCNNfu2jM9n5+VD/g26l+qkEOQ1m6cfRQCbH8ZRrtquTmrirJMgTmyOoax5a5XRYOuws14aypCOs51pdQ==", - "dev": true, - "peer": true, - "requires": { - "bn.js": "^5.2.1", - "ethereum-bloom-filters": "^1.0.6", - "ethereumjs-util": "^7.1.0", - "ethjs-unit": "0.1.6", - "number-to-bn": "1.7.0", - "randombytes": "^2.1.0", - "utf8": "3.0.0" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "peer": true, - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "ethereumjs-util": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", - "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", - "dev": true, - "peer": true, - "requires": { - "@types/bn.js": "^5.1.0", - "bn.js": "^5.1.2", - "create-hash": "^1.1.2", - "ethereum-cryptography": "^0.1.3", - "rlp": "^2.2.4" - } - } - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "peer": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "peer": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true, - "peer": true - }, - "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "peer": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "peer": true, - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "peer": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "peer": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "peer": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "peer": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "peer": true - }, - "wordwrapjs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", - "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", - "dev": true, - "peer": true, - "requires": { - "reduce-flatten": "^2.0.0", - "typical": "^5.2.0" - }, - "dependencies": { - "typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "peer": true - } - } - }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "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, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "requires": {} - }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==", - "dev": true, - "peer": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "peer": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/assets/erc-7007/test/test.js b/assets/erc-7007/test/test.js index 4728191951..9bfca6e04f 100644 --- a/assets/erc-7007/test/test.js +++ b/assets/erc-7007/test/test.js @@ -46,11 +46,11 @@ describe("ERC7007Zkml.sol", function () { await expect(erc7007.mint(owner.address, prompt, aigcData, uri, validProof)).to.be.revertedWith("ERC721: token already minted"); }); - it("should emit a Mint event", async function () { + it("should emit a AigcData event", async function () { const erc7007 = await deployERC7007Fixture(); const [owner] = await ethers.getSigners(); await expect(erc7007.mint(owner.address, prompt, aigcData, uri, validProof)) - .to.emit(erc7007, "Mint") + .to.emit(erc7007, "AigcData") }); }); @@ -130,11 +130,11 @@ describe("ERC7007Opml.sol", function () { await expect(erc7007.mint(owner.address, prompt, aigcData, uri, 0x00)).to.be.revertedWith("ERC721: token already minted"); }); - it("should emit a Mint event", async function () { + it("should emit a AigcData event", async function () { const erc7007 = await deployERC7007Fixture(); const [owner] = await ethers.getSigners(); await expect(erc7007.mint(owner.address, prompt, aigcData, uri, validProof)) - .to.emit(erc7007, "Mint") + .to.emit(erc7007, "AigcData") }); }); From e84ee2b9506ce7dd20d1edf5c902c41fa0128d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cuesta=20Ca=C3=B1ada?= <38806121+alcueca@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:25:33 +0100 Subject: [PATCH 102/126] Add ERC: Common Quote Oracle Merged by EIP-Bot. --- ERCS/erc-7726.md | 134 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 ERCS/erc-7726.md diff --git a/ERCS/erc-7726.md b/ERCS/erc-7726.md new file mode 100644 index 0000000000..85c4fec7c5 --- /dev/null +++ b/ERCS/erc-7726.md @@ -0,0 +1,134 @@ +--- +eip: 7726 +title: Common Quote Oracle +description: Interface for data feeds providing the relative value of assets. +author: alcueca (@alcueca), ruvaag (@ruvaag), totomanov (@totomanov), r0ohafza (@r0ohafza) +discussions-to: https://ethereum-magicians.org/t/erc-7726-common-quote-oracle/20351 +status: Draft +type: Standards Track +category: ERC +created: 2024-06-20 +requires: 7528 +--- + +## Abstract + +The following allows for the implementation of a standard API for data feeds providing the relative value of +assets, forcing compliant contracts to use explicit token amounts instead of price factors. This approach has been +shown to lead to better security and time-to-market outcomes. + +## Motivation + +The information required to value assets is scattered over a number of major and minor sources, each one with their own +integration API and security considerations. Many protocols over the years have implemented oracle adapter layers for +their own use to abstract this complexity away from their core implementations, leading to much duplicated effort. + +This specification provides a standard API aimed to serve the majority of use cases. Preference is given to ease of +integration and serving the needs of product teams with less knowledge, requirements and resources. + +## Specification +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. +### Definitions + +- base asset: The asset that the user needs to know the value for (e.g: USDC as in "I need to know the value of 1e6 USDC + in ETH terms"). +- quote asset: The asset in which the user needs to value the `base` (e.g: ETH as in "I need to know the value of 1e6 + USDC in ETH terms"). +- value: An amount of `base` in `quote` terms (e.g. The `value` of 1000e6 USDC in ETH terms is 283,969,794,427,307,000 + ETH, and the `value` of 1000e18 ETH in USDC terms is 3,521,501,299,000 USDC). Note that this is an asset amount, and + not a decimal factor. + +### Methods + +#### `getQuote` + +Returns the value of `baseAmount` of `base` in `quote` terms. + +MUST round down towards 0. + +MUST revert with `OracleUnsupportedPair` if not capable to provide data for the specified `base` and `quote` pair. + +MUST revert with `OracleUntrustedData` if not capable to provide data within a degree of confidence publicly specified. + +MUST revert if the value of `baseAmount` of `base` in `quote` terms would overflow in a uint256. + +```yaml +- name: getQuote + type: function + stateMutability: view + + inputs: + - name: baseAmount + type: uint256 + - name: base + type: address + - name: quote + type: address + + outputs: + - name: quoteAmount + type: uint256 +``` + +### Special Addresses + +Some assets under the scope of this specification don't have an address, such as ETH, BTC and national currencies. + +For ETH, the address will be `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE` as per [ERC-7528](./eip-7528.md). + +For BTC, the address will be `0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB`. + +For assets without an address, but with an ISO 4217 code, the code will be used (e.g. `address(840)` for USD). + +### Errors + +#### OracleUnsupportedPair + +```yaml +- name: OracleUnsupportedPair + type: error + + inputs: + - name: base + type: address + - name: quote + type: address +``` + +#### OracleUntrustedData + +```yaml +- name: OracleUntrustedData + type: error + + inputs: + - name: base + type: address + - name: quote + type: address +``` + +## Rationale + +The use of `getQuote` doesn't require the consumer to be aware of any decimal partitions that might have been defined +for the `base` or `quote` and should be preferred in most data processing cases. + +The spec doesn't include a `getPrice` function because it is rarely needed on-chain, and it would be a decimal number of +difficult representation. The popular option for representing prices can be implemented for [ERC-20](./eip-20.md) with decimals as +`oracle.getQuote(base, quote, 10\*\*base.decimals()) and will give the value of a whole unit of base in quote terms. + +## Backwards Compatibility + +Most existing data feeds related to the relative value of pairs of assets should be representable using this standard. + +## Security Considerations + +This specification purposefully provides no methods for data consumers to assess the validity of the data they receive. +It is expected of individual implementations using this specification to decide and publish the quality of the data that +they provide, including the conditions in which they will stop providing it. + +Consumers should review these guarantees and use them to decide whether to integrate or not with a data provider. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 6e77de4fc2636e9f5ef5720f829cbc85f18a93e8 Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:22:35 -0700 Subject: [PATCH 103/126] Update ERC-7677: Move to Last Call Merged by EIP-Bot. --- ERCS/erc-7677.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ERCS/erc-7677.md b/ERCS/erc-7677.md index 8f49665cb6..3bd5cfdf6a 100644 --- a/ERCS/erc-7677.md +++ b/ERCS/erc-7677.md @@ -4,7 +4,8 @@ title: Paymaster Web Service Capability description: A way for apps to communicate with smart wallets about paymaster web services author: Lukas Rosario (@lukasrosario), Dror Tirosh (@drortirosh), Wilson Cusack (@wilsoncusack), Kristof Gazso (@kristofgazso), Hazim Jumali (@hazim-j) discussions-to: https://ethereum-magicians.org/t/erc-7677-paymaster-web-service-capability/19530 -status: Review +status: Last Call +last-call-deadline: 2024-09-05 type: Standards Track category: ERC created: 2024-04-03 From 9d46ca6d3206171f371f9cf259c6687026aa8db2 Mon Sep 17 00:00:00 2001 From: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> Date: Wed, 21 Aug 2024 23:42:38 -0400 Subject: [PATCH 104/126] CI: Update stale.yml Merged by EIP-Bot. --- .github/workflows/stale.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index a96921d17a..f298015cc7 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -20,16 +20,16 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} ascending: true # Since we have so many issues, the stale bot finds it hard to keep track. This makes sure that at least the oldest are removed. # Issue config - stale-issue-message: There has been no activity on this issue for 1 week. It will be closed after 3 months of inactivity. + stale-issue-message: There has been no activity on this issue for six months. It will be closed in 7 days if there is no new activity. close-issue-message: This issue was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback. - days-before-issue-stale: 7 - days-before-issue-close: 49 # 49 + 7 weeks = 3 months + days-before-issue-stale: 183 + days-before-issue-close: 190 exempt-issue-labels: discussions-to stale-issue-label: w-stale # PR config - stale-pr-message: There has been no activity on this pull request for 2 weeks. It will be closed after 3 months of inactivity. If you would like to move this PR forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. + stale-pr-message: There has been no activity on this issue for six months. It will be closed in 7 days if there is no new activity. If you would like to move this PR forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. close-pr-message: This pull request was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment. - days-before-pr-stale: 14 - days-before-pr-close: 42 # 42 + 14 weeks = 3 months + days-before-pr-stale: 183 + days-before-pr-close: 190 exempt-pr-milestones: "Manual Merge Queue" stale-pr-label: w-stale From 4a5445f31e3c8168f9619ed826c7bfabdc90c45a Mon Sep 17 00:00:00 2001 From: Konrad Date: Sat, 24 Aug 2024 14:28:26 +0200 Subject: [PATCH 105/126] Update ERC-7579: fix typo Merged by EIP-Bot. --- ERCS/erc-7579.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7579.md b/ERCS/erc-7579.md index 02a467b8f6..2f3542e23c 100644 --- a/ERCS/erc-7579.md +++ b/ERCS/erc-7579.md @@ -93,7 +93,7 @@ function executeUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) The execution mode is a `bytes32` value that is structured as follows: -- callType (1 byte): `0x00` for a single `call`, `0x01` for a batch `call` and `0xff` for `delegatecall` +- callType (1 byte): `0x00` for a single `call`, `0x01` for a batch `call`, `0xfe` for `staticcall` and `0xff` for `delegatecall` - execType (1 byte): `0x00` for executions that revert on failure, `0x01` for executions that do not revert on failure but implement some form of error handling - unused (4 bytes): this range is reserved for future standardization - modeSelector (4 bytes): an additional mode selector that can be used to create further execution modes @@ -105,7 +105,7 @@ Here is a visual representation of the execution mode: | -------- | -------- | ------- | ------------ | ----------- | | 1 byte | 1 byte | 4 bytes | 4 bytes | 22 bytes | -Accounts are NOT REQUIRED to implement all execution modes. The account MUST declare what modes are supported in `supportsAccountMode` (see below) and if a mode is requested that is not supported by the account, the account MUST revert. +Accounts are NOT REQUIRED to implement all execution modes. The account MUST declare what modes are supported in `supportsExecutionMode` (see below) and if a mode is requested that is not supported by the account, the account MUST revert. The account MUST encode the execution data the following ways: From fe7dce5b1b2e70959997c0bbc30a6dfdd89cdfc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernani=20S=C3=A3o=20Thiago?= Date: Mon, 26 Aug 2024 09:05:36 -0600 Subject: [PATCH 106/126] Update ERC-7432: Update Specification Merged by EIP-Bot. From 18a8146952260cf1dfd399e495da137ccc8aae6d Mon Sep 17 00:00:00 2001 From: Haru <65120405+haruu8@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:56:33 +0000 Subject: [PATCH 107/126] Add ERC: ERC-1155 Multi-Asset extension Merged by EIP-Bot. --- ERCS/erc-7603.md | 159 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 ERCS/erc-7603.md diff --git a/ERCS/erc-7603.md b/ERCS/erc-7603.md new file mode 100644 index 0000000000..1c705bc133 --- /dev/null +++ b/ERCS/erc-7603.md @@ -0,0 +1,159 @@ +--- +eip: 7603 +title: ERC-1155 Multi-Asset extension +description: An interface compatible with ERC-1155 for Multi-Asset tokens with context-dependent asset type output control. +author: Haru (@haruu8) +discussions-to: https://ethereum-magicians.org/t/erc-multi-context-dependent-multi-asset-tokens-eip1155-extension/18303 +status: Draft +type: Standards Track +category: ERC +created: 2024-01-25 +requires: 165, 1155 +--- + +## Abstract + +The Multi-Asset Token standard, compatible with [ERC-1155](./eip-1155.md), facilitates the development of a new fundamental component: the context-dependent data output for each collection. + +The context-dependent data output means that the asset is displayed in an appropriate format based on how the token is accessed. I.e., if the token is being opened in an e-book reader, the PDF asset is displayed; if the token is opened in the marketplace, the PNG or the SVG asset is displayed; if the token is accessed from within a game, the 3D model asset is accessed, and if the token is accessed by an Internet of Things (IoT) hub, the asset providing the necessary addressing and specification information is accessed. + +A Token Collection can have multiple assets (outputs), which can be any file to order them by priority. They do not have to match in mime-type or tokenURI, nor do they depend on one another. Assets are not standalone entities but should be considered โ€œnamespaced tokenURIsโ€. + +## Motivation + +With ERC-1155 compatible tokens being a widespread form of tokens in the Ethereum ecosystem and being used for various use cases, it is time to standardize additional utility for them. Having multiple assets associated with a single Token Collection allows for greater utility, usability, and forward compatibility. This EIP improves upon ERC-1155 in the following areas: + +- [Cross-metaverse compatibility](#cross-metaverse-compatibility) +- [Multi-media output](#multi-media-output) +- [Media redundancy](#media-redundancy) + +### Cross-metaverse compatibility + +The proposal can support any number of different implementations. + +Cross-metaverse compatibility could also be referred to as cross-engine compatibility. An example is where a cosmetic item for game A is unavailable in game B because the frameworks are incompatible. + +Such Tokens can be given further utility through new assets: more games, cosmetic items, and more. + +The following is a more concrete example. One asset is a cosmetic item for game A, a file containing the cosmetic assets. Another is a cosmetic asset file for game B. A third is a generic asset intended to be shown in catalogs, marketplaces, portfolio trackers, or other generalized Token Collection viewers, containing a representation, stylized thumbnail, and animated demo/trailer of the cosmetic item. + +This EIP adds a layer of abstraction, allowing game developers to pull asset data from a user's Tokens directly instead of hard-coding it. + +### Multi-media output + +Tokens of an eBook can be represented as a PDF, MP3, or some other format, depending on what software loads it. If loaded into an eBook reader, a PDF should be displayed, and if loaded into an audiobook application, the MP3 representation should be used. Other metadata could be present in the Tokens (perhaps the book's cover image) for identification on various marketplaces, Search Engine Result Pages (SERPs), or portfolio trackers. + +### Media redundancy + +Many Tokens are minted hastily without best practices in mind. Specifically, many Tokens are minted with metadata centralized on a server somewhere or, in some cases, a hardcoded IPFS gateway which can also go down, instead of just an IPFS hash. + +By adding the same metadata file as different assets, e.g., one asset of metadata and its linked image on Arweave, one asset of this same combination on Sia, another of the same combination on IPFS, etc., the resilience of the metadata and its referenced information increases exponentially as the chances of all the protocols going down at once become less likely. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +```solidity +/// @title ERC-7603 Context-Dependent Multi-Asset Tokens, ERC-1155 Execution +/// @dev See https://eips.ethereum.org/EIPS/erc-7603 + +pragma solidity ^0.8.23; + +interface IERC7603 /* is ERC165 */ { + /** + * @notice Used to notify listeners that an asset object is initialised at `assetId`. + * @param assetId ID of the asset that was initialised + */ + event AssetSet(uint64 assetId); + + /** + * @notice Used to notify listeners that an asset object at `assetId` is added to token's asset + * array. + * @param tokenId An ID of the token that received a new asset + * @param assetId ID of the asset that has been added to the token's assets array + * @param replacesId ID of the asset that would be replaced + */ + event AssetAddedToToken( + uint256[] tokenId, + uint64 indexed assetId, + uint64 indexed replacesId + ); + + /** + * @notice Used to notify listeners that token's priority array is reordered. + * @param tokenId ID of the token that had the asset priority array updated + */ + event AssetPrioritySet(uint256 indexed tokenId); + + /** + * @notice Sets a new priority array for a given token. + * @dev The priority array is a non-sequential list of `uint16`s, where the lowest value is considered highest + * priority. + * @dev Value `0` of a priority is a special case equivalent to uninitialised. + * @dev Requirements: + * + * - `tokenId` must exist. + * - The length of `priorities` must be equal the length of the assets array. + * @dev Emits a {AssetPrioritySet} event. + * @param tokenId ID of the token to set the priorities for + * @param priorities An array of priorities of assets. The succession of items in the priorities array + * matches that of the succession of items in the array + */ + function setPriority(uint256 tokenId, uint64[] calldata priorities) + external; + + /** + * @notice Used to retrieve IDs of assets of given token. + * @dev Asset data is stored by reference, in order to access the data corresponding to the ID, call + * `getAssetMetadata(tokenId, assetId)`. + * @dev You can safely get 10k + * @param tokenId ID of the token to retrieve the IDs of the assets + * @return uint64[] An array of the asset IDs of the given token + */ + function getAssets(uint256 tokenId) + external + view + returns (uint64[] memory); + + /** + * @notice Used to retrieve the priorities of the assets of a given token. + * @dev Asset priorities are a non-sequential array of uint16 values with an array size equal to asset + * priorites. + * @param tokenId ID of the token for which to retrieve the priorities of the assets + * @return uint16[] An array of priorities of the assets of the given token + */ + function getAssetPriorities(uint256 tokenId) + external + view + returns (uint64[] memory); + + /** + * @notice Used to fetch the asset metadata of the specified token's asset with the given index. + * @dev Can be overridden to implement enumerate, fallback or other custom logic. + * @param tokenId ID of the token from which to retrieve the asset metadata + * @param assetId Asset Id, must be in the assets array + * @return string The metadata of the asset belonging to the specified index in the token's assets array + */ + function getAssetMetadata(uint256 tokenId, uint64 assetId) + external + view + returns (string memory); +} + +``` + +## Rationale + +TBD + +## Backwards Compatibility + +The MultiAsset token standard has been made compatible with ERC-1155 in order to take advantage of the robust tooling available for implementations of ERC-1155 and to ensure compatibility with existing ERC-1155 infrastructure. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 37581362e7def0033b3a5280a2ddac69bdb6e580 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Tue, 27 Aug 2024 10:16:43 +0800 Subject: [PATCH 108/126] Add ERC: Minimal Upgradeable Proxies Merged by EIP-Bot. --- ERCS/erc-7760.md | 514 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100644 ERCS/erc-7760.md diff --git a/ERCS/erc-7760.md b/ERCS/erc-7760.md new file mode 100644 index 0000000000..50fb32b43a --- /dev/null +++ b/ERCS/erc-7760.md @@ -0,0 +1,514 @@ +--- +eip: 7760 +title: Minimal Upgradeable Proxies +description: Minimal upgradeable proxies with immutable arguments and support for onchain implementation queries +author: Atarpara (@Atarpara), JT Riley (@jtriley-eth), Thomas (@0xth0mas), xiaobaiskill (@xiaobaiskill), Vectorized (@Vectorized) +discussions-to: https://ethereum-magicians.org/t/erc-7760-minimal-upgradeable-proxies/20868 +status: Draft +type: Standards Track +category: ERC +created: 2024-08-19 +requires: 1967 +--- + +## Abstract + +This standard defines minimal [ERC-1967](./eip-1967.md) proxies for three patterns: (1) transparent, (2) UUPS, (3) beacon. The proxies support optional immutable arguments which are appended to the end of their runtime bytecode. Additional variants which support onchain implementation querying are provided. + +## Motivation + +Having standardized minimal bytecode for upgradeable proxies enables the following: + +1. Automatic verification on block explorers. +2. Ability for immutable arguments to be queried onchain, as these arguments are stored at the same bytecode offset, +3. Ability for the implementation to be queried and verified onchain. + +The minimal nature of the proxies enables cheaper deployment and runtime costs. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### General specifications + +All of the following proxies MAY have optional data bytecode appended to the end of their runtime bytecode. + +Emitting the ERC-1967 events during initialization is OPTIONAL. Indexers MUST NOT expect the initialization code to emit the ERC-1967 events. + +### Onchain querying of implementation for I-variants + +The I-variants have logic that returns the implementation baked into their bytecode. + +When called with any 1-byte calldata, these I-variants will return the address (left-zero-padded to 32 bytes) and will not forward the calldata to the target. + +The bytecode of the proxies before any optional immutable arguments MUST be verified with the following steps: + +1. Fetch the bytecode before any immutable arguments with `EXTCODECOPY`. +2. Zeroize any baked-in factory address in the fetched bytecode. +3. Ensure that the hash of the final fetched bytecode matches the expected hash of the bytecode. + +If the hash does not match, the implementation address returned MUST NOT be trusted. + +### Minimal ERC-1967 transparent upgradeable proxy + +The transparent upgradeable proxy is RECOMMENDED to be deployed by a factory that doubles as the account that is authenticated to perform upgrades. An externally owned account may perform the deployment on behalf of the factory. For convention, we will refer to the factory as the immutable account authorized to invoke the upgrade logic on the proxy. + +As the proxy's runtime bytecode contains logic to allow the factory to set any storage slot with any value, the initialization code MAY skip storing the implementation slot. + +The upgrading logic does not emit the ERC-1967 event. Indexers MUST NOT expect the upgrading logic to emit the ERC-1967 events. + +During upgrades, the factory MUST call the upgradeable proxy with following calldata: + +```solidity +abi.encodePacked( + // The new implementation address, converted to a 32-byte word. + uint256(uint160(implementation)), + // ERC-1967 implementation slot. + bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc), + // Optional calldata to be forwarded to the implementation + // via delegatecall after setting the implementation slot. + "" +) +``` + +#### Minimal ERC-1967 transparent upgradeable proxy for (basic variant) + +Runtime bytecode (20-byte factory address subvariant): + +``` +3d3d3373________________________________________14605757363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e6052573d6000fd5b3d6000f35b3d356020355560408036111560525736038060403d373d3d355af43d6000803e6052573d6000fd +``` + +where `________________________________________` is the 20-byte factory address. + +Runtime bytecode (14-byte factory address subvariant): + +``` +3d3d336d____________________________14605157363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e604c573d6000fd5b3d6000f35b3d3560203555604080361115604c5736038060403d373d3d355af43d6000803e604c573d6000fd +``` + +where `____________________________` is the 14-byte factory address. + +#### Minimal ERC-1967 transparent upgradeable proxy (I-variant) + +Runtime bytecode (20-byte factory address subvariant): + +``` +3658146083573d3d3373________________________________________14605D57363d3d37363D7f360894a13ba1A3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e6058573d6000fd5b3d6000f35b3d35602035556040360380156058578060403d373d3d355af43d6000803e6058573d6000fd5b602060293d393d51543d52593df3 +``` + +where `________________________________________` is the 20-byte factory address. + +Runtime bytecode (14-byte factory address subvariant): + +``` +365814607d573d3d336d____________________________14605757363d3D37363d7F360894A13Ba1A3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e6052573d6000fd5b3d6000f35b3d35602035556040360380156052578060403d373d3d355af43d6000803e6052573d6000fd5b602060233d393d51543d52593df3 +``` + +where `____________________________` is the 14-byte factory address. + +### Minimal ERC-1967 UUPS proxy + +As this proxy does not contain upgrading logic, the initialization code MUST store the implementation at the ERC-1967 implementation storage slot `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`. + +#### Minimal ERC-1967 UUPS proxy (basic variant) + +Runtime bytecode: + +``` +363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3 +``` + +#### Minimal ERC-1967 UUPS proxy (I-variant) + +Runtime bytecode: + +``` +365814604357363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3 +``` + +### Minimal ERC-1967 beacon proxy + +As this proxy does not contain upgrading logic, the initialization code MUST store the implementation at the ERC-1967 implementation storage slot `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`. + +#### Minimal ERC-1967 beacon proxy (basic variant) + +Runtime bytecode: + +``` +363d3d373d3d363d602036600436635c60da1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3 +``` + +#### Minimal ERC-1967 beacon proxy (I-variant) + +Runtime bytecode: + +``` +363d3d373d3d363d602036600436635c60da1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3 +``` + +## Rationale + +### No usage of `PUSH0` opcode + +For more widespread EVM compatibility, the proxies deliberately do not use the `PUSH0` opcode proposed in [EIP-3855](./eip-3855.md). + +Converting the proxies to `PUSH0` variants may be done in a separate future ERC. + +### Optimization priorities + +The proxies are first optimized for minimal runtime gas before minimal bytecode size. + +### Minimal nature + +These proxies made from handcrafted EVM bytecode. While utmost efforts have been made to ensure that they are as minimal as possible at the time of development, it is possible that they can be further optimized. If a variant has already been used in the wild, it is preferable to keep their existing layout in this standard, as the benefits of automatic block explorer verification will outweigh the few gas saved during runtime or deployment. + +For historical reference, the [ERC-1167](./eip-1167.md) minimal proxy was not the theoretical minimal at the time of writing. The 0age minimal proxy has lower runtime gas costs and smaller bytecode size. + +### Transparent upgradeable proxy + +The factory address in the transparent upgradeable proxy is baked into the immutable bytecode of the minimal transparent upgradeable proxy. + +This is to save a `SLOAD` for every proxy call. + +As the factory can contain custom authorization logic that allows for admin rotation, we do not lose any flexibility. + +The upgrade logic takes in any 32 byte value and 32 byte storage slot. This is for flexibility and bytecode conciseness. + +We do not lose any security as the implementation can still modify any storage slot. + +### 14-byte factory address subvariants + +It is beneficial to install the transparent upgradeable proxy factory at a vanity address with leading zero bytes so that the proxy's bytecode can be optimized to be shorter. + +A 14-byte factory address (i.e. 6 leading zero bytes) is chosen because it strikes a balance between mining costs and bytecode size. + +### I-variants + +The so-called "I-variants" contain logic that returns the implementation address baked into the proxy bytecode. + +This allows contracts to retrieve the implementation of the proxy onchain in a verifiable way. + +As long as the proxy's runtime bytecode starts with the bytecode in this standard, we can be sure that the implementation address is not spoofed. + +The choice of reserving 1-byte calldata to denote an implementation query request is for efficiency and to prevent calldata collision. Regular ETH transfers use 0-byte calldata, and regular Solidity function calls use calldata that is 4 bytes or longer. + +### Omission of events in bytecode + +This is for minimal bytecode size and deployment costs. + +Most block explorers and indexers are able to deduce the latest implementation without the use of events simply by reading the slots. + +### Immutable arguments are not appended to forwarded calldata + +This is to avoid compatibility and safety issues with other ERC standards that append extra data to the calldata. + +The `EXTCODECOPY` opcode can be used to retrieve the immutable arguments. + +### No fixed initialization code + +As long as the initialization code is able to initialize the relevant ERC-1967 implementation slot where needed (i.e. for the UUPS proxy and Beacon proxy), there is no need for additional requirements on the initialization code. + +### Out of scope topics + +The following topics are intentionally out of scope of this standard, as they can contain custom logic: + +- Factories for proxy deployment. +- Logic for reading and verifying the implementation from the I-variants onchain. +- Beacon for the beacon proxies. + +Nevertheless, they require careful implementation to ensure security and correctness. + +## Backwards Compatibility + +No backward compatibility issues found. + +## Reference Implementation + +### Minimal ERC-1967 transparent upgradeable proxy implementation + +#### Minimal ERC-1967 transparent upgradeable proxy implementation (basic variant) + +```solidity +pragma solidity ^0.8.0; + +library ERC1967MinimalTransparentUpgradeableProxyLib { + function initCodeFor20ByteFactoryAddress() internal view returns (bytes memory) { + return abi.encodePacked( + bytes13(0x607f3d8160093d39f33d3d3373), + address(this), + bytes32(0x14605757363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc), + bytes32(0x3735a920a3ca505d382bbc545af43d6000803e6052573d6000fd5b3d6000f35b), + bytes32(0x3d356020355560408036111560525736038060403d373d3d355af43d6000803e), + bytes7(0x6052573d6000fd) + ); + } + + function initCodeFor14ByteFactoryAddress() internal view returns (bytes memory) { + return abi.encodePacked( + bytes13(0x60793d8160093d39f33d3d336d), + uint112(uint160(address(this))), + bytes32(0x14605157363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc), + bytes32(0x3735a920a3ca505d382bbc545af43d6000803e604c573d6000fd5b3d6000f35b), + bytes32(0x3d3560203555604080361115604c5736038060403d373d3d355af43d6000803e), + bytes7(0x604c573d6000fd) + ); + } + + function initCode() internal view returns (bytes memory) { + if (uint160(address(this)) >> 112 != 0) { + return initCodeFor20ByteFactoryAddress(); + } else { + return initCodeFor14ByteFactoryAddress(); + } + } + + function deploy(address implementation, bytes memory initializationData) + internal + returns (address instance) + { + bytes memory m = initCode(); + assembly { + instance := create(0, add(m, 0x20), mload(m)) + } + require(instance != address(0), "Deployment failed."); + upgrade(instance, implementation, initializationData); + } + + function upgrade(address instance, address implementation, bytes memory upgradeData) internal { + (bool success,) = instance.call( + abi.encodePacked( + // The new implementation address, converted to a 32-byte word. + uint256(uint160(implementation)), + // ERC-1967 implementation slot. + bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc), + // Optional calldata to be forwarded to the implementation + // via delegatecall after setting the implementation slot. + upgradeData + ) + ); + require(success, "Upgrade failed."); + } +} +``` + +#### Minimal ERC-1967 transparent upgradeable proxy implementation (I-variant) + +```solidity +pragma solidity ^0.8.0; + +library ERC1967IMinimalTransparentUpgradeableProxyLib { + function initCodeFor20ByteFactoryAddress() internal view returns (bytes memory) { + return abi.encodePacked( + bytes19(0x60923d8160093d39f33658146083573d3d3373), + address(this), + bytes20(0x14605D57363d3d37363D7f360894a13ba1A32106), + bytes32(0x67c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e60), + bytes32(0x58573d6000fd5b3d6000f35b3d35602035556040360380156058578060403d37), + bytes32(0x3d3d355af43d6000803e6058573d6000fd5b602060293d393d51543d52593df3) + ); + } + + function initCodeFor14ByteFactoryAddress() internal view returns (bytes memory) { + return abi.encodePacked( + bytes19(0x608c3d8160093d39f3365814607d573d3d336d), + uint112(uint160(address(this))), + bytes20(0x14605757363d3D37363d7F360894A13Ba1A32106), + bytes32(0x67c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e60), + bytes32(0x52573d6000fd5b3d6000f35b3d35602035556040360380156052578060403d37), + bytes32(0x3d3d355af43d6000803e6052573d6000fd5b602060233d393d51543d52593df3) + ); + } + + function initCode() internal view returns (bytes memory) { + if (uint160(address(this)) >> 112 != 0) { + return initCodeFor20ByteFactoryAddress(); + } else { + return initCodeFor14ByteFactoryAddress(); + } + } + + function deploy(address implementation, bytes memory initializationData) + internal + returns (address instance) + { + bytes memory m = initCode(); + assembly { + instance := create(0, add(m, 0x20), mload(m)) + } + require(instance != address(0), "Deployment failed."); + upgrade(instance, implementation, initializationData); + } + + function upgrade(address instance, address implementation, bytes memory upgradeData) internal { + (bool success,) = instance.call( + abi.encodePacked( + // The new implementation address, converted to a 32-byte word. + uint256(uint160(implementation)), + // ERC-1967 implementation slot. + bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc), + // Optional calldata to be forwarded to the implementation + // via delegatecall after setting the implementation slot. + upgradeData + ) + ); + require(success, "Upgrade failed."); + } +} +``` + +### Minimal ERC-1967 UUPS proxy implementation + +#### Minimal ERC-1967 UUPS proxy implementation (basic variant) + +```solidity +pragma solidity ^0.8.0; + +library ERC1967MinimalUUPSProxyLib { + function initCode(address implementation, bytes memory args) + internal + pure + returns (bytes memory) + { + uint256 n = 0x003d + args.length; + require(n <= 0xffff, "Immutable args too long."); + return abi.encodePacked( + bytes1(0x61), + uint16(n), + bytes7(0x3d8160233d3973), + implementation, + bytes2(0x6009), + bytes32(0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076), + bytes32(0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3), + args + ); + } + + function deploy(address implementation, bytes memory args) + internal + returns (address instance) + { + bytes memory m = initCode(implementation, args); + assembly { + instance := create(0, add(m, 0x20), mload(m)) + } + require(instance != address(0), "Deployment failed."); + } +} +``` + +#### Minimal ERC-1967 UUPS proxy implementation (I-variant) + +```solidity +pragma solidity ^0.8.0; + +library ERC1967IMinimalUUPSProxyLib { + function initCode(address implementation, bytes memory args) + internal + pure + returns (bytes memory) + { + uint256 n = 0x0052 + args.length; + require(n <= 0xffff, "Immutable args too long."); + return abi.encodePacked( + bytes1(0x61), + uint16(n), + bytes7(0x3d8160233d3973), + implementation, + bytes23(0x600f5155f3365814604357363d3d373d3d363d7f360894), + bytes32(0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4), + bytes32(0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3), + args + ); + } + + function deploy(address implementation, bytes memory args) + internal + returns (address instance) + { + bytes memory m = initCode(implementation, args); + assembly { + instance := create(0, add(m, 0x20), mload(m)) + } + require(instance != address(0), "Deployment failed."); + } +} +``` + +### Minimal ERC-1967 beacon proxy implementation + +#### Minimal ERC-1967 beacon proxy implementation (basic variant) + +```solidity +pragma solidity ^0.8.0; + +library ERC1967MinimalBeaconProxyLib { + function initCode(address beacon, bytes memory args) internal pure returns (bytes memory) { + uint256 n = 0x0052 + args.length; + require(n <= 0xffff, "Immutable args too long."); + return abi.encodePacked( + bytes1(0x61), + uint16(n), + bytes7(0x3d8160233d3973), + beacon, + bytes23(0x60195155f3363d3d373d3d363d602036600436635c60da), + bytes32(0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c), + bytes32(0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3), + args + ); + } + + function deploy(address beacon, bytes memory args) internal returns (address instance) { + bytes memory m = initCode(beacon, args); + assembly { + instance := create(0, add(m, 0x20), mload(m)) + } + require(instance != address(0), "Deployment failed."); + } +} +``` + +#### Minimal ERC-1967 beacon proxy implementation (I-variant) + +```solidity +pragma solidity ^0.8.0; + +library ERC1967IMinimalBeaconProxyLib { + function initCode(address beacon, bytes memory args) internal pure returns (bytes memory) { + uint256 n = 0x0057 + args.length; + require(n <= 0xffff, "Immutable args too long."); + return abi.encodePacked( + bytes1(0x61), + uint16(n), + bytes7(0x3d8160233d3973), + beacon, + bytes28(0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36), + bytes32(0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513), + bytes32(0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3), + args + ); + } + + function deploy(address beacon, bytes memory args) internal returns (address instance) { + bytes memory m = initCode(beacon, args); + assembly { + instance := create(0, add(m, 0x20), mload(m)) + } + require(instance != address(0), "Deployment failed."); + } +} +``` + +## Security Considerations + +### Transparent upgradeable proxy factory security considerations + +To ensure security, the transparent upgradeable proxy factory must implement proper access control to allow proxies to be upgraded by only authorized accounts. + +### Calldata length collision for I-variants + +The I-variants reserve all calldata of length 1 to denote a request to return the implementation. This may pose compatibility issues if the underlying implementation actually uses 1-byte calldata for special purposes. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 5cb2679232df2229caab89ec8384daca77951da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=BBuk?= Date: Tue, 27 Aug 2024 23:00:48 +0200 Subject: [PATCH 109/126] Add ERC: Puppet Proxy Contract Merged by EIP-Bot. --- ERCS/erc-7613.md | 345 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 ERCS/erc-7613.md diff --git a/ERCS/erc-7613.md b/ERCS/erc-7613.md new file mode 100644 index 0000000000..a1f71a8789 --- /dev/null +++ b/ERCS/erc-7613.md @@ -0,0 +1,345 @@ +--- +eip: 7613 +title: Puppet Proxy Contract +description: A proxy that, if called by its deployer, delegates to an implementation specified in calldata. +author: Igor ลปuk (@CodeSandwich) +discussions-to: https://ethereum-magicians.org/t/eip-7613-puppet-proxy-contract/18482 +status: Draft +type: Standards Track +category: ERC +created: 2024-02-04 +--- + +## Abstract + +A puppet is a contract that, when called, acts like an empty account. It doesn't do anything and it has no API, except when it is called by the address that deployed it. In that case, it delegates the call to the address passed to it in calldata. This gives the deployer the ability to execute any logic they want in the context of the puppet. + +## Motivation + +A puppet can be used as an alternative account of its deployer. It has a different address, so it has a separate set of asset balances. This enables sophisticated accounting, e.g. each user of a protocol can get their own address where assets can be sent and stored. The user may call the protocol contract, which in turn will deploy a new puppet and consider it assigned to the user. If the puppet is deployed under a predictable address, e.g. by using the user's address as the CREATE2 salt, the puppet may not even need to be deployed before funds are sent to its address. From now on the protocol will consider all the assets sent to the puppet as owned by the user. If the protocol needs to move the funds out from the puppet address, it can call the puppet ordering it to delegate to a function transferring the assets to arbitrary addresses, or making arbitrary calls triggering approved transfers to other contracts. + +Puppets can be used as an alternative to approved transfers when loading funds into the protocol. Any contract and any wallet can transfer the funds to the puppet address assigned to the user without making any approvals or calling the protocol contracts. Funds can be loaded across multiple transactions and potentially from multiple sources. To funnel funds from another protocol, there's no need for integration in the 3rd party contracts as long as they are capable of transferring funds to an arbitrary address. Wallets limited to plain [ERC-20](./eip-20.md) transfers and stripped of any web3 functionality can be used to load funds into the protocol. The users of the fully featured wallets don't need to sign opaque calldata blobs that may be harmful or approve the protocol to take their tokens, they only need to make a transfer, which is a simple process with a familiar UX. When the funds are already stored in the puppet assigned to the user, somebody needs to call the protocol so it's notified that the funds were loaded. Depending on the protocol and its API this call may or may not be permissionless potentially making the UX even more convenient with gasless transactions or 3rd parties covering the gas cost. Some protocols don't need the users to specify what needs to be done with the loaded funds or they allow the users to configure that in advance. Most of the protocols using approved transfers to load funds may benefit from using the puppets. + +The puppet's logic doesn't need to be ever upgraded. To change its behavior the deployer needs to change the address it passes to the puppet to delegate to or the calldata it passes for delegation. The entire fleet of puppets deployed by a single contract can be upgraded by upgrading the contract that deployed them, without using beacons. A nice trick is that the deployer can make the puppet delegate to the address holding the deployer's own logic, so the puppet's logic is encapsulated in the deployer's. + +A puppet is unable to expose any API to any caller except the deployer. If a 3rd party needs to be able to somehow make the puppet execute some logic, it can't be requested by directly calling the puppet. Instead, the deployer needs to expose a function that if called by the 3rd parties, will call the puppet, and make it execute the desired logic. Mechanisms expecting contracts to expose some APIs don't work with puppet, e.g. [ERC-721](./eip-721.md)'s `safeTransfer`s. + +This standard defines the puppet as a blob of bytes used as creation code, which enables integration with many frameworks and codebases written in variety of languages. The specific tooling is outside of the scope of this standard, but it should be easy to create the libraries and helpers necessary for usage in practice. All the implementations will be interoperable because they will be creating identical puppets and if CREATE2 is used, they will have deterministic addresses predictable by all implementations. + +Because the puppet can be deployed under a predictable address despite having no fixed logic, in some cases it can be used as a CREATE3 alternative. It can be also used as a full replacement of the CREATE3 factory by using a puppet deployed using CREATE2 to deploy arbitrary code using plain CREATE. + +Deploying a new puppet is almost as cheap as deploying a new clone proxy. Its whole deployed bytecode is 66 bytes, and its creation code is 62 bytes. Just like clone proxy, it can be deployed using just the Solidity scratch space in memory. The cost to deploy a puppet is 45K gas, only 4K more than a clone. Because the bytecode is not compiled, it can be reliably deployed under a predictable CREATE2 address regardless of the compiler version. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +To delegate, the deployer must prepend the calldata with an ABI-encoded address to delegate to. +All the data after the address will be passed verbatim as the delegation calldata. +If the caller isn't the deployer, the calldata is shorter than 32 bytes, or it doesn't start with +an address left-padded with zeros, the puppet doesn't do anything. +This lets the deployer make a plain native tokens transfer to the puppet, +it will have an empty calldata, and the puppet will accept the transfer without delegating. + +The puppet is deployed with this creation code: +``` +0x604260126D60203D3D3683113D3560A01C17733D3360147F331817604057823603803D943D373D3D355AF43D82803E903D91604057FD5BF36034525252F3 +``` + +The bytecode breakdown: +``` +// The creation code. +// [code 1] and [code 2] are parts of the deployed code, +// placed respectively before and after the deployer's address. +// | Opcode used | Hex value | Stack content after executing +// Code size and offset in memory +// | PUSH1 | 60 42 | 66 +// | PUSH1 | 60 12 | 18 66 +// The code before the deployer's address and where it's stored in memory +// | PUSH14 | 6D [code 1] | [code 1] 18 66 +// | RETURNDATASIZE | 3D | 0 [code 1] 18 66 +// The deployer's address and where it's stored in memory +// | CALLER | 33 | [deployer] 0 [code 1] 18 66 +// | PUSH1 | 60 14 | 20 [deployer] 0 [code 1] 18 66 +// The code after the deployer's address and where it's stored in memory +// | PUSH32 | 7F [code 2] | [code 2] 20 [deployer] 0 [code 1] 18 66 +// | PUSH1 | 60 34 | 52 [code 2] 20 [deployer] 0 [code 1] 18 66 +// Return the entire code +// | MSTORE | 52 | 20 [deployer] 0 [code 1] 18 66 +// | MSTORE | 52 | 0 [code 1] 18 66 +// | MSTORE | 52 | 18 66 +// | RETURN | F3 | + +// The deployed code. +// `deployer` is the deployer's address. +// | Opcode used | Hex value | Stack content after executing +// Push some constants +// | PUSH1 | 60 20 | 32 +// | RETURNDATASIZE | 3D | 0 32 +// | RETURNDATASIZE | 3D | 0 0 32 +// Do not delegate if calldata shorter than 32 bytes +// | CALLDATASIZE | 36 | [calldata size] 0 0 32 +// | DUP4 | 83 | 32 [calldata size] 0 0 32 +// | GT | 11 | [do not delegate] 0 0 32 +// Do not delegate if the first word of calldata is not a zero-padded address +// | RETURNDATASIZE | 3D | 0 [do not delegate] 0 0 32 +// | CALLDATALOAD | 35 | [first word] [do not delegate] 0 0 32 +// | PUSH1 | 60 A0 | 160 [first word] [do not delegate] 0 0 32 +// | SHR | 1C | [first word upper bits] [do not delegate] 0 0 32 +// | OR | 17 | [do not delegate] 0 0 32 +// Do not delegate if not called by the deployer +// | PUSH20 | 73 [deployer] | [deployer] [do not delegate] 0 0 32 +// | CALLER | 33 | [sender] [deployer] [do not delegate] 0 0 32 +// | XOR | 18 | [sender not deployer] [do not delegate] 0 0 32 +// | OR | 17 | [do not delegate] 0 0 32 +// Skip to the return if should not delegate +// | PUSH1 | 60 40 | [success branch] [do not delegate] 0 0 32 +// | JUMPI | 57 | 0 0 32 +// Calculate the payload size +// | DUP3 | 82 | 32 0 0 32 +// | CALLDATASIZE | 36 | [calldata size] 32 0 0 32 +// | SUB | 03 | [payload size] 0 0 32 +// Copy the payload from calldata +// | DUP1 | 80 | [payload size] [payload size] 0 0 32 +// | RETURNDATASIZE | 3D | 0 [payload size] [payload size] 0 0 32 +// | SWAP5 | 94 | 32 [payload size] [payload size] 0 0 0 +// | RETURNDATASIZE | 3D | 0 32 [payload size] [payload size] 0 0 0 +// | CALLDATACOPY | 37 | [payload size] 0 0 0 +// Delegate call +// | RETURNDATASIZE | 3D | 0 [payload size] 0 0 0 +// | RETURNDATASIZE | 3D | 0 0 [payload size] 0 0 0 +// | CALLDATALOAD | 35 | [delegate to] 0 [payload size] 0 0 0 +// | GAS | 5A | [gas] [delegate to] 0 [payload size] 0 0 0 +// | DELEGATECALL | F4 | [success] 0 +// Copy return data +// | RETURNDATASIZE | 3D | [return size] [success] 0 +// | DUP3 | 82 | 0 [return size] [success] 0 +// | DUP1 | 80 | 0 0 [return size] [success] 0 +// | RETURNDATACOPY | 3E | [success] 0 +// Return +// | SWAP1 | 90 | 0 [success] +// | RETURNDATASIZE | 3D | [return size] 0 [success] +// | SWAP2 | 91 | [success] 0 [return size] +// | PUSH1 | 60 40 | [success branch] [success] 0 [return size] +// | JUMPI | 57 | 0 [return size] +// | REVERT | FD | +// | JUMPDEST | 5B | 0 [return size] +// | RETURN | F3 | +``` + +## Rationale + +The main goals of the puppet design are low cost and modularity. It should be cheap to deploy and cheap to interact with. The contract should be self-contained, simple to reason about, and easy to use as an architectural building block. + +The puppet behavior could be implemented fairly easily in Solidity with some inline Yul for delegation. This would make the bytecode much larger and more expensive to deploy. It would also be different depending on the compiler version and configuration, so deployments under predictable addresses using CREATE2 would be trickier. + +A workaround for the problems with the above solution could be to use the clone proxy pattern to deploy copies of the puppet implementation. It would make the cost to deploy each puppet a little lower than deploying the bytecode proposed in this document, and the addresses of the clones would be predictable when deploying using CREATE2. The downside is that now there would be 1 extra delegation for each call, from the clone proxy to the puppet implementation address, which costs gas. The architecture of such solution is also more complicated with more contracts involved, and it requires the initialization step of deploying the puppet implementation before any clone can be deployed. The initialization step limits the CREATE2 address predictability because the creation code of the clone proxy includes the implementation address, which affects the deployment address. + +Another alternative is to use the beacon proxy pattern. Making a Solidity API call safely is a relatively complex procedure that takes up a non-trivial space in the bytecode. To lower the cost of the puppets, the beacon proxy probably should be used with the clone proxy, which would be even more complicated and more expensive to use than the above solutions. Querying a beacon for the delegation address is less flexible than passing it in calldata, it requires updating the state of the beacon to change the address. + +## Backwards Compatibility + +No backward compatibility issues found. + +The puppet bytecode doesn't use PUSH0, because many chains don't support it yet. + +## Test Cases + +Here are the tests verifying that the bytecode and the reference implementation library are working as expected, using the Foundry test tools: + +```solidity +pragma solidity ^0.8.0; + +import {Test} from "forge-std/Test.sol"; +import {Puppet} from "src/Puppet.sol"; + +contract Logic { + string public constant ERROR = "Failure called"; + + fallback(bytes calldata data) external returns (bytes memory) { + return abi.encode(data); + } + + function success(uint256 arg) external payable returns (address, uint256, uint256) { + return (address(this), arg, msg.value); + } + + function failure() external pure { + revert(ERROR); + } +} + +contract PuppetTest is Test { + address puppet = Puppet.deploy(); + address logic = address(new Logic()); + + function logicFailurePayload() internal view returns (bytes memory) { + return Puppet.delegationCalldata(logic, abi.encodeWithSelector(Logic.failure.selector)); + } + + function call(address target, bytes memory data) internal returns (bytes memory) { + return call(target, data, 0); + } + + function call(address target, bytes memory data, uint256 value) + internal + returns (bytes memory) + { + (bool success, bytes memory returned) = target.call{value: value}(data); + require(success, "Unexpected revert"); + return returned; + } + + function testDeployDeterministic() public { + bytes32 salt = keccak256("Puppet"); + address newPuppet = Puppet.deployDeterministic(salt); + assertEq( + newPuppet, Puppet.predictDeterministicAddress(salt, address(this)), "Invalid address" + ); + assertEq( + newPuppet, Puppet.predictDeterministicAddress(salt), "Invalid address when no deployer" + ); + assertEq(newPuppet.code, puppet.code, "Invalid code"); + } + + function testPuppetDelegates() public { + uint256 arg = 1234; + bytes memory data = abi.encodeWithSelector(Logic.success.selector, arg); + bytes memory payload = Puppet.delegationCalldata(logic, data); + uint256 value = 5678; + + bytes memory returned = call(puppet, payload, value); + + (address thisAddr, uint256 receivedArg, uint256 receivedValue) = + abi.decode(returned, (address, uint256, uint256)); + assertEq(thisAddr, puppet, "Invalid delegation context"); + assertEq(receivedArg, arg, "Invalid argument"); + assertEq(receivedValue, value, "Invalid value"); + } + + function testPuppetDelegatesWithEmptyCalldata() public { + bytes memory payload = Puppet.delegationCalldata(logic, ""); + bytes memory returned = call(puppet, payload); + bytes memory data = abi.decode(returned, (bytes)); + assertEq(data.length, 0, "Delegated with non-empty calldata"); + } + + function testPuppetBubblesRevertPayload() public { + vm.expectRevert(bytes(Logic(logic).ERROR())); + call(puppet, logicFailurePayload()); + } + + function testPuppetDoesNothingForNonDeployer() public { + vm.prank(address(1234)); + call(puppet, logicFailurePayload()); + } + + function testCallingWithCalldataShorterThan32BytesDoesNothing() public { + address delegateTo = address(uint160(1234) << 8); + bytes memory payload = abi.encodePacked(bytes31(bytes32(uint256(uint160(delegateTo))))); + vm.mockCallRevert(delegateTo, "", "Logic called"); + call(puppet, payload); + } + + function testCallingWithDelegationAddressOver20BytesDoesNothing() public { + bytes memory payload = logicFailurePayload(); + payload[11] = 0x01; + call(puppet, payload); + } + + function testCallingPuppetDoesNothing() public { + // Forge the calldata, so if puppet uses it to delegate, it will run `Logic.failure` + uint256 forged = uint256(uint160(address(this))) << 32; + forged |= uint32(Logic.failure.selector); + bytes memory payload = abi.encodeWithSignature("abc(uint)", forged); + call(puppet, payload); + } + + function testTransferFromDeployerToPuppet() public { + uint256 amt = 123; + payable(puppet).transfer(amt); + assertEq(puppet.balance, amt, "Invalid balance"); + } + + function testTransferToPuppet() public { + uint256 amt = 123; + address sender = address(456); + payable(sender).transfer(amt); + vm.prank(sender); + payable(puppet).transfer(amt); + assertEq(puppet.balance, amt, "Invalid balance"); + } +} +``` + +## Reference Implementation + +The puppet bytecode is explained in the specification section. Here's the example helper library: + +```solidity +library Puppet { + bytes internal constant CREATION_CODE = + hex"604260126D60203D3D3683113D3560A01C17733D3360147F33181760405782" + hex"3603803D943D373D3D355AF43D82803E903D91604057FD5BF36034525252F3"; + bytes32 internal constant CREATION_CODE_HASH = keccak256(CREATION_CODE); + + /// @notice Deploy a new puppet. + /// @return instance The address of the puppet. + function deploy() internal returns (address instance) { + bytes memory creationCode = CREATION_CODE; + assembly { + instance := create(0, add(creationCode, 32), mload(creationCode)) + } + require(instance != address(0), "Failed to deploy the puppet"); + } + + /// @notice Deploy a new puppet under a deterministic address. + /// @param salt The salt to use for the deterministic deployment. + /// @return instance The address of the puppet. + function deployDeterministic(bytes32 salt) internal returns (address instance) { + bytes memory creationCode = CREATION_CODE; + assembly { + instance := create2(0, add(creationCode, 32), mload(creationCode), salt) + } + require(instance != address(0), "Failed to deploy the puppet"); + } + + /// @notice Calculate the deterministic address for a puppet deployment made by this contract. + /// @param salt The salt to use for the deterministic deployment. + /// @return predicted The address of the puppet. + function predictDeterministicAddress(bytes32 salt) internal view returns (address predicted) { + return predictDeterministicAddress(salt, address(this)); + } + + /// @notice Calculate the deterministic address for a puppet deployment. + /// @param salt The salt to use for the deterministic deployment. + /// @param deployer The address of the deployer of the puppet. + /// @return predicted The address of the puppet. + function predictDeterministicAddress(bytes32 salt, address deployer) + internal + pure + returns (address predicted) + { + bytes32 hash = keccak256(abi.encodePacked(hex"ff", deployer, salt, CREATION_CODE_HASH)); + return address(uint160(uint256(hash))); + } + + function delegationCalldata(address delegateTo, bytes memory data) + internal + pure + returns (bytes memory payload) + { + return abi.encodePacked(bytes32(uint256(uint160(delegateTo))), data); + } +} +``` + +## Security Considerations + +The bytecode is made to resemble clone proxy's wherever it makes sense to simplify auditing. + +ABI-encoding the delegation address protects the deployer from being tricked by a 3rd party into calling the puppet and making it delegate to an arbitrary address. Such scenario would only be possible if the deployer called on the puppet a function with the selector `0x00000000`, which as of now doesn't come from any reasonably named function. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 22ace52a8260af46218e4412321de1d3cfa6da5d Mon Sep 17 00:00:00 2001 From: Dongri Jin Date: Wed, 28 Aug 2024 06:18:34 +0900 Subject: [PATCH 110/126] Add ERC: Transfer With Authorization Merged by EIP-Bot. --- ERCS/erc-7758.md | 530 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 530 insertions(+) create mode 100644 ERCS/erc-7758.md diff --git a/ERCS/erc-7758.md b/ERCS/erc-7758.md new file mode 100644 index 0000000000..1219feda4b --- /dev/null +++ b/ERCS/erc-7758.md @@ -0,0 +1,530 @@ +--- +eip: 7758 +title: Transfer With Authorization +description: Transfer fungible assets via a signed authorization. +author: Peter Jihoon Kim (@petejkim), Kevin Britz (@kbrizzle), David Knott (@DavidLKnott), Dongri Jin (@dongri) +discussions-to: https://ethereum-magicians.org/t/erc-7758-transfer-with-authorization/20859 +status: Draft +type: Standards Track +category: ERC +created: 2020-09-28 +requires: 20, 712 +--- + +## Abstract + +A set of functions to enable meta-transactions and atomic interactions with [ERC-20](./eip-20.md) token contracts via signatures conforming to the [EIP-712](./eip-712.md) typed message signing specification. + +This enables the user to: + +- delegate the gas payment to someone else, +- pay for gas in the token itself rather than in ETH, +- perform one or more token transfers and other operations in a single atomic transaction, +- transfer ERC-20 tokens to another address, and have the recipient submit the transaction, +- batch multiple transactions with minimal overhead, and +- create and perform multiple transactions without having to worry about them failing due to accidental nonce-reuse or improper ordering by the miner. + +## Motivation + +There is an existing spec, [EIP-2612](./eip-2612), that also allows meta-transactions, and it is encouraged that a contract implements both for maximum compatibility. The two primary differences between this spec and EIP-2612 are that: + +- EIP-2612 uses sequential nonces, but this uses random 32-byte nonces, and that +- EIP-2612 relies on the ERC-20 `approve`/`transferFrom` ("ERC-20 allowance") pattern. + +The biggest issue with the use of sequential nonces is that it does not allow users to perform more than one transaction at time without risking their transactions failing, because: + +- DApps may unintentionally reuse nonces that have not yet been processed in the blockchain. +- Miners may process the transactions in the incorrect order. + +This can be especially problematic if the gas prices are very high and transactions often get queued up and remain unconfirmed for a long time. Non-sequential nonces allow users to create as many transactions as they want at the same time. + +The ERC-20 allowance mechanism is susceptible to the multiple withdrawal attack, and encourages antipatterns such as the use of the "infinite" allowance. The wide-prevalence of upgradeable contracts have made the conditions favorable for these attacks to happen in the wild. + +The deficiencies of the ERC-20 allowance pattern brought about the development of alternative token standards such as the [ERC-777](./eip-777). However, they haven't been able to gain much adoption due to compatibility and potential security issues. + +## Specification + +### Event + +```solidity +event AuthorizationUsed( + address indexed authorizer, + bytes32 indexed nonce +); + +// keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") +bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; + +// keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") +bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8; + +/** + * @notice Returns the state of an authorization + * @dev Nonces are randomly generated 32-byte data unique to the authorizer's + * address + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @return True if the nonce is used + */ +function authorizationState( + address authorizer, + bytes32 nonce +) external view returns (bool); + +/** + * @notice Execute a transfer with a signed authorization + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ +function transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s +) external; + +/** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address matches + * the caller of this function to prevent front-running attacks. (See security + * considerations) + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ +function receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s +) external; +``` + +**Optional:** + +``` +event AuthorizationCanceled( + address indexed authorizer, + bytes32 indexed nonce +); + +// keccak256("CancelAuthorization(address authorizer,bytes32 nonce)") +bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429; + +/** + * @notice Attempt to cancel an authorization + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ +function cancelAuthorization( + address authorizer, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s +) external; +``` + + +The arguments `v`, `r`, and `s` must be obtained using the [EIP-712](./eip-712.md) typed message signing spec. + +**Example:** + +``` +DomainSeparator := Keccak256(ABIEncode( + Keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ), + Keccak256("USD Coin"), // name + Keccak256("2"), // version + 1, // chainId + 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 // verifyingContract +)) +``` + +With the domain separator, the typehash, which is used to identify the type of the EIP-712 message being used, and the values of the parameters, you are able to derive a Keccak-256 hash digest which can then be signed using the token holder's private key. + +**Example:** + +``` +// Transfer With Authorization +TypeHash := Keccak256( + "TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)" +) +Params := { From, To, Value, ValidAfter, ValidBefore, Nonce } + +// ReceiveWithAuthorization +TypeHash := Keccak256( + "ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)" +) +Params := { From, To, Value, ValidAfter, ValidBefore, Nonce } + +// CancelAuthorization +TypeHash := Keccak256( + "CancelAuthorization(address authorizer,bytes32 nonce)" +) +Params := { Authorizer, Nonce } +``` + +``` +// "โ€–" denotes concatenation. +Digest := Keecak256( + 0x1901 โ€– DomainSeparator โ€– Keccak256(ABIEncode(TypeHash, Params...)) +) + +{ v, r, s } := Sign(Digest, PrivateKey) +``` + +Smart contract functions that wrap `receiveWithAuthorization` call may choose to reduce the number of arguments by accepting the full ABI-encoded set of arguments for the `receiveWithAuthorization` call as a single argument of the type `bytes`. + +**Example:** + +```solidity +// keccak256("receiveWithAuthorization(address,address,uint256,uint256,uint256,bytes32,uint8,bytes32,bytes32)")[0:4] +bytes4 private constant _RECEIVE_WITH_AUTHORIZATION_SELECTOR = 0xef55bec6; + +function deposit(address token, bytes calldata receiveAuthorization) + external + nonReentrant +{ + (address from, address to, uint256 amount) = abi.decode( + receiveAuthorization[0:96], + (address, address, uint256) + ); + require(to == address(this), "Recipient is not this contract"); + + (bool success, ) = token.call( + abi.encodePacked( + _RECEIVE_WITH_AUTHORIZATION_SELECTOR, + receiveAuthorization + ) + ); + require(success, "Failed to transfer tokens"); + + ... +} +``` + +### Use with web3 providers + +The signature for an authorization can be obtained using a web3 provider with the `eth_signTypedData{_v4}` method. + +**Example:** + +```javascript +const data = { + types: { + EIP712Domain: [ + { name: "name", type: "string" }, + { name: "version", type: "string" }, + { name: "chainId", type: "uint256" }, + { name: "verifyingContract", type: "address" }, + ], + TransferWithAuthorization: [ + { name: "from", type: "address" }, + { name: "to", type: "address" }, + { name: "value", type: "uint256" }, + { name: "validAfter", type: "uint256" }, + { name: "validBefore", type: "uint256" }, + { name: "nonce", type: "bytes32" }, + ], + }, + domain: { + name: tokenName, + version: tokenVersion, + chainId: selectedChainId, + verifyingContract: tokenAddress, + }, + primaryType: "TransferWithAuthorization", + message: { + from: userAddress, + to: recipientAddress, + value: amountBN.toString(10), + validAfter: 0, + validBefore: Math.floor(Date.now() / 1000) + 3600, // Valid for an hour + nonce: Web3.utils.randomHex(32), + }, +}; + +const signature = await ethereum.request({ + method: "eth_signTypedData_v4", + params: [userAddress, JSON.stringify(data)], +}); + +const v = "0x" + signature.slice(130, 132); +const r = signature.slice(0, 66); +const s = "0x" + signature.slice(66, 130); +``` + +## Rationale + +### Unique Random Nonce, Instead of Sequential Nonce + +One might say transaction ordering is one reason why sequential nonces are preferred. However, sequential nonces do not actually help achieve transaction ordering for meta transactions in practice: + +- For native Ethereum transactions, when a transaction with a nonce value that is too-high is submitted to the network, it will stay pending until the transactions consuming the lower unused nonces are confirmed. +- However, for meta-transactions, when a transaction containing a sequential nonce value that is too high is submitted, instead of staying pending, it will revert and fail immediately, resulting in wasted gas. +- The fact that miners can also reorder transactions and include them in the block in the order they want (assuming each transaction was submitted to the network by different meta-transaction relayers) also makes it possible for the meta-transactions to fail even if the nonces used were correct. (e.g. User submits nonces 3, 4 and 5, but miner ends up including them in the block as 4,5,3, resulting in only 3 succeeding) +- Lastly, when using different applications simultaneously, in absence of some sort of an off-chain nonce-tracker, it is not possible to determine what the correct next nonce value is if there exists nonces that are used but haven't been submitted and confirmed by the network. +- Under high gas price conditions, transactions can often "get stuck" in the pool for a long time. Under such a situation, it is much more likely for the same nonce to be unintentionally reused twice. For example, if you make a meta-transaction that uses a sequential nonce from one app, and switch to another app to make another meta-transaction before the previous one confirms, the same nonce will be used if the app relies purely on the data available on-chain, resulting in one of the transactions failing. +- In conclusion, the only way to guarantee transaction ordering is for relayers to submit transactions one at a time, waiting for confirmation between each submission (and the order in which they should be submitted can be part of some off-chain metadata), rendering sequential nonce irrelevant. + +### Valid After and Valid Before + +- Relying on relayers to submit transactions for you means you may not have exact control over the timing of transaction submission. +- These parameters allow the user to schedule a transaction to be only valid in the future or before a specific deadline, protecting the user from potential undesirable effects that may be caused by the submission being made either too late or too early. + +### EIP-712 + +- EIP-712 ensures that the signatures generated are valid only for this specific instance of the token contract and cannot be replayed on a different network with a different chain ID. +- This is achieved by incorporating the contract address and the chain ID in a Keccak-256 hash digest called the domain separator. The actual set of parameters used to derive the domain separator is up to the implementing contract, but it is highly recommended that the fields `verifyingContract` and `chainId` are included. + +## Backwards Compatibility + +New contracts benefit from being able to directly utilize this proposal in order to create atomic transactions, but existing contracts may still rely on the conventional [ERC-20](./eip-20.md) allowance pattern (`approve`/`transferFrom`). + +In order to add support for this proposal to existing contracts ("parent contract") that use the ERC-20 allowance pattern, a forwarding contract ("forwarder") can be constructed that takes an authorization and does the following: + +1. Extract the user and deposit amount from the authorization +2. Call `receiveWithAuthorization` to transfer specified funds from the user to the forwarder +3. Approve the parent contract to spend funds from the forwarder +4. Call the method on the parent contract that spends the allowance set from the forwarder +5. Transfer the ownership of any resulting tokens back to the user + +**Example:** + +```solidity +interface IDeFiToken { + function deposit(uint256 amount) external returns (uint256); + + function transfer(address account, uint256 amount) + external + returns (bool); +} + +contract DepositForwarder { + bytes4 private constant _RECEIVE_WITH_AUTHORIZATION_SELECTOR = 0xef55bec6; + + IDeFiToken private _parent; + IERC20 private _token; + + constructor(IDeFiToken parent, IERC20 token) public { + _parent = parent; + _token = token; + } + + function deposit(bytes calldata receiveAuthorization) + external + nonReentrant + returns (uint256) + { + (address from, address to, uint256 amount) = abi.decode( + receiveAuthorization[0:96], + (address, address, uint256) + ); + require(to == address(this), "Recipient is not this contract"); + + (bool success, ) = address(_token).call( + abi.encodePacked( + _RECEIVE_WITH_AUTHORIZATION_SELECTOR, + receiveAuthorization + ) + ); + require(success, "Failed to transfer to the forwarder"); + + require( + _token.approve(address(_parent), amount), + "Failed to set the allowance" + ); + + uint256 tokensMinted = _parent.deposit(amount); + require( + _parent.transfer(from, tokensMinted), + "Failed to transfer the minted tokens" + ); + + uint256 remainder = _token.balanceOf(address(this); + if (remainder > 0) { + require( + _token.transfer(from, remainder), + "Failed to refund the remainder" + ); + } + + return tokensMinted; + } +} +``` + +## Reference Implementation + +### `EIP7758.sol` + +```solidity +abstract contract EIP7758 is IERC20Transfer, EIP712Domain { + // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") + bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; + + // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") + bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8; + + mapping(address => mapping(bytes32 => bool)) internal _authorizationStates; + + event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce); + + string internal constant _INVALID_SIGNATURE_ERROR = "EIP7758: invalid signature"; + + function authorizationState(address authorizer, bytes32 nonce) + external + view + returns (bool) + { + return _authorizationStates[authorizer][nonce]; + } + + function transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) external { + require(now > validAfter, "EIP7758: authorization is not yet valid"); + require(now < validBefore, "EIP7758: authorization is expired"); + require( + !_authorizationStates[from][nonce], + "EIP7758: authorization is used" + ); + + bytes memory data = abi.encode( + TRANSFER_WITH_AUTHORIZATION_TYPEHASH, + from, + to, + value, + validAfter, + validBefore, + nonce + ); + require( + EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, + "EIP7758: invalid signature" + ); + + _authorizationStates[from][nonce] = true; + emit AuthorizationUsed(from, nonce); + + _transfer(from, to, value); + } +} +``` + +### `IERC20Transfer.sol` + +```solidity +abstract contract IERC20Transfer { + function _transfer( + address sender, + address recipient, + uint256 amount + ) internal virtual; +} +``` + +### `EIP712Domain.sol` +```solidity +abstract contract EIP712Domain { + bytes32 public DOMAIN_SEPARATOR; +} +``` + +### `EIP712.sol` + +```solidity +library EIP712 { + // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") + bytes32 public constant EIP712_DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; + + function makeDomainSeparator(string memory name, string memory version) + internal + view + returns (bytes32) + { + uint256 chainId; + assembly { + chainId := chainid() + } + + return + keccak256( + abi.encode( + EIP712_DOMAIN_TYPEHASH, + keccak256(bytes(name)), + keccak256(bytes(version)), + bytes32(chainId), + address(this) + ) + ); + } + + function recover( + bytes32 domainSeparator, + uint8 v, + bytes32 r, + bytes32 s, + bytes memory typeHashAndData + ) internal pure returns (address) { + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + domainSeparator, + keccak256(typeHashAndData) + ) + ); + address recovered = ecrecover(digest, v, r, s); + require(recovered != address(0), "EIP712: invalid signature"); + return recovered; + } +} +``` + +## Security Considerations + +Use `receiveWithAuthorization` instead of `transferWithAuthorization` when calling from other smart contracts. It is possible for an attacker watching the transaction pool to extract the transfer authorization and front-run the `transferWithAuthorization` call to execute the transfer without invoking the wrapper function. This could potentially result in unprocessed, locked up deposits. `receiveWithAuthorization` prevents this by performing an additional check that ensures that the caller is the payee. Additionally, if there are multiple contract functions accepting receive authorizations, the app developer could dedicate some leading bytes of the nonce could as the identifier to prevent cross-use. + +When submitting multiple transfers simultaneously, be mindful of the fact that relayers and miners will decide the order in which they are processed. This is generally not a problem if the transactions are not dependent on each other, but for transactions that are highly dependent on each other, it is recommended that the signed authorizations are submitted one at a time. + +The zero address must be rejected when using `ecrecover` to prevent unauthorized transfers and approvals of funds from the zero address. The built-in `ecrecover` returns the zero address when a malformed signature is provided. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 4c9859f541f5eea0ba865c1b4c5066ac7520aa1a Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:42:47 -0400 Subject: [PATCH 111/126] Update ERC-3770: Fix broken links and formatting (#531) * Update ERC-3770: Fix broken links and formatting * use relative links only, for website --- ERCS/erc-3770.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/ERCS/erc-3770.md b/ERCS/erc-3770.md index fbed54b7c5..7b3a48ad43 100644 --- a/ERCS/erc-3770.md +++ b/ERCS/erc-3770.md @@ -12,8 +12,8 @@ created: 2021-08-26 ## Abstract -[ERC-3770](./eip-3770.md) introduces a new address standard to be adapted by wallets and dApps to display chain-specific addresses by using a human-reacable prefix. - +[ERC-3770](./eip-3770.md) introduces a new address standard to be adapted by wallets and dApps to display chain-specific addresses by using a human-readable prefix. + ## Motivation The need for this proposal emerges from the increasing adoption of non-Ethereum Mainnet chains that use the Ethereum Virtual Machine (EVM). In this context, addresses become ambiguous, as the same address may refer to an EOA on chain X or a smart contract on chain Y. This will eventually lead to Ethereum users losing funds due to human error. For example, users sending funds to a smart contract wallet address which was not deployed on a particular chain. @@ -36,13 +36,8 @@ Chain-specific address = "`shortName`" "`:`" "`address`" ### Semantics -``` - -`shortName` is mandatory and MUST be a valid chain short name from https://github.com/ethereum-lists/chains - -`address` is mandatory and MUST be a [ERC-55](./eip-55.md) compatible hexadecimal address - -``` +* `shortName` is mandatory and MUST be a valid chain short name from https://github.com/ethereum-lists/chains +* `address` is mandatory and MUST be a [ERC-55](./eip-55.md) compatible hexadecimal address ### Examples From fb96eca5df44178ada0d1f0fe00fd24d849c37cb Mon Sep 17 00:00:00 2001 From: William Entriken Date: Fri, 30 Aug 2024 20:40:59 -0400 Subject: [PATCH 112/126] Update ERC-7585: fix typo Merged by EIP-Bot. --- ERCS/erc-7585.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7585.md b/ERCS/erc-7585.md index 0e7eedf6b3..ddbd016e93 100644 --- a/ERCS/erc-7585.md +++ b/ERCS/erc-7585.md @@ -13,7 +13,7 @@ requires: 165, 721, 1155 ## Abstract -This proposal introduces a design for `minimum value selection` storage proofs on Merkle trees. The design consists of two main components: +This proposal introduces a design for "minimum value selection" storage proofs on Merkle trees. The design consists of two main components: 1. A hashing algorithm termed MixHash, aimed to replace the commonly used Keccak256 and SHA256 algorithms. 2. Public data storage proofs. This enables anyone to present a proof to a public network, verifying their possession of a copy of specific public data marked by MixHash. @@ -192,7 +192,7 @@ def generateProofWithPow(mixHash, blockHeight,file) noise = noise + 1 m_path = getMerkleTreePath(chunk_hash_array, min_index) - return strorage_proof(mixHash, blockHeight, min_index, m_path, min_chunk,noise) + return storage_proof(mixHash, blockHeight, min_index, m_path, min_chunk,noise) ``` Applying this mechanism increases the cost of generating storage proofs, which deviates from our initial intent to reduce the widespread effective storage of public data. Moreover, heavily relying on a PoW-based economic model may allow Suppliers with significant advantages in PoW through specialized hardware to disrupt the basic participatory nature of the game, reducing the widespread distribution of public data. Therefore, it is advised not to enable the PoW mechanism unless absolutely necessary. @@ -262,12 +262,15 @@ PublicDataProofDemo includes test cases written using Hardhat. ## Reference Implementation PublicDataProof Demo + - A standard reference implementation DMC public data inscription + - Based on public data storage certification, a complete economic model and gameplay has been designed on ETH network and BTC inscription network Learn more background and existing attempts + - DMC Main Chain - CYFS @@ -280,4 +283,3 @@ The design of MixHash can support storage proofs for private files, but this req ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). - From a0e9b17aa012c521f054bb9944dfc8594952ed49 Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Tue, 3 Sep 2024 08:59:20 -0400 Subject: [PATCH 113/126] Update ERC-7677: postopgas callout Merged by EIP-Bot. --- ERCS/erc-7677.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ERCS/erc-7677.md b/ERCS/erc-7677.md index 3bd5cfdf6a..39c9f0a256 100644 --- a/ERCS/erc-7677.md +++ b/ERCS/erc-7677.md @@ -32,7 +32,9 @@ We define two JSON-RPC methods to be implemented by paymaster web services. Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation. Accepts an unsigned user operation, entrypoint address, chain id, and a context object. Paymaster service providers can define fields that app developers should use in the context object. -This method MAY return paymaster-specific gas values if applicable to the provided EntryPoint version. For example, if provided with EntryPoint v0.7, this method MAY return `paymasterVerificationGasLimit` and `paymasterPostOpGasLimit` values. The wallet SHOULD use these provided gas values when submitting the `UserOperation` to a bundler for gas estimation. +This method MAY return paymaster-specific gas values if applicable to the provided EntryPoint version. For example, if provided with EntryPoint v0.7, this method MAY return `paymasterVerificationGasLimit`. Furthermore, for EntryPoint v0.7, this method MUST return a value for `paymasterPostOpGasLimit`. This is because it is the paymaster that pays the postOpGasLimit, so it cannot trust the wallet to estimate this amount. + +The wallet SHOULD use these provided gas values when submitting the `UserOperation` to a bundler for gas estimation. This method MAY also return a `sponsor` object with a `name` field and an optional `icon` field. The `name` field is the name of the party sponsoring the transaction, and the `icon` field is a URI pointing to an image. Wallet developers MAY choose to display sponsor information to users. The `icon` string MUST be a data URI as defined in [RFC-2397]. The image SHOULD be a square with 96x96px minimum resolution. The image format is RECOMMENDED to be either lossless or vector based such as PNG, WebP or SVG to make the image easy to render on the wallet. Since SVG images can execute Javascript, wallets MUST render SVG images using the `` tag to ensure no untrusted Javascript execution can occur. From 972c598493f8469f9ee95de4bfcb10c7abf7b994 Mon Sep 17 00:00:00 2001 From: Gavin Date: Tue, 3 Sep 2024 22:02:51 +0800 Subject: [PATCH 114/126] Update ERC-7588: Move to Final Merged by EIP-Bot. --- ERCS/erc-7588.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ERCS/erc-7588.md b/ERCS/erc-7588.md index 62ce8f5d9b..212f051034 100644 --- a/ERCS/erc-7588.md +++ b/ERCS/erc-7588.md @@ -4,8 +4,7 @@ title: Blob Transactions Metadata JSON Schema description: Attaching metadata to blobs carried by blob transactions author: Gavin Fu (@gavfu), Leo Wang (@wanglie1986), Bova Chen (@appoipp), Aiden X (@4ever9) discussions-to: https://ethereum-magicians.org/t/erc7588-attaching-metadata-to-blobs-carried-by-blob-transactions/17873 -status: Last Call -last-call-deadline: 2024-09-03 +status: Final type: Standards Track category: ERC created: 2024-01-01 From 7da20a351d8d3c79454ab35b283868166ea81266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernani=20S=C3=A3o=20Thiago?= Date: Tue, 3 Sep 2024 08:20:56 -0600 Subject: [PATCH 115/126] Update ERC-7432: Move to Last Call Merged by EIP-Bot. --- ERCS/erc-7432.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ERCS/erc-7432.md b/ERCS/erc-7432.md index 47c2041e4b..0708c4c36a 100644 --- a/ERCS/erc-7432.md +++ b/ERCS/erc-7432.md @@ -4,7 +4,8 @@ title: Non-Fungible Token Roles description: Role Management for NFTs. Enables accounts to share the utility of NFTs via expirable role assignments. author: Ernani Sรฃo Thiago (@ernanirst), Daniel Lima (@karacurt) discussions-to: https://ethereum-magicians.org/t/eip-7432-non-fungible-token-roles/15298 -status: Review +status: Last Call +last-call-deadline: 2024-09-17 type: Standards Track category: ERC created: 2023-07-14 @@ -337,7 +338,7 @@ Automatic expiration is implemented via the `grantRole` and `roleExpirationDate` for setting the expiration date, and `roleExpirationDate` allow developers to check whether the role is expired. Since `uint256` is not natively supported by most programming languages, dates are represented as `uint64` on this standard. The maximum UNIX timestamp represented by a `uint64` is about the year `584,942,417,355`, which should be enough to be -considered "permanent". For this reason, it's RECOMMENDED using `type(uint64).max` to support use cases that require a +considered "permanent". For this reason, it's recommended using `type(uint64).max` to support use cases that require a role never to expire. ### Revocable Roles @@ -347,8 +348,8 @@ others, the recipient may require assurance that the role cannot be revoked. The to the `grantRole` function to specify whether a role can be revoked prematurely, enabling the standard to support both use cases. -Regardless of the value of `revocable`, it's RECOMMENDED always to enable the `recipient` to revoke roles, allowing -them to eliminate undesirable assignments. +Regardless of the value of `revocable`, it's recommended always to enable the `recipient` to revoke roles, allowing them +to eliminate undesirable assignments. ### Custom Data From 1aaec254cbbf60ea2dd62ece58d9688ef3174c5e Mon Sep 17 00:00:00 2001 From: Peersky <61459744+peersky@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:22:04 +0300 Subject: [PATCH 116/126] Add ERC: Code Index Merged by EIP-Bot. --- ERCS/erc-7744.md | 118 +++++++++++++++++++++++++++++++++ assets/erc-7744/CodeIndex.sol | 38 +++++++++++ assets/erc-7744/ICodeIndex.sol | 11 +++ 3 files changed, 167 insertions(+) create mode 100644 ERCS/erc-7744.md create mode 100644 assets/erc-7744/CodeIndex.sol create mode 100644 assets/erc-7744/ICodeIndex.sol diff --git a/ERCS/erc-7744.md b/ERCS/erc-7744.md new file mode 100644 index 0000000000..2d48b0bac1 --- /dev/null +++ b/ERCS/erc-7744.md @@ -0,0 +1,118 @@ +--- +eip: 7744 +title: Code Index +description: Global repository of bytecode, enabling developers, auditors, and researchers to find, analyze, and reuse bytecode efficiently. +author: Tim Pechersky (@peersky) +discussions-to: https://ethereum-magicians.org/t/erc-7744-code-index/20569 +status: Draft +type: Standards Track +category: ERC +created: 2024-07-16 +--- + +## Abstract + +This EIP defines a standard interface for indexing smart contracts on Ethereum by their bytecode hash. This enables trustless discovery and verification of contract code, facilitating use cases like bytecode signing, whitelisting, and decentralized distribution mechanisms. + +## Motivation + +Existing contract discovery relies on addresses, which are non-deterministic and can be obfuscated through proxies. Indexing by bytecode hash provides a deterministic and tamper-proof way to identify and verify contract code, enhancing security and trust in the Ethereum ecosystem. + +Consider a security auditor who wants to attest to the integrity of a contract's code. By referencing bytecode hashes, auditors can focus their audit on the bytecode itself, without needing to assess deployment parameters or storage contents. This method verifies the integrity of a contract's codebase without auditing the entire contract state. + +Additionally, bytecode referencing allows whitelist contracts before deployment, allowing developers to get pre-approval for their codebase without disclosing the code itself, or even pre-setup infrastructure that will change it behavior upon adding some determined functionality on chain. + +For developers relying on extensive code reuse, bytecode referencing protects against malicious changes that can occur with address-based referencing through proxies. This builds long-term trust chains extending to end-user applications. + +For decentralized application (dApp) developers, a code index can save gas costs by allowing them to reference existing codebases instead of redeploying them, optimizing resource usage. This can be useful for dApps that rely on extensive re-use of same codebase as own dependencies. + +### Why this registry needs to be an ERC + +The Code Index is essential for trustless and secure smart contract development. By standardizing the interface for indexing contracts by their bytecode, developers can easily integrate this feature into their smart contracts, enhancing the security and trustworthiness of the Ethereum ecosystem. + +Its simplicity and generic nature make it suitable for a wide range of applications. The ability to globally reference the same codebase makes it an ideal candidate for standardization. + +Ultimately, this feature should be incorporated into EIP standards, as it is a fundamental building block for trustless and secure smart contract development. This standard is a step towards this goal. + + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity =0.8.20; + +interface ICodeIndex { + event Indexed(address indexed container, bytes32 indexed codeHash); + error alreadyExists(bytes32 id, address source); + + function register(address container) external; + + function get(bytes32 id) external view returns (address); +} + +/** + * @title Byte Code Indexer Contract + * @notice You can use this contract to index contracts by their bytecode. + * @dev This allows to query contracts by their bytecode instead of addresses. + * @author Tim Pechersky (@Peersky) + */ +contract CodeIndex is ICodeIndex { + mapping(bytes32 => address) private index; + + /** + * @notice Registers a contract in the index by its bytecode hash + * @param container The contract to register + * @dev `msg.codeHash` will be used + * @dev It will revert if the contract is already indexed + */ + function register(address container) external { + address etalon = index[container.codehash]; + if (etalon != address(0)) { + revert alreadyExists(container.codehash, etalon); + } + index[container.codehash] = container; + emit Indexed(container, container.codehash); + } + + /** + * @notice Returns the contract address by its bytecode hash + * @dev returns zero if the contract is not indexed + * @param id The bytecode hash + * @return The contract address + */ + function get(bytes32 id) external view returns (address) { + return index[id]; + } +} + +``` + +### Deployment method + +The `CodeIndex` contract is deployed at: `0xc0D31d398c5ee86C5f8a23FA253ee8a586dA03Ce` using `CREATE2` via the deterministic deployer at `0x4e59b44847b379578588920ca78fbf26c0b4956c` with a salt of `0x220a70730c743a005cfd55180805d2c0d5b8c7695c5496100dcffa91c02befce` is obtained by seeking a vanity address with meaningful name "Code ID (`c0D31d`). + +## Rationale + +**Bytecode over Addresses**: Bytecode is deterministic and can be verified on-chain, while addresses are opaque and mutable. + +**Reverting on re-indexing**: There is small, yet non-zero probability of hash collision attack. Disallowing updates to indexed location of bytecode coupes with this. + +**Simple Interface**: The interface is minimal and focused to maximize composability and ease of implementation. + +**Library Implementation**: Implementing this as a library would limit its impact, making code reuse more difficult and lacking a single, official source of truth. By establishing this as an ERC, we ensure standardization and widespread adoption, driving the ecosystem forward. + +## Reference Implementation + +Reference implementation of the Code Index can be found in the assets folder. There you can find the [interface](../assets/eip-7744/ICodeIndex.sol) and the [implementation](../assets/eip-7744/CodeIndex.sol) of the Code Index. + +## Security Considerations + +**Malicious Code**: The index does NOT guarantee the safety or functionality of indexed contracts. Users MUST exercise caution and perform their own due diligence before interacting with indexed contracts. + +**Storage contents of registered contracts**: The index only refers to the bytecode of the contract, not the storage contents. This means that the contract state is not indexed and may change over time. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/assets/erc-7744/CodeIndex.sol b/assets/erc-7744/CodeIndex.sol new file mode 100644 index 0000000000..bd7a076378 --- /dev/null +++ b/assets/erc-7744/CodeIndex.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity 0.8.20; +import "./ICodeIndex.sol"; + +/** + * @title Byte Code Indexer Contract + * @notice You can use this contract to index contracts by their bytecode. + * @dev This allows to query contracts by their bytecode instead of addresses. + * @author Tim Pechersky (@Peersky) + */ +contract CodeIndex is ICodeIndex { + mapping(bytes32 => address) private index; + + /** + * @notice Registers a contract in the index by its bytecode hash + * @param container The contract to register + * @dev `msg.codeHash` will be used + * @dev It will revert if the contract is already indexed + */ + function register(address container) external { + address etalon = index[container.codehash]; + if (etalon != address(0)) { + revert alreadyExists(container.codehash, etalon); + } + index[container.codehash] = container; + emit Indexed(container, container.codehash); + } + + /** + * @notice Returns the contract address by its bytecode hash + * @dev returns zero if the contract is not indexed + * @param id The bytecode hash + * @return The contract address + */ + function get(bytes32 id) external view returns (address) { + return index[id]; + } +} \ No newline at end of file diff --git a/assets/erc-7744/ICodeIndex.sol b/assets/erc-7744/ICodeIndex.sol new file mode 100644 index 0000000000..aadd4f4f3d --- /dev/null +++ b/assets/erc-7744/ICodeIndex.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity 0.8.20; + +interface ICodeIndex { + event Indexed(address indexed container, bytes32 indexed codeHash); + error alreadyExists(bytes32 id, address source); + + function register(address container) external; + + function get(bytes32 id) external view returns (address); +} \ No newline at end of file From 0fe4e1c19c2dc6c81da1cad5370656cf82c60491 Mon Sep 17 00:00:00 2001 From: Peersky <61459744+peersky@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:28:43 +0300 Subject: [PATCH 117/126] Add ERC: Composable Security Middleware Hooks Merged by EIP-Bot. --- ERCS/erc-7746.md | 101 +++++++++++++ assets/erc-7746/ILayer.sol | 21 +++ assets/erc-7746/README.md | 7 + assets/erc-7746/test/AccessLayers.sol | 21 +++ assets/erc-7746/test/Drainer.sol | 13 ++ assets/erc-7746/test/IVictim.sol | 6 + assets/erc-7746/test/LibAccessLayers.sol | 150 +++++++++++++++++++ assets/erc-7746/test/MockERC20.sol | 17 +++ assets/erc-7746/test/Protected.sol | 30 ++++ assets/erc-7746/test/RateLimitLayer.sol | 36 +++++ assets/erc-7746/test/deploy/drainer.ts | 19 +++ assets/erc-7746/test/deploy/layered_proxy.ts | 40 +++++ assets/erc-7746/test/deploy/simple_layer.ts | 18 +++ assets/erc-7746/test/test.ts | 40 +++++ 14 files changed, 519 insertions(+) create mode 100644 ERCS/erc-7746.md create mode 100644 assets/erc-7746/ILayer.sol create mode 100644 assets/erc-7746/README.md create mode 100644 assets/erc-7746/test/AccessLayers.sol create mode 100644 assets/erc-7746/test/Drainer.sol create mode 100644 assets/erc-7746/test/IVictim.sol create mode 100644 assets/erc-7746/test/LibAccessLayers.sol create mode 100644 assets/erc-7746/test/MockERC20.sol create mode 100644 assets/erc-7746/test/Protected.sol create mode 100644 assets/erc-7746/test/RateLimitLayer.sol create mode 100644 assets/erc-7746/test/deploy/drainer.ts create mode 100644 assets/erc-7746/test/deploy/layered_proxy.ts create mode 100644 assets/erc-7746/test/deploy/simple_layer.ts create mode 100644 assets/erc-7746/test/test.ts diff --git a/ERCS/erc-7746.md b/ERCS/erc-7746.md new file mode 100644 index 0000000000..cf944f6d71 --- /dev/null +++ b/ERCS/erc-7746.md @@ -0,0 +1,101 @@ +--- +eip: 7746 +title: Composable Security Middleware Hooks +description: An interface for composable, runtime security checks in smart contracts. +author: Tim Pechersky (@peersky) +discussions-to: https://ethereum-magicians.org/t/erc-7746-composable-security-middleware-hooks/19471 +status: Draft +type: Standards Track +category: ERC +created: 2024-07-17 +--- + +## Abstract + +This EIP proposes a standard interface, `ILayer`, for implementing composable security layers in smart contracts. These layers act as middleware, enabling runtime validation of function calls before and after execution, independent of the protected contract's logic. This approach facilitates modular security, allowing independent providers to manage and upgrade security layers across multiple contracts. + +## Motivation + +Current smart contract security practices often rely on monolithic validation logic within the contract itself. This can lead to tightly coupled code, making it difficult to isolate and address security concerns. Existing ERCs already are using something that can be seen as specific implementation of such layers wrapping: [ERC-4337](./eip-4337.md) describes requirements to perform validate user operations in a separate contract, and later elaborates same need to paymaster validation, that can be seen as a separate layers of a generic system. +The Security Layers Standard introduces a modular approach, enabling: + +- **Independent Security Providers:** Specialized security providers can focus on developing and maintaining specific security checks. +- **Composable Security:** Layers can be combined to create comprehensive security profiles tailored to individual contract needs. +- **Upgradability:** Security layers can be updated without requiring changes to the protected contract. +- **Flexibility:** Layers can perform a wide range of validation checks, including access control, input sanitization, output verification, and more. + +Having a generalized standard for such layers can help to build more secure and modular systems as well as enable security providers to build generic, service-oriented security oracle solutions. + +## Specification + +A contract implementing the `ILayer` interface MUST provide two functions: + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity 0.8.20; + +interface ILayer { + /// @notice Validates a function call before execution. + /// @param configuration Layer-specific configuration data. + /// @param selector The function selector being called. + /// @param sender The address initiating the call. + /// @param value The amount of ETH sent with the call (if any). + /// @param data The calldata for the function call. + /// @return beforeCallResult Arbitrary data to be passed to `afterCallValidation`. + /// @dev MUST revert if validation fails. + function beforeCall( + bytes memory configuration, + bytes4 selector, + address sender, + uint256 value, + bytes memory data + ) external returns (bytes memory); + + /// @notice Validates a function call after execution. + /// @param configuration Layer-specific configuration data. + /// @param selector The function selector being called. + /// @param sender The address initiating the call. + /// @param value The amount of ETH sent with the call (if any). + /// @param data The calldata for the function call. + /// @param beforeCallResult The data returned by `beforeCallValidation`. + /// @dev MUST revert if validation fails. + function afterCall( + bytes memory configuration, + bytes4 selector, + address sender, + uint256 value, + bytes memory data, + bytes memory beforeCallResult + ) external; +} + +``` + +A protected contract MAY integrate security layers by calling the `beforeCallValidation` function before executing its logic and the `afterCallValidation` function afterwards. Multiple layers can be registered and executed in a defined order. The protected contract MUST revert if any layer reverts. + +## Rationale + +**Flexibility**: The `layerConfig` parameter allows for layer-specific customization, enabling a single layer implementation to serve multiple contracts with varying requirements. + +**non-static calls**: Layers can maintain their own state, allowing for more complex validation logic (e.g., rate limiting, usage tracking). + +**Strict Validation**: Reverts on validation failure ensure a fail-safe mechanism, preventing execution of potentially harmful transactions. + +**Gas Costs**: Layers naturally will have gas costs associated with their execution. However, the benefits of enhanced security and modularity outweigh these costs, especially as blockchain technology continues to evolve and we expect gas costs to decrease over time. + +## Reference Implementation + +A reference implementation of the `ILayer` interface and a sample protected contract can be found in the repository: +In the [`../assets/eip-7746/ILayer.sol`](../assets/eip-7746/ILayer.sol) a reference interface is provided. + +In this test, a [`Protected.sol`](../assets/eip-7746/test/Protected.sol) contract is protected by a [`RateLimitLayer.sol`](../assets/eip-7746/test/RateLimitLayer.sol) layer. The `RateLimitLayer` implements the `ILayer` interface and enforces a rate which client has configured. +The `Drainer` simulates a vulnerable contract that acts in a malicious way. In the `test.ts` The `Drainer` contract is trying to drain the funds from the `Protected` contract. It is assumed that `Protected` contract has bug that allows partial unauthorized access to the state. +The `RateLimitLayer` is configured to allow only 10 transactions per block from same sender. The test checks that the `Drainer` contract is not able to drain the funds from the `Protected` contract. + +## Security Considerations + +**Layer Trust**: Thoroughly audit and vet any security layer before integrating it into your contract. Malicious layers can compromise contract security. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/assets/erc-7746/ILayer.sol b/assets/erc-7746/ILayer.sol new file mode 100644 index 0000000000..283773b31c --- /dev/null +++ b/assets/erc-7746/ILayer.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity =0.8.20; + +interface ILayer { + function beforeCall( + bytes memory configuration, + bytes4 selector, + address sender, + uint256 value, + bytes memory data + ) external returns (bytes memory); + + function afterCall( + bytes memory configuration, + bytes4 selector, + address sender, + uint256 value, + bytes memory data, + bytes memory beforeCallResult + ) external; +} diff --git a/assets/erc-7746/README.md b/assets/erc-7746/README.md new file mode 100644 index 0000000000..2d1594c300 --- /dev/null +++ b/assets/erc-7746/README.md @@ -0,0 +1,7 @@ +# References for ERC-7746 + +In this directory you can find a reference implementation of the ILayer interface and a sample MockERC20 contract. + +In this test, a [Protected.sol](./test/Protected.sol) contract is protected by a [RateLimitLayer.sol](./test/RateLimitLayer.sol) layer. The RateLimitLayer implements the ILayer interface and enforces a rate which client has configured. +The Drainer simulates a vulnerable contract that acts in a malicious way. In the `test.ts` The Drainer contract is trying to drain the funds from the Protected contract. It is assumed that Protected contract has bug that allows partial unauthorized access to the state. +The RateLimitLayer is configured to allow only 10 transactions per block from same sender. The test checks that the Drainer contract is not able to drain the funds from the Protected contract. \ No newline at end of file diff --git a/assets/erc-7746/test/AccessLayers.sol b/assets/erc-7746/test/AccessLayers.sol new file mode 100644 index 0000000000..c7a08503ed --- /dev/null +++ b/assets/erc-7746/test/AccessLayers.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 + +/** + * Author: @Peersky https://github.com/peersky + */ + +pragma solidity =0.8.20; +import "./LibAccessLayers.sol"; + +abstract contract AccessLayers { + modifier layers( + bytes4 _selector, + address sender, + bytes calldata data, + uint256 value + ) { + bytes[] memory layerReturns = LibAccessLayers.beforeCall(_selector, sender, data, value); + _; + LibAccessLayers.afterCall(_selector, sender, data, value, layerReturns); + } +} diff --git a/assets/erc-7746/test/Drainer.sol b/assets/erc-7746/test/Drainer.sol new file mode 100644 index 0000000000..7eb8d05422 --- /dev/null +++ b/assets/erc-7746/test/Drainer.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: CC0-1.0 +import "./MockERC20.sol"; +pragma solidity =0.8.20; + +contract Drainer { + constructor() {} + + function drain(address payable victim, uint256 cycles) public { + for (uint256 i = 0; i < cycles; i++) { + MockERC20(victim).mint(address(this), 1); + } + } +} diff --git a/assets/erc-7746/test/IVictim.sol b/assets/erc-7746/test/IVictim.sol new file mode 100644 index 0000000000..3320104d6d --- /dev/null +++ b/assets/erc-7746/test/IVictim.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity =0.8.20; + +interface IVictim { + function drainedMethod() external; +} diff --git a/assets/erc-7746/test/LibAccessLayers.sol b/assets/erc-7746/test/LibAccessLayers.sol new file mode 100644 index 0000000000..29d9c3d40d --- /dev/null +++ b/assets/erc-7746/test/LibAccessLayers.sol @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity =0.8.20; +import "../ILayer.sol"; + +library LibAccessLayers { + bytes32 constant ACCESS_LAYERS_STORAGE_POSITION = keccak256("lib.access.layer.storage"); + + struct LayerStruct { + address layerAddess; + bytes4 beforeSig; + bytes4 afterSig; + bytes layerConfigData; + } + + function accessLayersStorage() internal pure returns (LayerStruct[] storage ls) { + bytes32 position = ACCESS_LAYERS_STORAGE_POSITION; + assembly { + ls.slot := position + } + } + + function setLayer( + address layerAddress, + uint256 layerIndex, + bytes memory layerConfigData, + bytes4 beforeCallMethodSignature, + bytes4 afterCallMethodSignature + ) internal { + LayerStruct[] storage ls = accessLayersStorage(); + ls[layerIndex].layerAddess = layerAddress; + ls[layerIndex].layerConfigData = layerConfigData; + ls[layerIndex].beforeSig = beforeCallMethodSignature; + ls[layerIndex].afterSig = afterCallMethodSignature; + } + + function addLayer(LayerStruct memory newLayer) internal { + LayerStruct[] storage ls = accessLayersStorage(); + ls.push(newLayer); + } + + function setLayers(LayerStruct[] memory newLayers) internal { + for (uint256 i = 0; i < newLayers.length; i++) { + addLayer(newLayers[i]); + } + } + + function addLayer( + address layerAddress, + bytes memory layerConfigData, + bytes4 beforeCallMethodSignature, + bytes4 afterCallMethodSignature + ) internal { + LayerStruct[] storage ls = accessLayersStorage(); + LayerStruct memory newLayer = LayerStruct({ + layerAddess: layerAddress, + layerConfigData: layerConfigData, + beforeSig: beforeCallMethodSignature, + afterSig: afterCallMethodSignature + }); + ls.push(newLayer); + } + + function popLayer() internal { + LayerStruct[] storage ls = accessLayersStorage(); + ls.pop(); + } + + function getLayer(uint256 layerIdx) internal view returns (LayerStruct storage) { + LayerStruct[] storage ls = accessLayersStorage(); + return ls[layerIdx]; + } + + function beforeCall( + bytes4 _selector, + address sender, + bytes calldata data, + uint256 value + ) internal returns (bytes[] memory) { + LayerStruct[] storage ls = accessLayersStorage(); + bytes[] memory layerReturns = new bytes[](ls.length); + for (uint256 i = 0; i < ls.length; i++) { + layerReturns[i] = validateLayerBeforeCall(ls[i], _selector, sender, data, value); + } + return layerReturns; + } + + function validateLayerBeforeCall( + LayerStruct storage layer, + bytes4 _selector, + address sender, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + bytes memory retval = ILayer(layer.layerAddess).beforeCallValidation( + layer.layerConfigData, + _selector, + sender, + value, + data + ); + + return retval; + } + + function afterCall( + bytes4 _selector, + address sender, + bytes calldata data, + uint256 value, + bytes[] memory beforeCallReturns + ) internal { + LayerStruct[] storage ls = accessLayersStorage(); + for (uint256 i = 0; i < ls.length; i++) { + validateLayerAfterCall(ls[ls.length - 1 - i], _selector, sender, data, value, beforeCallReturns[i]); + } + } + + function extractRevertReason(bytes memory revertData) internal pure returns (string memory reason) { + uint l = revertData.length; + if (l < 68) return ""; + uint t; + assembly { + revertData := add(revertData, 4) + t := mload(revertData) // Save the content of the length slot + mstore(revertData, sub(l, 4)) // Set proper length + } + reason = abi.decode(revertData, (string)); + assembly { + mstore(revertData, t) // Restore the content of the length slot + } + } + + function validateLayerAfterCall( + LayerStruct storage layer, + bytes4 _selector, + address sender, + bytes calldata data, + uint256 value, + bytes memory beforeCallReturnValue + ) internal { + ILayer(layer.layerAddess).afterCallValidation( + layer.layerConfigData, + _selector, + sender, + value, + data, + beforeCallReturnValue + ); + } +} diff --git a/assets/erc-7746/test/MockERC20.sol b/assets/erc-7746/test/MockERC20.sol new file mode 100644 index 0000000000..2a78e84c0b --- /dev/null +++ b/assets/erc-7746/test/MockERC20.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: CC0-1.0 +import "@openzeppelin/contracts-upgradable/token/ERC20/extensions/ERC20Burnable.sol"; +import "@openzeppelin/contracts-upgradalbe/access/OwnableUpgradeable.sol"; + +pragma solidity ^0.8.0; + +contract MockERC20 is ERC20Burnable, Ownable { + uint256 numTokens; + + constructor() ERC20("Mock", "MCK") Ownable(msg.sender) {} + + function mint(address to, uint256 amount) public { + require(to != address(0), "MockERC20->mint: Address not specified"); + require(amount != 0, "MockERC20->mint: amount not specified"); + _mint(to, amount); + } +} diff --git a/assets/erc-7746/test/Protected.sol b/assets/erc-7746/test/Protected.sol new file mode 100644 index 0000000000..aebd1bfc32 --- /dev/null +++ b/assets/erc-7746/test/Protected.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity =0.8.20; + +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "./AccessLayers.sol"; +import "./LibAccessLayers.sol"; + +contract Protected is TransparentUpgradeableProxy, AccessLayers { + uint256 balance = 10000000 ether; + + constructor( + address initialOwner, + LibAccessLayers.LayerStruct[] memory layers, + address initialImplementation + ) TransparentUpgradeableProxy(initialImplementation, initialOwner, "") { + LibAccessLayers.setLayers(layers); + } + + event Transfer(address from, address to, uint256 amount); + + fallback() external payable override layers(msg.sig, msg.sender, msg.data, msg.value) { + // _delegate(_implementation()); <- this method will not return to solidity :( + (bool success, bytes memory result) = _implementation().delegatecall(msg.data); + require(success, string(result)); + } + + receive() external payable { + // custom function code + } +} diff --git a/assets/erc-7746/test/RateLimitLayer.sol b/assets/erc-7746/test/RateLimitLayer.sol new file mode 100644 index 0000000000..c576ff9f86 --- /dev/null +++ b/assets/erc-7746/test/RateLimitLayer.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity =0.8.20; +import "../ILayer.sol"; + +contract RateLimitLayer is ILayer { + mapping(address => mapping(bytes4 => uint256)) usage; + mapping(address => mapping(bytes4 => uint256)) usageUpdatedAtBlock; + + function beforeCallValidation( + bytes memory, + bytes4 messageSig, + address, + uint256, + bytes memory + ) public returns (bytes memory) { + if (usageUpdatedAtBlock[msg.sender][messageSig] != block.number) { + usage[msg.sender][messageSig] = 0; + usageUpdatedAtBlock[msg.sender][messageSig] = block.number; + } else { + usage[msg.sender][messageSig] += 1; + } + return ""; + } + + function afterCallValidation( + bytes memory layerConfig, + bytes4 messageSig, + address, + uint256, + bytes memory, + bytes memory + ) public view { + uint256 blockQuota = uint256(bytes32(layerConfig)); + require(usage[msg.sender][messageSig] < blockQuota, "Rate limited"); + } +} diff --git a/assets/erc-7746/test/deploy/drainer.ts b/assets/erc-7746/test/deploy/drainer.ts new file mode 100644 index 0000000000..7f67b20d28 --- /dev/null +++ b/assets/erc-7746/test/deploy/drainer.ts @@ -0,0 +1,19 @@ +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { DeployFunction } from "hardhat-deploy/types"; + +const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const { deployments, getNamedAccounts } = hre; + const { deploy } = deployments; + + const { deployer, owner } = await getNamedAccounts(); + + await deploy("Drainer", { + from: deployer, + args: [], + skipIfAlreadyDeployed: true, + }); +}; + +export default func; +func.dependencies = ["layer_proxy"]; +func.tags = ["poc", "drainer"]; diff --git a/assets/erc-7746/test/deploy/layered_proxy.ts b/assets/erc-7746/test/deploy/layered_proxy.ts new file mode 100644 index 0000000000..ed7c223088 --- /dev/null +++ b/assets/erc-7746/test/deploy/layered_proxy.ts @@ -0,0 +1,40 @@ +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { DeployFunction } from "hardhat-deploy/types"; +import { MockERC20, RateLimitLayer } from "../types"; +import { ethers } from "hardhat"; +import { LibAccessLayers } from "../types/src/MockERC20"; + +const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const { deployments, getNamedAccounts } = hre; + const { deploy } = deployments; + + const { deployer, owner } = await getNamedAccounts(); + + const simpleLayer = await deployments.get("RateLimitLayer"); + const simpleLayerContract = (await ethers.getContractAt(simpleLayer.abi, simpleLayer.address)) as RateLimitLayer; + + let layer: LibAccessLayers.LayerStructStruct = { + layerAddess: simpleLayer.address, + beforeSig: simpleLayerContract.interface.getSighash(simpleLayerContract.interface.functions["beforeCallValidation(bytes,bytes4,address,uint256,bytes)"]), + afterSig: simpleLayerContract.interface.getSighash( + simpleLayerContract.interface.functions["afterCallValidation(bytes,bytes4,address,uint256,bytes,bytes)"] + ), + layerConfigData: ethers.utils.defaultAbiCoder.encode(["uint256"], [10]), + }; + + const result = await deploy("MockERC20", { + from: deployer, + args: [], + skipIfAlreadyDeployed: true, + }); + + const lp = await deploy("MockERC20", { + from: deployer, + args: [owner, [layer], result.address], + skipIfAlreadyDeployed: true, + }); +}; + +export default func; +func.dependencies = ["simple_layer"]; +func.tags = ["poc", "layer_proxy"]; diff --git a/assets/erc-7746/test/deploy/simple_layer.ts b/assets/erc-7746/test/deploy/simple_layer.ts new file mode 100644 index 0000000000..a4baa8fc61 --- /dev/null +++ b/assets/erc-7746/test/deploy/simple_layer.ts @@ -0,0 +1,18 @@ +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { DeployFunction } from "hardhat-deploy/types"; + +const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const { deployments, getNamedAccounts } = hre; + const { deploy } = deployments; + + const { deployer } = await getNamedAccounts(); + + await deploy("RateLimitLayer", { + from: deployer, + args: [], + skipIfAlreadyDeployed: true, + }); +}; + +export default func; +func.tags = ["poc", "simple_layer"]; diff --git a/assets/erc-7746/test/test.ts b/assets/erc-7746/test/test.ts new file mode 100644 index 0000000000..94b469043b --- /dev/null +++ b/assets/erc-7746/test/test.ts @@ -0,0 +1,40 @@ +/* global ethers */ + +import { deployments, ethers } from "hardhat"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { DeployFunction } from "hardhat-deploy/types"; + +import { expect } from "chai"; +import { Drainer, Protected, MockERC20 } from "../types"; + +const setupTest = deployments.createFixture(async ({ deployments, getNamedAccounts, ethers: _eth }, options) => { + const { deployer, owner } = await getNamedAccounts(); + await deployments.fixture(["poc"]); + console.warn(deployer, owner); + const c = await deployments.get("Protected"); + const d = await deployments.get("Drainer"); + return { + owner, + deployer, + victim: (await ethers.getContractAt(c.abi, c.address)) as Protected, + attacker: (await ethers.getContractAt(d.abi, d.address)) as Drainer, + }; +}); + +describe("test drainage", async function () { + let env: { + owner: string; + deployer: string; + victim: Protected; + attacker: Drainer; + }; + beforeEach(async function () { + env = await setupTest(); + }); + it("succeeds below 10 transactions", async () => { + await expect(env.attacker.drain(env.victim.address, 1)).to.emit(env.victim, "Transfer"); + }); + it("fails beyond 10 transactions", async () => { + await expect(env.attacker.drain(env.victim.address, 11)).to.be.revertedWith("Rate limited"); + }); +}); From ab5518bd3884b89ed0f3f76e105823f0b89ae09e Mon Sep 17 00:00:00 2001 From: Liaoyuan Date: Tue, 3 Sep 2024 22:43:26 +0800 Subject: [PATCH 118/126] Update ERC-7627: Move to Review Merged by EIP-Bot. --- ERCS/erc-7627.md | 140 +++++++++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 64 deletions(-) diff --git a/ERCS/erc-7627.md b/ERCS/erc-7627.md index e035415ca6..f9c6428703 100644 --- a/ERCS/erc-7627.md +++ b/ERCS/erc-7627.md @@ -1,10 +1,10 @@ --- eip: 7627 title: Secure Messaging Protocol -description: A solution implementing end-to-end encryption for sending messages between users. +description: End-to-end encryption for sending messages between users. author: Chen Liaoyuan (@chenly) discussions-to: https://ethereum-magicians.org/t/erc-7627-secure-messaging-protocol/18761 -status: Draft +status: Review type: Standards Track category: ERC created: 2024-02-19 @@ -16,7 +16,7 @@ This proposal implements the capability to securely exchange encrypted messages ## Motivation -With the emergence of Layer 2 chains featuring sub-second block times and the introduction of account abstraction, the use of end-to-end encrypted communication has facilitated the proliferation of real-time communication and online chat dApps. Leveraging asymmetric encryption now enables the establishment of decentralized, end-to-end interoperable messaging protocols as a standard. +With the emergence of Layer 2 chains featuring sub-second block times and the introduction of account abstraction, the use of end-to-end encrypted communication has facilitated the proliferation of real-time communication and online chat dApps. Providing a unified interface enables easy integration of encrypted communication into smart contracts, thereby fostering innovation. Standardization promotes interoperability, facilitating seamless communication across platforms. ## Specification @@ -40,56 +40,71 @@ pragma solidity ^0.8.0; interface IERC7627 { enum PublicKeyAlgorithm { ECDSA, ED25519, X25519 } + + struct PublicKey { + bytes public_key; + uint64 valid_before; + PublicKeyAlgorithm algorithm; + } // Events - - /** - * @dev Event emitted when a message is sent. - * @param from The address of the sender. - * @param to The address of the recipient. - * @param sessionId The session ID of the message. - * @param encryptedMessage The encrypted message. - */ - event MessageSent(address indexed from, address indexed to, bytes32 sessionId, bytes encryptedMessage); - - /** - * @dev Event emitted when a user updates their public key. - * @param user The address of the user. - * @param newPublicKey The new public key of the user. - * @param algorithm The algorithm used for the public key. - */ - event PublicKeyUpdated(address indexed user, bytes newPublicKey, PublicKeyAlgorithm algorithm); + /** + * @notice Event emitted when a message is sent between users. + * @param from The address of the sender + * @param to The address of the recipient + * @param keyIndex The index of the public key used to encrypt the message + * @param sessionId The session ID for the communication + * @param encryptedMessage The encrypted message in bytes + */ + event MessageSent(address indexed from, address indexed to, bytes32 indexed keyIndex, bytes32 sessionId, bytes encryptedMessage); + + /** + * @notice Event emitted when a user's public key is updated. + * @param user The address of the user whose public key is updated + * @param keyIndex The index of the public key being updated + * @param newPublicKey The new public key data + */ + event PublicKeyUpdated(address indexed user, bytes32 indexed keyIndex, PublicKey newPublicKey); // Functions - /** - * @dev Function to update a user's public key. - * @param _publicKey The public key of the user. - * @param _algorithm The algorithm used for the public key. - */ - function updatePublicKey(bytes calldata _publicKey, PublicKeyAlgorithm _algorithm) external; - - /** - * @dev Function to send an encrypted message from one user to another. - * @param _to The address of the recipient. - * @param _sessionId The session ID of the message. - * @param _encryptedMessage The encrypted message. - */ - function sendMessage(address _to, bytes32 _sessionId, bytes calldata _encryptedMessage) external; - - /** - * @dev Function to retrieve a user's public key and algorithm. - * @param _user The address of the user. - * @return publicKey The public key of the user. - * @return algorithm The algorithm used for the public key. - */ - function getUserPublicKey(address _user) external view returns (bytes memory publicKey, PublicKeyAlgorithm algorithm); + /** + * @notice Updates the public key for the sender. + * @param _keyIndex The index of the key to be updated + * @param _publicKey The new public key data + */ + function updatePublicKey(bytes32 _keyIndex, PublicKey memory _publicKey) external; + + /** + * @notice Sends an encrypted message to a specified address. + * @param _to The recipient's address + * @param _keyIndex The index of the public key used to encrypt the message + * @param _sessionId The session ID for the communication + * @param _encryptedMessage The encrypted message in bytes + */ + function sendMessage(address _to, bytes32 _keyIndex, bytes32 _sessionId, bytes calldata _encryptedMessage) external; + + /** + * @notice Retrieves a public key for a specific user and key index. + * @param _user The address of the user + * @param _keyIndex The index of the key to retrieve + * @return The public key data associated with the user and key index + */ + function getUserPublicKey(address _user, bytes32 _keyIndex) external view returns (PublicKey memory); } ``` ## Rationale -Traditional messaging lacks security and transparency for blockchain communication. The choice of asymmetric encryption ensures the confidentiality and integrity of messages, which is why we opted for this encryption method. Providing a unified interface enables easy integration of encrypted communication into smart contracts, thereby fostering innovation. Encrypted messaging guarantees adherence to best practices in data security. Due to security reasons, public keys need to be regularly updated, hence we have added a feature that allows users to autonomously update their public keys. The interface supports various encryption methods, enhancing adaptability. Event tracking enhances the observability and auditability of the contract, aiding compliance efforts. Standardization promotes interoperability, facilitating seamless communication across platforms. +### Event Emission for Off-Chain Integration +By emitting events when messages are sent or public keys are updated, the implementation facilitates seamless integration with off-chain dApps. This enables these dApps to easily track and display the latest messages and updates, ensuring real-time responsiveness and enhancing user interaction. + +### End-to-End Encryption Security +The design ensures that only the owner of an address can update their public key. This restriction preserves the integrity of the end-to-end encryption, making sure that only the intended recipient can decrypt and read the messages, thereby securing communication. + +### Session ID for Conversation Organization +The use of session IDs in message transactions allows multiple messages to be grouped under specific conversations. This feature is crucial for organizing and managing discussions within a dApp, providing users with a coherent and structured messaging experience. + ## Reference Implementation @@ -98,34 +113,34 @@ pragma solidity ^0.8.0; contract ERC7627 { + /// @dev Enum to specify the algorithm used for the public key. enum PublicKeyAlgorithm { ECDSA, ED25519, X25519 } - struct UserInfo { - bytes publicKey; - PublicKeyAlgorithm algorithm; + /// @dev Structure to represent a user's public key. + struct PublicKey { + bytes public_key; + uint64 valid_before; + PublicKeyAlgorithm algorithm; } - mapping(address => UserInfo) public pk; + /// @dev Mapping to store public keys for each address. The mapping is by user address and a unique key index. + mapping(address => mapping(bytes32 => PublicKey)) public pk; - event MessageSent(address indexed from, address indexed to, bytes32 sessionId, bytes encryptedMessage); - event PublicKeyUpdated(address indexed user, bytes newPublicKey, PublicKeyAlgorithm algorithm); + event MessageSent(address indexed from, address indexed to, bytes32 indexed keyIndex, bytes32 sessionId, bytes encryptedMessage); - // Function to register a user with their public key - function updatePublicKey(bytes calldata _publicKey, PublicKeyAlgorithm _algorithm) external { - pk[msg.sender].publicKey = _publicKey; - pk[msg.sender].algorithm = _algorithm; - emit PublicKeyUpdated(msg.sender, _publicKey, _algorithm); + event PublicKeyUpdated(address indexed user, bytes32 indexed keyIndex, PublicKey newPublicKey); + + function updatePublicKey(bytes32 _keyIndex, PublicKey memory _publicKey) external { + pk[msg.sender][_keyIndex] = _publicKey; + emit PublicKeyUpdated(msg.sender, _keyIndex, _publicKey); } - // Function to send an encrypted message from one user to another - function sendMessage(address _to, bytes32 _sessionId, bytes calldata _encryptedMessage) external { - emit MessageSent(msg.sender, _to, _sessionId, _encryptedMessage); + function sendMessage(address _to, bytes32 _keyIndex, bytes32 _sessionId, bytes calldata _encryptedMessage) external { + emit MessageSent(msg.sender, _to, _keyIndex, _sessionId, _encryptedMessage); } - // Function to retrieve a user's public key - function getUserPublicKey(address _user) external view returns (bytes memory, PublicKeyAlgorithm) { - UserInfo memory userInfo = pk[_user]; - return (userInfo.publicKey, userInfo.algorithm); + function getUserPublicKey(address _user, bytes32 _keyIndex) external view returns (PublicKey memory) { + return pk[_user][_keyIndex]; } } ``` @@ -141,9 +156,6 @@ To maintain message confidentiality, the content of sent messages must be strict #### Key Management and Protection Robust key management and protection measures are necessary for both user public and private keys. Ensure secure storage and transmission of keys to prevent leakage and tampering. Employ multi-factor authentication and key rotation strategies to enhance key security and regularly assess key management processes to mitigate potential security risks. -#### Auditing and Monitoring -Implement auditing and monitoring mechanisms to track message sending and receiving, as well as key usage. Promptly identify anomalous activities and potential security threats and take appropriate response measures. Record critical operations and events for security incident investigation and traceability purposes. - ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md) From 95258c8b3667e3adbf6408839a255b8f56c78d5c Mon Sep 17 00:00:00 2001 From: Francesco Sullo Date: Tue, 3 Sep 2024 07:43:54 -0700 Subject: [PATCH 119/126] Update ERC-7656: Move to Review Merged by EIP-Bot. --- ERCS/erc-7656.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ERCS/erc-7656.md b/ERCS/erc-7656.md index bceea4f3b7..b91e771d0c 100644 --- a/ERCS/erc-7656.md +++ b/ERCS/erc-7656.md @@ -4,7 +4,7 @@ title: Generalized Token-Linked Services description: Define a registry for generic services linked to a specific NFT author: Francesco Sullo (@sullof) discussions-to: https://ethereum-magicians.org/t/variation-to-erc6551-to-deploy-any-kind-of-contract-linked-to-an-nft/19223 -status: Draft +status: Review type: Standards Track category: ERC created: 2024-03-15 @@ -116,11 +116,7 @@ ERC-1167 Footer (15 bytes) Any contract created using a `ERC7656Registry` SHOULD implement the `IERC7656Service` interface: ```solidity -<<<<<<< HEAD interface IERC7656Service { -======= -interface IERC7656Linked { ->>>>>>> master /** * @notice Returns the token linked to the contract * @return chainId The chainId of the token From 920a06828dc5e717e78774884b1eee7a6b2531c3 Mon Sep 17 00:00:00 2001 From: Liaoyuan Date: Tue, 3 Sep 2024 22:45:08 +0800 Subject: [PATCH 120/126] Update ERC-7628: Move to Review Merged by EIP-Bot. --- ERCS/erc-7628.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7628.md b/ERCS/erc-7628.md index c348fa4850..ff0a1398ea 100644 --- a/ERCS/erc-7628.md +++ b/ERCS/erc-7628.md @@ -4,7 +4,7 @@ title: ERC-721 Ownership Shares Extension description: Introduces ownership shares to ERC-721 tokens, allowing for queryable, transferable, and approvable fractional ownership. author: Chen Liaoyuan (@chenly) discussions-to: https://ethereum-magicians.org/t/erc-7628-erc-721-ownership-shares-extension/18744 -status: Draft +status: Review type: Standards Track category: ERC created: 2024-02-20 @@ -13,7 +13,7 @@ requires: 721 ## Abstract -The proposal introduces an attribute of ownership and profit share quantities for each token under an NFT. This attribute signifies a stake in the ownership and profit rights associated with the NFT's specific privileges, enabling the querying, transferring, and approval of these shares, thereby making the shares represented by each token applicable in a broader range of use cases. +This proposal introduces an attribute of ownership and profit share quantities for each token under an NFT. This attribute signifies a stake in the ownership and profit rights associated with the NFT's specific privileges, enabling the querying, transferring, and approval of these shares, thereby making the shares represented by each token applicable in a broader range of use cases. ## Motivation From 2b8a2fa51bc45b039caee56b6640be89e6de94fd Mon Sep 17 00:00:00 2001 From: Lukas <36800180+lukasrosario@users.noreply.github.com> Date: Wed, 4 Sep 2024 06:51:18 -0400 Subject: [PATCH 121/126] Update ERC-7677: Move to Review Merged by EIP-Bot. --- ERCS/erc-7677.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ERCS/erc-7677.md b/ERCS/erc-7677.md index 39c9f0a256..e6dce38f95 100644 --- a/ERCS/erc-7677.md +++ b/ERCS/erc-7677.md @@ -4,8 +4,7 @@ title: Paymaster Web Service Capability description: A way for apps to communicate with smart wallets about paymaster web services author: Lukas Rosario (@lukasrosario), Dror Tirosh (@drortirosh), Wilson Cusack (@wilsoncusack), Kristof Gazso (@kristofgazso), Hazim Jumali (@hazim-j) discussions-to: https://ethereum-magicians.org/t/erc-7677-paymaster-web-service-capability/19530 -status: Last Call -last-call-deadline: 2024-09-05 +status: Review type: Standards Track category: ERC created: 2024-04-03 @@ -30,7 +29,7 @@ We define two JSON-RPC methods to be implemented by paymaster web services. #### `pm_getPaymasterStubData` -Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation. Accepts an unsigned user operation, entrypoint address, chain id, and a context object. Paymaster service providers can define fields that app developers should use in the context object. +Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation. Accepts an unsigned user operation, entrypoint address, chain id, and a context object. Paymaster service providers can define fields that app developers should use in the context object. This method MAY return paymaster-specific gas values if applicable to the provided EntryPoint version. For example, if provided with EntryPoint v0.7, this method MAY return `paymasterVerificationGasLimit`. Furthermore, for EntryPoint v0.7, this method MUST return a value for `paymasterPostOpGasLimit`. This is because it is the paymaster that pays the postOpGasLimit, so it cannot trust the wallet to estimate this amount. @@ -67,14 +66,14 @@ type GetPaymasterStubDataParams = [ ]; type GetPaymasterStubDataResult = { - sponsor?: { name: string; icon?: string } // Sponsor info - paymaster?: string // Paymaster address (entrypoint v0.7) + sponsor?: { name: string; icon?: string }; // Sponsor info + paymaster?: string; // Paymaster address (entrypoint v0.7) paymasterData?: string; // Paymaster data (entrypoint v0.7) paymasterVerificationGasLimit?: `0x${string}`; // Paymaster validation gas (entrypoint v0.7) paymasterPostOpGasLimit?: `0x${string}`; // Paymaster post-op gas (entrypoint v0.7) paymasterAndData?: string; // Paymaster and data (entrypoint v0.6) isFinal?: boolean; // Indicates that the caller does not need to call pm_getPaymasterData -} +}; ``` ###### `pm_getPaymasterStubData` Example Parameters @@ -90,7 +89,7 @@ type GetPaymasterStubDataResult = { "verificationGasLimit": "0x...", "preVerificationGas": "0x...", "maxFeePerGas": "0x...", - "maxPriorityFeePerGas": "0x...", + "maxPriorityFeePerGas": "0x..." }, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "0x2105", @@ -188,10 +187,10 @@ type GetPaymasterDataParams = [ ]; type GetPaymasterDataResult = { - paymaster?: string // Paymaster address (entrypoint v0.7) + paymaster?: string; // Paymaster address (entrypoint v0.7) paymasterData?: string; // Paymaster data (entrypoint v0.7) paymasterAndData?: string; // Paymaster and data (entrypoint v0.6) -} +}; ``` ###### `pm_getPaymasterData` Example Parameters @@ -207,7 +206,7 @@ type GetPaymasterDataResult = { "verificationGasLimit": "0x...", "preVerificationGas": "0x...", "maxFeePerGas": "0x...", - "maxPriorityFeePerGas": "0x...", + "maxPriorityFeePerGas": "0x..." }, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "0x2105", @@ -305,6 +304,7 @@ The wallet will then make the above paymaster RPC calls to the URLs specified in #### Wallet Implementation To conform to this specification, smart wallets that wish to leverage app-sponsored transactions: + 1. MUST indicate to apps that they can communicate with paymaster web services via their response to an [EIP-5792](./eip-5792.md) `wallet_getCapabilities` call. 2. SHOULD make calls to and use the values returned by the paymaster service specified in the capabilities field of an [EIP-5792](./eip-5792.md) `wallet_sendCalls` call. An example of an exception is a wallet that allows users to select a paymaster provided by the wallet. Since there might be cases in which the provided paymaster is ultimately not usedโ€”either due to service failure or due to a user selecting a different, wallet-provided paymasterโ€”applications MUST NOT assume that the paymaster it provides to a wallet is the entity that pays for transaction fees. @@ -313,7 +313,7 @@ To conform to this specification, smart wallets that wish to leverage app-sponso ```typescript type PaymasterServiceCapability = { supported: boolean; -} +}; ``` ###### `wallet_getCapabilities` Example Response @@ -323,7 +323,7 @@ type PaymasterServiceCapability = { "0x2105": { "paymasterService": { "supported": true - }, + } }, "0x14A34": { "paymasterService": { @@ -385,4 +385,4 @@ This flow would allow developers to keep their paymaster service API keys secret ## Copyright -Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). From 991debbee9fa44f35c5579376aa640892ca57a04 Mon Sep 17 00:00:00 2001 From: Francesco Sullo Date: Thu, 5 Sep 2024 07:42:31 -0700 Subject: [PATCH 122/126] Update ERC-7656: Fix wording, using service instead of account, when needed Merged by EIP-Bot. --- ERCS/erc-7656.md | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/ERCS/erc-7656.md b/ERCS/erc-7656.md index b91e771d0c..8083137097 100644 --- a/ERCS/erc-7656.md +++ b/ERCS/erc-7656.md @@ -61,15 +61,15 @@ interface IERC7656Registry { error CreationFailed(); /** - * @notice Creates a token linked account for a non-fungible token. - * If account has already been created, returns the account address without calling create2. + * @notice Creates a token linked service for a non-fungible token. + * If the service has already been created, returns the service address without calling create2. * @param implementation The address of the implementation contract * @param salt The salt to use for the create2 operation - * @param chainId The chain id of the chain where the account is being created + * @param chainId The chain id of the chain where the service is being created * @param tokenContract The address of the token contract * @param tokenId The id of the token * Emits Created event. - * @return account The address of the token linked account + * @return service The address of the token linked service */ function create( address implementation, @@ -77,16 +77,16 @@ interface IERC7656Registry { uint256 chainId, address tokenContract, uint256 tokenId - ) external returns (address account); + ) external returns (address service); /** - * @notice Returns the computed token linked account address for a non-fungible token. + * @notice Returns the computed token linked service address for a non-fungible token. * @param implementation The address of the implementation contract * @param salt The salt to use for the create2 operation - * @param chainId The chain id of the chain where the account is being created + * @param chainId The chain id of the chain where the service is being created * @param tokenContract The address of the token contract * @param tokenId The id of the token - * @return account The address of the token linked account + * @return service The address of the token linked service */ function compute( address implementation, @@ -94,15 +94,15 @@ interface IERC7656Registry { uint256 chainId, address tokenContract, uint256 tokenId - ) external view returns (address account); + ) external view returns (address service); } ``` Any `ERC7656Registry` implementation MUST support the `IERC7656Registry`'s interface ID, i.e., `0xc6bdc908`. -Similarly to [ERC-6551](./eip-6551.md), The registry MUST deploy each token linked account as an [ERC-1167](./eip-1167.md) minimal proxy with immutable constant data appended to the bytecode. +Similarly to [ERC-6551](./eip-6551.md), The registry MUST deploy each token linked service as an [ERC-1167](./eip-1167.md) minimal proxy with immutable constant data appended to the bytecode. -The deployed bytecode of each token bound account MUST have the following structure: +The deployed bytecode of each token bound service MUST have the following structure: ``` ERC-1167 Header (10 bytes) (20 bytes) @@ -116,6 +116,7 @@ ERC-1167 Footer (15 bytes) Any contract created using a `ERC7656Registry` SHOULD implement the `IERC7656Service` interface: ```solidity +// InterfaceId 0xfc0c546a interface IERC7656Service { /** * @notice Returns the token linked to the contract @@ -189,12 +190,12 @@ contract ERC7656Registry is IERC7656Registry { mstore(0x01, shl(96, address())) // registry address mstore(0x15, salt) // salt - // Compute account address + // Compute service address let computed := keccak256(0x00, 0x55) - // If the account has not yet been deployed + // If the service has not yet been deployed if iszero(extcodesize(computed)) { - // Deploy account contract + // Deploy service contract let deployed := create2(0, 0x55, 0xb7, salt) // Revert if the deployment fails @@ -203,7 +204,7 @@ contract ERC7656Registry is IERC7656Registry { revert(0x1c, 0x04) } - // Store account address in memory before salt and chainId + // Store service address in memory before salt and chainId mstore(0x6c, deployed) // Emit the Created event @@ -216,11 +217,11 @@ contract ERC7656Registry is IERC7656Registry { tokenId ) - // Return the account address + // Return the service address return(0x6c, 0x20) } - // Otherwise, return the computed account address + // Otherwise, return the computed service address mstore(0x00, shr(96, shl(96, computed))) return(0x00, 0x20) } @@ -247,10 +248,10 @@ contract ERC7656Registry is IERC7656Registry { mstore(0x01, shl(96, address())) // registry address mstore(0x15, salt) // salt - // Store computed account address in memory + // Store computed service address in memory mstore(0x00, shr(96, shl(96, keccak256(0x00, 0x55)))) - // Return computed account address + // Return computed service address return(0x00, 0x20) } } From 8082e27c5db9dbb02f659498039d9f1b8f42eece Mon Sep 17 00:00:00 2001 From: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:27:45 -0400 Subject: [PATCH 123/126] Remove polyfill. (#620) The `polyfill.io` domain was taken over by a malicious actor, and now injects code that redirects to unrelated websites. References: Reported-By: Elliott Green --- _includes/head.html | 1 - 1 file changed, 1 deletion(-) diff --git a/_includes/head.html b/_includes/head.html index bc8a94533a..d388a866fa 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -43,7 +43,6 @@ {%- feed_meta -%} - + + + + + + Info + + + +

+ + + + + + + Mint + + + + + + + + + +

+ +
+

Use this to mint your own tokens.

+
+
+ + \ No newline at end of file diff --git a/assets/erc-7738/tokenscript/info.html b/assets/erc-7738/tokenscript/info.html new file mode 100644 index 0000000000..9755f14073 --- /dev/null +++ b/assets/erc-7738/tokenscript/info.html @@ -0,0 +1,181 @@ + +
+

ERC-7738 registry

+ +

Contract

+
+ +

Order #

+
+ +

ScriptURI

+
+ +

ENS

+
+ +

Name

+
+ +

Authenticated by Owner

+
+ +
+ + \ No newline at end of file diff --git a/assets/erc-7738/tokenscript/onboard.html b/assets/erc-7738/tokenscript/onboard.html new file mode 100644 index 0000000000..f5e19e4f6b --- /dev/null +++ b/assets/erc-7738/tokenscript/onboard.html @@ -0,0 +1,221 @@ + +

Create a new script mapping for a contract. Input the contract address and the scriptURI

+

+
+

Contract Address

+ + Not a valid contract + โœ” +
+
+

ScriptURI

+ + Not a valid URL + โœ” +
+ + +

+ \ No newline at end of file diff --git a/assets/erc-7738/tokenscript/out/tokenscript.tsml b/assets/erc-7738/tokenscript/out/tokenscript.tsml new file mode 100644 index 0000000000..6b7762175c --- /dev/null +++ b/assets/erc-7738/tokenscript/out/tokenscript.tsml @@ -0,0 +1,1163 @@ + + + + + ERC-7738 Registry Token + + + ERC-7738 Registry Tokens + + + + + 0x0077380bCDb2717C9640e892B9d5Ee02Bb5e0682 + + + [ + { + "name": "setScriptURI", + "inputs": [ + { + "internalType": "address", + "name": "contractAddressLocal", + "type": "address" + }, + { + "internalType": "string[]", + "name": "scriptURILocal", + "type": "string[]" + } + ], + "outputs": [ + + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "name": "updateScriptURI", + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "scriptURILocal", + "type": "string" + } + ], + "outputs": [ + + ], + "stateMutability": "nonpayable", + "type": "function" + } + ] + + + + + + + + + + + Info + + + + + +
+

ERC-7738 registry

+ +

Contract

+
+ +

Order #

+
+ +

ScriptURI

+
+ +

ENS

+
+ +

Name

+
+ +

Authenticated by Owner

+
+ +
+ +
+
+ + + + Update ScriptURI + + + + + + + + + + + + + +

Create a new script mapping for a contract. Input the contract address and the scriptURI

+

+
+

Current ScriptURI

+
+
+
+

+
+

ScriptURI

+ + Not a valid URL + โœ” +
+ + +

+
+
+ + + + Set Name + + + + + + + + + + + + + +

Add a name for your script entry

+

+
+

Current Name

+
+
+
+

+
+

Name

+ +
+ +

+
+
+ + + + Set Icon + + + + + + + + + + + + + +
+

Set an icon for your script entry

+

(note, you must upload the icon yourself and provide the URI here)

+
+

+
+

Current Icon

+
+
+
+

+
+

Icon URL

+ + Not a valid URL + โœ” +
+ + +

+
+
+ + + + Set ScriptURI + + + + + + + + + + + + + + +

Create a new script mapping for a contract. Input the contract address and the scriptURI

+

+
+

Contract Address

+ + Not a valid contract + โœ” +
+
+

ScriptURI

+ + Not a valid URL + โœ” +
+ + +

+
+
+
+ + + 1.3.6.1.4.1.1466.115.121.1.36 + + + + totalSupply + + + + + + + + +
\ No newline at end of file diff --git a/assets/erc-7738/tokenscript/setIcon.html b/assets/erc-7738/tokenscript/setIcon.html new file mode 100644 index 0000000000..5497415e1c --- /dev/null +++ b/assets/erc-7738/tokenscript/setIcon.html @@ -0,0 +1,226 @@ + +
+

Set an icon for your script entry

+

(note, you must upload the icon yourself and provide the URI here)

+
+

+
+

Current Icon

+
+
+
+

+
+

Icon URL

+ + Not a valid URL + โœ” +
+ + +

+ \ No newline at end of file diff --git a/assets/erc-7738/tokenscript/setName.html b/assets/erc-7738/tokenscript/setName.html new file mode 100644 index 0000000000..6f5858a163 --- /dev/null +++ b/assets/erc-7738/tokenscript/setName.html @@ -0,0 +1,165 @@ + +

Add a name for your script entry

+

+
+

Current Name

+
+
+
+

+
+

Name

+ +
+ +

+ \ No newline at end of file diff --git a/assets/erc-7738/tokenscript/tokenscript.xml b/assets/erc-7738/tokenscript/tokenscript.xml new file mode 100644 index 0000000000..d7a46fcc42 --- /dev/null +++ b/assets/erc-7738/tokenscript/tokenscript.xml @@ -0,0 +1,170 @@ + + + + + + ERC-7738 Registry Token + + + ERC-7738 Registry Tokens + + + + + 0x0077380bCDb2717C9640e892B9d5Ee02Bb5e0682 + + + + + + + + + + + + Info + + + + + + + + + + Update ScriptURI + + + + + + + + + + + + + + + + + + Set Name + + + + + + + + + + + + + + + + + + Set Icon + + + + + + + + + + + + + + + + + + Set ScriptURI + + + + + + + + + + + + + + + + + + + 1.3.6.1.4.1.1466.115.121.1.36 + + + + totalSupply + + + + + + + + + \ No newline at end of file diff --git a/assets/erc-7738/tokenscript/updateScript.html b/assets/erc-7738/tokenscript/updateScript.html new file mode 100644 index 0000000000..7a5e8341dc --- /dev/null +++ b/assets/erc-7738/tokenscript/updateScript.html @@ -0,0 +1,191 @@ + +

Create a new script mapping for a contract. Input the contract address and the scriptURI

+

+
+

Current ScriptURI

+
+
+
+

+
+

ScriptURI

+ + Not a valid URL + โœ” +
+ + +

+ \ No newline at end of file