From 6be254f993d6774a4d386b3bb3273817ac32da98 Mon Sep 17 00:00:00 2001 From: alexohre Date: Thu, 13 Feb 2025 22:35:21 +0100 Subject: [PATCH 1/9] alex assignment 1 & 2 Signed-off-by: alexohre --- PULL_REQUEST_TEMPLATE.md | 4 +- contracts/counter_alex.sol | 78 ++ contracts/student_registry_alex.sol | 158 +++ package-lock.json | 1438 ++++++++++++++++++--------- package.json | 3 +- 5 files changed, 1225 insertions(+), 456 deletions(-) create mode 100644 contracts/counter_alex.sol create mode 100644 contracts/student_registry_alex.sol diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 617e65c4..b8381609 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,8 @@ +Assignment 1 link [here](https://github.com/BlockheaderWeb3-Community/cohort-6/blob/main/assignments/Assignment1.md) + +Assignment 2 link [here](https://github.com/BlockheaderWeb3-Community/cohort-6/blob/main/assignments/Assignment2.md) @@ -12,4 +15,3 @@ - [ ] Tests - [ ] Documentation - diff --git a/contracts/counter_alex.sol b/contracts/counter_alex.sol new file mode 100644 index 00000000..7a2d9f7c --- /dev/null +++ b/contracts/counter_alex.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0 <0.9.0; + +/** + * @title Counter Contract + * @notice A simple counter contract that supports incrementing and decrementing operations. + * @dev Provides functions to increment, decrement and adjust the counter by a specified value. + */ + +contract Counter { + /** + * @notice The current count value. + * @dev Public state variable initialized to zero. + */ + uint public countVal = 0; + + /** + * @notice Retrieves the current count value. + * @return The current counter value. + */ + function getCount() public view returns (uint) { + return countVal; + } + + /** + * @notice Increments the counter by one. + * @dev Ensures that the counter does not exceed the maximum value defined by runner(). + */ + function increment() public { + uint maxValue = runner(); + require(countVal < maxValue); + countVal++; + } + + /** + * @notice Decrements the counter by one. + * @dev Prevents the counter from decrementing below zero. + */ + function decrement() public { + require(countVal > 0, "Counter cannot be negative"); + countVal--; + } + + /** + * @notice Increments the counter by a specified positive value. + * @param _val The value to add to the current count. + * @dev Validates that _val is greater than zero and that the resulting count does not exceed the maximum value. + */ + function incrementByVal(uint _val) public { + uint maxValue = runner(); + require(_val > 0, "Must be greater than zero"); + require(countVal + _val <= maxValue, "Counter cannot be negative"); + countVal += _val; + } + + /** + * @notice Decrements the counter by a specified positive value. + * @param _val The value to subtract from the current count. + * @dev Validates that _val is greater than zero and that the current count is sufficient to subtract _val without underflow. + */ + function decrementByVal(uint _val) public { + require(_val > 0, "Must be greater than zero"); + require(countVal >= _val, "Underflow: Cannot decrement below zero"); // Prevent underflow + + countVal -= _val; + } + + /** + * @notice Computes the maximum value for a uint256. + * @return The maximum possible value of a uint256. + * @dev Uses unchecked arithmetic to derive the maximum uint256 value. + */ + function runner() public pure returns (uint) { + unchecked { + return uint256(0) - 1; + } + } +} diff --git a/contracts/student_registry_alex.sol b/contracts/student_registry_alex.sol new file mode 100644 index 00000000..867ecff5 --- /dev/null +++ b/contracts/student_registry_alex.sol @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.5.0 <0.9.0; + +/// @title StudentRegistry +/// @dev A contract to manage student registration, attendance, and interests. +contract StudentRegister { + /// @dev Enum to represent student attendance status. + enum Attendance { + Absent, + Present + } + + /// @dev Struct to represent a student. + struct Student { + string Name; // Name of the student + Attendance attendance; // Attendance status of the student + string[] interest; // List of interests of the student + } + + /// @dev Address of the contract owner. + address public owner; + + /// @dev Mapping to store student data by their address. + mapping(address => Student) public students; + + /// @dev Event emitted when a new student is registered. + event StudentCreated(address indexed _studentAddress, string _name); + + /// @dev Event emitted when a student's attendance status is updated. + event AttendanceStatus(address indexed _studentAddress, Attendance _attendance); + + /// @dev Event emitted when a new interest is added to a student's profile. + event InterestAdded(address indexed _studentAddress, string _interest); + + /// @dev Event emitted when an interest is removed from a student's profile. + event InterestRemoved(address indexed _studentAddress, string _interest); + + /// @dev Modifier to restrict access to the contract owner. + modifier OnlyOwner() { + require(msg.sender == owner, "Owner only function"); + _; + } + + /// @dev Modifier to ensure a student with the given address exists. + modifier studentExists(address _studentaddr) { + require(bytes(students[_studentaddr].Name).length != 0, "The specified student does not exist."); + _; + } + + /// @dev Modifier to ensure a student with the given address does not exist. + modifier studentDoesNotExist(address _studentAddr) { + require(bytes(students[_studentAddr].Name).length == 0, "The specified student already exists."); + _; + } + + /// @dev Constructor to set the contract owner. + constructor() { + owner = msg.sender; + } + + /// @dev Registers a new student with a name, attendance status, and interests. + /// @param _name The name of the student. + /// @param _attendance The attendance status of the student. + /// @param _interests The list of interests of the student. + function registerStudent(string memory _name, Attendance _attendance, string[] memory _interests) public { + students[msg.sender].Name = _name; + students[msg.sender].attendance = _attendance; + students[msg.sender].interest = _interests; + + emit StudentCreated(msg.sender, _name); + } + + /// @dev Registers a new student with a default attendance status (Absent) and no interests. + /// @param _name The name of the student. + function registerNewStudent(string memory _name) public studentDoesNotExist(msg.sender) { + require(bytes(_name).length > 0, "The provided name is empty."); + students[msg.sender] = Student({Name: _name, attendance: Attendance.Absent, interest: new string[](0)}); + + emit StudentCreated(msg.sender, _name); + } + + /// @dev Marks the attendance of a student. + /// @param _address The address of the student. + /// @param _attendance The attendance status to set (Absent or Present). + function markAttendance(address _address, Attendance _attendance) public studentExists(_address) { + students[_address].attendance = _attendance; + emit AttendanceStatus(_address, _attendance); + } + + /// @dev Adds an interest to a student's profile. + /// @param _address The address of the student. + /// @param _interest The interest to add. + function addInterests(address _address, string memory _interest) public studentExists(_address) { + require(bytes(_interest).length > 0, "The provided interest is empty."); + require(bytes(_interest).length <= 5, "Maximum of 5 interests can be added."); + + for (uint256 i = 0; i < students[_address].interest.length; i++) { + require( + keccak256(bytes(students[_address].interest[i])) != keccak256(bytes(_interest)), + "Interest already exists." + ); + } + students[_address].interest.push(_interest); + emit InterestAdded(_address, _interest); + } + + /// @dev Removes an interest from a student's profile. + /// @param _addr The address of the student. + /// @param _interest The interest to remove. + function removeInterest(address _addr, string memory _interest) public studentExists(_addr) { + require(bytes(students[_addr].Name).length >= 0, "Interest cannot be empty."); + bool indexFound = false; + uint indexToRemove; + for (uint i = 0; i < students[_addr].interest.length; i++) { + if (keccak256(bytes(students[_addr].interest[i])) == keccak256(bytes(_interest))) { + indexFound = true; + indexToRemove = i; + break; + } + } + require(indexFound, "The provided interest does not exist."); + + // Move the last element into the place of the element to remove + students[_addr].interest[indexToRemove] = students[_addr].interest[students[_addr].interest.length - 1]; + + // Remove the last element + students[_addr].interest.pop(); + + emit InterestRemoved(_addr, _interest); + } + + /// @dev Retrieves the name of a student. + /// @param _addr The address of the student. + /// @return The name of the student. + function getStudentName(address _addr) public view studentExists(_addr) returns (string memory) { + return students[_addr].Name; + } + + /// @dev Retrieves the attendance status of a student. + /// @param _address The address of the student. + /// @return The attendance status of the student. + function getStudentAttendance(address _address) public view studentExists(_address) returns (Attendance) { + return students[_address].attendance; + } + + /// @dev Retrieves the list of interests of a student. + /// @param _address The address of the student. + /// @return The list of interests of the student. + function getStudentInterests(address _address) public view studentExists(_address) returns (string[] memory) { + return students[_address].interest; + } + + /// @dev Transfers ownership of the contract to a new address. + /// @param _newOwner The address of the new owner. + function transferOwnership(address _newOwner) public OnlyOwner { + owner = _newOwner; + } +} diff --git a/package-lock.json b/package-lock.json index 130ec26a..17dce013 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,8 @@ "": { "name": "Cohort-6", "dependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^2.0.7" + "@nomicfoundation/hardhat-chai-matchers": "^2.0.7", + "glob": "^11.0.1" }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^5.0.0", @@ -871,6 +872,102 @@ "node": ">=14" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -928,9 +1025,9 @@ } }, "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==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", "license": "MIT" }, "node_modules/@metamask/eth-sig-util/node_modules/ethereumjs-util": { @@ -1031,6 +1128,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.7.0.tgz", "integrity": "sha512-+Zyu7TE47TGNcPhOfWLPA/zISs32WDMXrhSWdWYyPHDVn/Uux5TVuOeScKb0BR/R8EJ+leR8COUF/EGxvDOVKg==", + "license": "MIT", "dependencies": { "@nomicfoundation/edr-darwin-arm64": "0.7.0", "@nomicfoundation/edr-darwin-x64": "0.7.0", @@ -1048,6 +1146,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.7.0.tgz", "integrity": "sha512-vAH20oh4GaSB/iQFTRcoO8jLc0CLd9XuLY9I7vtcqZWAiM4U1J4Y8cu67PWmtxbvUQOqXR7S6FtAr8/AlWm14g==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -1056,6 +1155,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.7.0.tgz", "integrity": "sha512-WHDdIrPvLlgXQr2eKypBM5xOZAwdxhDAEQIvEMQL8tEEm2qYW2bliUlssBPrs8E3bdivFbe1HizImslMAfU3+g==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -1064,6 +1164,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.7.0.tgz", "integrity": "sha512-WXpJB54ukz1no7gxCPXVEw9pgl/9UZ/WO3l1ctyv/T7vOygjqA4SUd6kppTs6MNXAuTiisPtvJ/fmvHiMBLrsw==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -1072,6 +1173,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.7.0.tgz", "integrity": "sha512-1iZYOcEgc+zJI7JQrlAFziuy9sBz1WgnIx3HIIu0J7lBRZ/AXeHHgATb+4InqxtEx9O3W8A0s7f11SyFqJL4Aw==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -1080,6 +1182,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.7.0.tgz", "integrity": "sha512-wSjC94WcR5MM8sg9w3OsAmT6+bbmChJw6uJKoXR3qscps/jdhjzJWzfgT0XGRq3XMUfimyafW2RWOyfX3ouhrQ==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -1088,6 +1191,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.7.0.tgz", "integrity": "sha512-Us22+AZ7wkG1mZwxqE4S4ZcuwkEA5VrUiBOJSvKHGOgy6vFvB/Euh5Lkp4GovwjrtiXuvyGO2UmtkzymZKDxZw==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -1096,6 +1200,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.7.0.tgz", "integrity": "sha512-HAry0heTsWkzReVtjHwoIq3BgFCvXpVhJ5qPmTnegZGsr/KxqvMmHyDMifzKao4bycU8yrpTSyOiAJt27RWjzQ==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -1166,9 +1271,9 @@ } }, "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==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.8.tgz", + "integrity": "sha512-Z5PiCXH4xhNLASROlSUOADfhfpfhYO6D7Hn9xp8PddmHey0jq704cr6kfU8TRrQ4PUZbpfsZadPj+pCfZdjPIg==", "license": "MIT", "dependencies": { "@types/chai-as-promised": "^7.1.3", @@ -1184,9 +1289,9 @@ } }, "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==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.8.tgz", + "integrity": "sha512-zhOZ4hdRORls31DTOqg+GmEZM0ujly8GGIuRY7t7szEk2zW/arY1qDug/py8AEktT00v5K+b6RvbVog+va51IA==", "license": "MIT", "peer": true, "dependencies": { @@ -1199,18 +1304,19 @@ } }, "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==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition/-/hardhat-ignition-0.15.9.tgz", + "integrity": "sha512-lSWqhaDOBt6gsqMadkRLvH6HdoFV1v8/bx7z+12cghaOloVwwn48CPoTH2iXXnkqilPGw8rdH5eVTE6UM+2v6Q==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@nomicfoundation/ignition-core": "^0.15.5", - "@nomicfoundation/ignition-ui": "^0.15.5", + "@nomicfoundation/ignition-core": "^0.15.9", + "@nomicfoundation/ignition-ui": "^0.15.9", "chalk": "^4.0.0", "debug": "^4.3.2", "fs-extra": "^10.0.0", + "json5": "^2.2.3", "prompts": "^2.4.2" }, "peerDependencies": { @@ -1219,24 +1325,24 @@ } }, "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==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition-ethers/-/hardhat-ignition-ethers-0.15.9.tgz", + "integrity": "sha512-9PwwgLv3z2ec3B26mK0IjiFezHFFBcBcs1qKaRu8SanARE4b7RvrfiLIy8ZXE7HaxgPt32kSsQzehhzAwAIj1Q==", "dev": true, "license": "MIT", "peer": true, "peerDependencies": { "@nomicfoundation/hardhat-ethers": "^3.0.4", - "@nomicfoundation/hardhat-ignition": "^0.15.5", - "@nomicfoundation/ignition-core": "^0.15.5", + "@nomicfoundation/hardhat-ignition": "^0.15.9", + "@nomicfoundation/ignition-core": "^0.15.9", "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==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.12.tgz", + "integrity": "sha512-xTNQNI/9xkHvjmCJnJOTyqDSl8uq1rKb2WOVmixQxFtRd7Oa3ecO8zM0cyC2YmOK+jHB9WPZ+F/ijkHg1CoORA==", "dev": true, "license": "MIT", "peer": true, @@ -1275,9 +1381,9 @@ } }, "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==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.12.tgz", + "integrity": "sha512-Lg3Nu7DCXASQRVI/YysjuAX2z8jwOCbS0w5tz2HalWGSTZThqA0v9N0v0psHbKNqzPJa8bNOeapIVSziyJTnAg==", "dev": true, "license": "MIT", "peer": true, @@ -1285,9 +1391,9 @@ "@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", + "picocolors": "^1.1.0", "semver": "^6.3.0", "table": "^6.8.0", "undici": "^5.14.0" @@ -1296,95 +1402,10 @@ "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-core/-/ignition-core-0.15.9.tgz", + "integrity": "sha512-X8W+7UP/UQPorpHUnGvA1OdsEr/edGi8tDpNwEqzaLm83FMZVbRWdOsr3vNICHN2XMzNY/xIm18Cx7xGKL2PQw==", "dev": true, "license": "MIT", "peer": true, @@ -1440,9 +1461,9 @@ } }, "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==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-ui/-/ignition-ui-0.15.9.tgz", + "integrity": "sha512-8lzbT7gpJ5PoowPQDQilkwdyqBviUKDMoHp/5rhgnwG1bDslnCS+Lxuo6s9R2akWu9LtEL14dNyqQb6WsURTag==", "dev": true, "peer": true }, @@ -1535,9 +1556,9 @@ } }, "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==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" @@ -1849,18 +1870,18 @@ } }, "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==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz", + "integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==", "license": "MIT", "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==", + "version": "4.3.20", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", + "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", "license": "MIT" }, "node_modules/@types/chai-as-promised": { @@ -1921,20 +1942,20 @@ "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==", + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", "dev": true, "license": "MIT", "peer": true }, "node_modules/@types/node": { - "version": "20.14.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz", - "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==", + "version": "22.13.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.2.tgz", + "integrity": "sha512-Z+r8y3XL9ZpI2EY52YYygAFmo2/oWfNSj4BCpAXE2McAexDk8VcnBMGC9Djn9gTKt4d2T/hhXqmPzo4hfIXtTg==", "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.20.0" } }, "node_modules/@types/pbkdf2": { @@ -1955,9 +1976,9 @@ "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==", + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", "dev": true, "license": "MIT", "peer": true @@ -1980,9 +2001,9 @@ "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==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "devOptional": true, "license": "MIT", "peer": true, @@ -1994,9 +2015,9 @@ } }, "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==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "devOptional": true, "license": "MIT", "peer": true, @@ -2260,9 +2281,9 @@ } }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "dev": true, "license": "MIT", "peer": true, @@ -2354,15 +2375,12 @@ } }, "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, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "license": "MIT", - "peer": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -2444,19 +2462,31 @@ "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==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "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" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -2570,27 +2600,18 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "license": "MIT", "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" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" } }, "node_modules/ci-info": { @@ -2600,13 +2621,16 @@ "license": "MIT" }, "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==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", + "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" } }, "node_modules/clean-stack": { @@ -3022,6 +3046,20 @@ "license": "MIT", "peer": true }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/crypt": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", @@ -3041,12 +3079,12 @@ "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==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -3100,25 +3138,6 @@ "license": "MIT", "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, - "license": "MIT", - "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", @@ -3175,6 +3194,28 @@ "node": ">=8" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -3191,9 +3232,9 @@ } }, "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==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", "license": "MIT" }, "node_modules/emoji-regex": { @@ -3225,15 +3266,12 @@ } }, "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==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", "peer": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -3249,10 +3287,24 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "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==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" @@ -3488,14 +3540,14 @@ } }, "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==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", "dev": true, "license": "MIT", "peer": true, "engines": { - "node": ">= 16" + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -3528,6 +3580,7 @@ "version": "0.6.8", "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "deprecated": "This library has been deprecated and usage is discouraged.", "license": "MIT", "dependencies": { "bn.js": "^4.11.8", @@ -3544,9 +3597,9 @@ } }, "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==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", "license": "MIT" }, "node_modules/ethereumjs-abi/node_modules/ethereumjs-util": { @@ -3583,9 +3636,9 @@ } }, "node_modules/ethers": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.2.tgz", - "integrity": "sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==", + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.5.tgz", + "integrity": "sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ==", "funding": [ { "type": "individual", @@ -3602,9 +3655,9 @@ "@adraffy/ens-normalize": "1.10.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.2", - "@types/node": "18.15.13", + "@types/node": "22.7.5", "aes-js": "4.0.0-beta.5", - "tslib": "2.4.0", + "tslib": "2.7.0", "ws": "8.17.1" }, "engines": { @@ -3612,9 +3665,19 @@ } }, "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==", + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "license": "MIT", "peer": true }, @@ -3675,9 +3738,9 @@ "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==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "peer": true, @@ -3686,7 +3749,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -3701,17 +3764,27 @@ "peer": true }, "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", "peer": true }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "dev": true, "license": "ISC", "peer": true, @@ -3749,6 +3822,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3770,9 +3844,9 @@ } }, "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==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -3789,10 +3863,26 @@ } } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "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==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, "license": "MIT", "peer": true, @@ -3885,18 +3975,23 @@ } }, "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==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3916,6 +4011,21 @@ "node": ">=4" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ghost-testrpc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", @@ -4017,23 +4127,23 @@ } }, "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", - "dev": true, + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", "license": "ISC", - "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" + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4081,6 +4191,20 @@ "node": ">=6" } }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/globby": { "version": "10.0.2", "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", @@ -4102,15 +4226,64 @@ "node": ">=8" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/globby/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, "license": "MIT", "peer": true, "dependencies": { - "get-intrinsic": "^1.1.3" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/globby/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globby/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, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4284,20 +4457,6 @@ "@scure/base": "~1.1.0" } }, - "node_modules/hardhat/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/hardhat/node_modules/ethereum-cryptography": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", @@ -4333,18 +4492,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/hardhat/node_modules/readdirp": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", - "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/hardhat/node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -4384,38 +4531,10 @@ "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, - "license": "MIT", - "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, - "license": "MIT", - "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", "peer": true, @@ -4570,9 +4689,9 @@ } }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "peer": true, @@ -4746,9 +4865,22 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.3.tgz", + "integrity": "sha512-oSwM7q8PTHQWuZAlp995iPpPJ4Vkl7qT0ZRD+9duL9j2oBy6KcTfyxc8mEuHJYC+z/kbps80aJLkaNzTOrf/kw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/js-sha3": { "version": "0.8.0", @@ -4780,6 +4912,7 @@ "version": "3.1.6", "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz", "integrity": "sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==", + "license": "MIT", "engines": { "node": ">=7.10.1" } @@ -4792,6 +4925,20 @@ "license": "ISC", "peer": true }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -4807,9 +4954,9 @@ } }, "node_modules/jsonschema": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", - "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz", + "integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==", "dev": true, "license": "MIT", "peer": true, @@ -4873,6 +5020,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -4909,6 +5057,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", "license": "MIT", "peer": true }, @@ -4952,6 +5101,15 @@ "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", "license": "MIT" }, + "node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -4968,6 +5126,17 @@ "license": "MIT", "peer": true }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -5007,9 +5176,9 @@ "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==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "peer": true, @@ -5059,17 +5228,18 @@ "license": "MIT" }, "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, + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "license": "ISC", - "peer": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -5083,6 +5253,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -5107,9 +5286,9 @@ } }, "node_modules/mocha": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", - "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", "license": "MIT", "dependencies": { "ansi-colors": "^4.1.3", @@ -5141,13 +5320,28 @@ "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==", + "node_modules/mocha/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "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/mocha/node_modules/glob": { @@ -5182,11 +5376,17 @@ "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==", - "license": "MIT" + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", @@ -5204,10 +5404,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==", - "license": "MIT" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/ndjson": { "version": "2.0.0", @@ -5256,9 +5456,9 @@ } }, "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==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "license": "MIT", "bin": { "node-gyp-build": "bin.js", @@ -5336,9 +5536,9 @@ } }, "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==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "peer": true, @@ -5350,9 +5550,9 @@ } }, "node_modules/obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", + "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", "license": "MIT" }, "node_modules/once": { @@ -5402,6 +5602,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -5416,6 +5617,7 @@ "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==", + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -5441,6 +5643,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parse-cache-control": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", @@ -5452,6 +5660,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -5467,12 +5676,37 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "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==", "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -5513,7 +5747,8 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -5608,14 +5843,14 @@ "peer": true }, "node_modules/qs": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.3.tgz", - "integrity": "sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "dev": true, "license": "BSD-3-Clause", "peer": true, "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -5685,15 +5920,16 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", + "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/rechoir": { @@ -5723,6 +5959,32 @@ "node": ">=6.0.0" } }, + "node_modules/recursive-readdir/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, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/recursive-readdir/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, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/reduce-flatten": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", @@ -5928,6 +6190,18 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/sc-istanbul/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, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/sc-istanbul/node_modules/glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", @@ -5988,6 +6262,20 @@ "node": ">=4" } }, + "node_modules/sc-istanbul/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, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/sc-istanbul/node_modules/resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", @@ -6010,6 +6298,20 @@ "node": ">=0.8.0" } }, + "node_modules/sc-istanbul/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/scrypt-js": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", @@ -6017,20 +6319,47 @@ "license": "MIT" }, "node_modules/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", + "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "elliptic": "^6.5.4", - "node-addon-api": "^2.0.0", + "elliptic": "^6.5.7", + "node-addon-api": "^5.0.0", "node-gyp-build": "^4.2.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/secp256k1/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "license": "MIT" + }, + "node_modules/secp256k1/node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/secp256k1/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "license": "MIT" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -6049,25 +6378,6 @@ "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, - "license": "MIT", - "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", @@ -6108,6 +6418,27 @@ "node": "*" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/shelljs": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", @@ -6127,18 +6458,68 @@ "node": ">=4" } }, + "node_modules/shelljs/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, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/shelljs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/shelljs/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, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6147,6 +6528,77 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -6216,15 +6668,15 @@ } }, "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==", + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.14.tgz", + "integrity": "sha512-ItAAObe5GaEOp20kXC2BZRnph+9P7Rtoqg2mQc2SXGEHgSDF2wWd1Wxz3ntzQWXkbCtIIGdJT918HG00cObwbA==", "dev": true, "license": "ISC", "peer": true, "dependencies": { "@ethersproject/abi": "^5.0.9", - "@solidity-parser/parser": "^0.18.0", + "@solidity-parser/parser": "^0.19.0", "chalk": "^2.4.2", "death": "^1.1.0", "difflib": "^0.2.4", @@ -6251,9 +6703,9 @@ } }, "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==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.19.0.tgz", + "integrity": "sha512-RV16k/qIxW/wWc+mLzV3ARyKUaMUTBy9tOLMzFhtNSKYeTAanQ3a5MudJKf/8arIFnA2L27SNjarQKmFg0w/jA==", "dev": true, "license": "MIT", "peer": true @@ -6357,9 +6809,9 @@ } }, "node_modules/solidity-coverage/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "peer": true, @@ -6508,6 +6960,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "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", @@ -6520,6 +6987,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "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", @@ -6585,9 +7065,9 @@ } }, "node_modules/table": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", - "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, "license": "BSD-3-Clause", "peer": true, @@ -6674,16 +7154,17 @@ "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==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" }, "engines": { "node": ">= 0.12" @@ -6704,6 +7185,7 @@ "version": "0.2.10", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "license": "MIT", "dependencies": { "fdir": "^6.4.2", "picomatch": "^4.0.2" @@ -6716,6 +7198,7 @@ "version": "6.4.3", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -6729,6 +7212,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -6854,9 +7338,9 @@ } }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "license": "0BSD", "peer": true }, @@ -6939,6 +7423,18 @@ "typescript": ">=4.3.0" } }, + "node_modules/typechain/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, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/typechain/node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -6989,6 +7485,20 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/typechain/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, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/typechain/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -7023,9 +7533,9 @@ "peer": true }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "devOptional": true, "license": "Apache-2.0", "peer": true, @@ -7049,9 +7559,9 @@ } }, "node_modules/uglify-js": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", - "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, "license": "BSD-2-Clause", "optional": true, @@ -7064,9 +7574,9 @@ } }, "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "version": "5.28.5", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz", + "integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==", "license": "MIT", "dependencies": { "@fastify/busboy": "^2.0.0" @@ -7076,9 +7586,9 @@ } }, "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==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, "node_modules/universalify": { @@ -7196,17 +7706,18 @@ } }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", - "peer": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/widest-line": { @@ -7289,6 +7800,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "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", @@ -7383,6 +7912,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index 3f454a7e..530eb8cd 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "hardhat": "^2.22.18" }, "dependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^2.0.7" + "@nomicfoundation/hardhat-chai-matchers": "^2.0.7", + "glob": "^11.0.1" }, "scripts": { "format": "prettier --write \"test/**/*.js\"", From fc6757a4498346b7647a06d1a3086a87f2b74e03 Mon Sep 17 00:00:00 2001 From: frankhood Date: Fri, 14 Feb 2025 17:15:03 +0100 Subject: [PATCH 2/9] add counter contract --- contracts/counter.frankhood.sol | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 contracts/counter.frankhood.sol diff --git a/contracts/counter.frankhood.sol b/contracts/counter.frankhood.sol new file mode 100644 index 00000000..e69de29b From 2be84a1b4964d8de0ddea6545e148160939f7fb3 Mon Sep 17 00:00:00 2001 From: alexohre Date: Wed, 19 Feb 2025 12:32:07 +0100 Subject: [PATCH 3/9] added link to assignment 1 Signed-off-by: alexohre --- submissions/Assignment1/README.md | 3 +++ submissions/Assignment2/README.md | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 submissions/Assignment1/README.md create mode 100644 submissions/Assignment2/README.md diff --git a/submissions/Assignment1/README.md b/submissions/Assignment1/README.md new file mode 100644 index 00000000..cad7bad2 --- /dev/null +++ b/submissions/Assignment1/README.md @@ -0,0 +1,3 @@ +# Submissions for Assignment 3 + +Link to Counter Assignment1 [Here](../../contracts/counter_alex.sol) diff --git a/submissions/Assignment2/README.md b/submissions/Assignment2/README.md new file mode 100644 index 00000000..6f3a09c7 --- /dev/null +++ b/submissions/Assignment2/README.md @@ -0,0 +1,3 @@ +# Submissions for Assignment 3 +--- +--- \ No newline at end of file From 459ca00ca80e3d318665fd85bd365e1002460507 Mon Sep 17 00:00:00 2001 From: alexohre Date: Mon, 10 Mar 2025 17:42:38 +0100 Subject: [PATCH 4/9] refac: update Crowdfunding contract Signed-off-by: alexohre --- .gitmodules | 3 + contracts/crowdfunding_alex.sol | 6 + contracts/reward_nft_alex.sol | 6 + contracts/reward_token_alex.sol | 6 + contracts/with-foundry/Counter.sol | 70 ++++ contracts/with-foundry/Crowdfunding.sol | 132 ++++++++ contracts/with-foundry/Payable.sol | 114 +++++++ contracts/with-foundry/RewardNft.sol | 88 +++++ contracts/with-foundry/RewardToken.sol | 137 ++++++++ foundry.toml | 1 + lib/openzeppelin-contracts | 1 + test/Counter.t.sol | 213 +++++++++++- test/Crowdfunding.t.sol | 431 ++++++++++++++++++++++++ test/Payable.t.sol | 70 ++++ 14 files changed, 1271 insertions(+), 7 deletions(-) create mode 100644 contracts/crowdfunding_alex.sol create mode 100644 contracts/reward_nft_alex.sol create mode 100644 contracts/reward_token_alex.sol create mode 100644 contracts/with-foundry/Counter.sol create mode 100644 contracts/with-foundry/Crowdfunding.sol create mode 100644 contracts/with-foundry/Payable.sol create mode 100644 contracts/with-foundry/RewardNft.sol create mode 100644 contracts/with-foundry/RewardToken.sol create mode 160000 lib/openzeppelin-contracts create mode 100644 test/Crowdfunding.t.sol create mode 100644 test/Payable.t.sol diff --git a/.gitmodules b/.gitmodules index 888d42dc..690924b6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/contracts/crowdfunding_alex.sol b/contracts/crowdfunding_alex.sol new file mode 100644 index 00000000..2447dc4f --- /dev/null +++ b/contracts/crowdfunding_alex.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import "./reward_token.sol"; +import "./reward_nft_alex.sol"; +contract CrowdFund {} diff --git a/contracts/reward_nft_alex.sol b/contracts/reward_nft_alex.sol new file mode 100644 index 00000000..8c9600bc --- /dev/null +++ b/contracts/reward_nft_alex.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +contract RewardNFT { + // ... +} diff --git a/contracts/reward_token_alex.sol b/contracts/reward_token_alex.sol new file mode 100644 index 00000000..f2611882 --- /dev/null +++ b/contracts/reward_token_alex.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +contract RewardToken { + // ... +} diff --git a/contracts/with-foundry/Counter.sol b/contracts/with-foundry/Counter.sol new file mode 100644 index 00000000..a28e5d32 --- /dev/null +++ b/contracts/with-foundry/Counter.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + address public owner; + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner, "Only owner can call this function"); + _; + } + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + // test increment function + function increment() public onlyOwner { + number++; + } + + // test decrement function + function decrement() public onlyOwner { + number--; + } + + // test add by value function + function add(uint256 x) public onlyOwner { + number += x; + } + + // test substract by value function + function sub(uint256 x) public onlyOwner { + number -= x; + } + + // test multiply by value function + function mul(uint256 x) public onlyOwner { + number *= x; + } + + // test divide by value function + function div(uint256 x) public onlyOwner { + number /= x; + } + + // test if a number is even + function isEven(uint256 x) public view onlyOwner returns (bool) { + return x % 2 == 0; + } + + // test if a number is odd + function isOdd(uint256 x) public view onlyOwner returns (bool) { + return x % 2 != 0; + } + + // test reset function + function reset() public onlyOwner { + number = 0; + } + + // test get total function + function getTotal() public view onlyOwner returns (uint256) { + return number; + } +} diff --git a/contracts/with-foundry/Crowdfunding.sol b/contracts/with-foundry/Crowdfunding.sol new file mode 100644 index 00000000..819df009 --- /dev/null +++ b/contracts/with-foundry/Crowdfunding.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; +import {console} from "forge-std/Test.sol"; + +import "./RewardToken.sol"; +import "./RewardNft.sol"; + +contract Crowdfunding { + address public Owner; + uint public constant FUNDING_GOAL = 50 ether; + uint public constant NFT_THRESHOLD = 5 ether; + uint256 public totalFundsRaised; + bool public isFundingComplete; + // address public constant NFT_CONTRACT_ADDRESS = + // 0x2e234DAe75C793f67A35089C9d99245E1C58470b; + // address public constant TOKEN_CONTRACT_ADDRESS = + // 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; + + RewardToken public rewardToken; + RewardNft public rewardNFT; + uint256 public tokenRewardRate; + + // Contribution tracking + mapping(address => uint256) public contributions; + mapping(address => bool) public hasReceivedNFT; + + // Events + event ContributionReceived(address indexed contributor, uint256 amount); + event TokenRewardSent(address indexed contributor, uint256 amount); + event NFTRewardSent(address indexed contributor, uint256 tokenId); + event FundsWithdrawn(address indexed projectOwner, uint256 amount); + + constructor( + uint256 _tokenRewardRate, + address _rewardToken, + address _rewardNft + ) { + Owner = msg.sender; + rewardToken = RewardToken(_rewardToken); + rewardNFT = RewardNft(_rewardNft); + tokenRewardRate = _tokenRewardRate; + } + + function contribute() external payable { + // console.log("Ether Value contribution___%s", msg.value); + require(msg.value > 0, "Contribution must be greater than 0"); + require(!isFundingComplete, "Funding goal already reached"); + + // Calculate contribution amount and process any refunds + uint256 contributionAmount = _calculateContributionAndRefund(msg.value); + // console.log("contributed Amount____%s", contributionAmount); + // Update contribution record + contributions[msg.sender] += contributionAmount; + totalFundsRaised += contributionAmount; + // console.log("total funds raised____%s", totalFundsRaised); + + // Check if funding goal is reached + if (totalFundsRaised >= FUNDING_GOAL) { + isFundingComplete = true; + // console.log("isComplete____%s", isFundingComplete); + } + + // Calculate token reward + uint256 tokenReward = calculateReward(msg.value); + + console.log("token reward____%s", tokenReward); + + if (tokenReward > 0) { + console.log("the contract caller____%s", msg.sender); + sendRewardToken(tokenReward, msg.sender); + console.log("token reward____%s", tokenReward); + emit TokenRewardSent(msg.sender, tokenReward); + } + + // Check for NFT eligibility + if ( + contributions[msg.sender] >= NFT_THRESHOLD && + !hasReceivedNFT[msg.sender] + ) { + uint256 tokenId = rewardNFT.mintNFT(msg.sender); + hasReceivedNFT[msg.sender] = true; + emit NFTRewardSent(msg.sender, tokenId); + } + + emit ContributionReceived(msg.sender, msg.value); + } + + function calculateReward(uint256 _value) private returns (uint256) { + uint256 tokenReward = (_value * tokenRewardRate) / 1 ether; + + return tokenReward; + } + + function sendRewardToken(uint256 _amount, address _recipient) private { + uint256 rewardAmount = calculateReward(_amount); + rewardToken.transferFrom(address(this), _recipient, rewardAmount); + } + + function _calculateContributionAndRefund( + uint256 _contributionAmount + ) private returns (uint256) { + // Calculate the remaining amount needed to complete the funding goal + uint256 remainingAmount = FUNDING_GOAL - totalFundsRaised; + uint256 contributionAmount = _contributionAmount; + + // If contribution exceeds remaining goal, adjust contribution and refund excess + if (_contributionAmount > remainingAmount) { + contributionAmount = remainingAmount; + uint256 refundAmount = _contributionAmount - remainingAmount; + payable(msg.sender).transfer(refundAmount); + } + + return contributionAmount; + } + + function withdrawFunds() external { + require(msg.sender == Owner, "Only project owner can withdraw"); + require(isFundingComplete, "Funding goal not yet reached"); + require(address(this).balance > 0, "No funds to withdraw"); + + uint256 amount = address(this).balance; + payable(Owner).transfer(amount); + + emit FundsWithdrawn(Owner, amount); + } + + function getContribution( + address contributor + ) external view returns (uint256) { + return contributions[contributor]; + } +} diff --git a/contracts/with-foundry/Payable.sol b/contracts/with-foundry/Payable.sol new file mode 100644 index 00000000..2adf6599 --- /dev/null +++ b/contracts/with-foundry/Payable.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/// @title Payable Contract +/// @notice This contract allows users to deposit ETH and keeps track of investments +/// @dev Provides basic payable functionalities with fallback and receive functions +contract Payable { + /// @notice Address of the contract owner + address payable public owner; + + /// @notice Counter for tracking fallback calls + uint256 public counter; + + /// @notice Mapping to store investments made by investors + mapping(address investors => uint256 amount) internal investments; + + /// @notice Error for calling a function that does not exist + error FunctionDoesNotExit(); + + /// @notice Constructor to initialize the contract owner + constructor() payable { + owner = payable(msg.sender); + } + + /// @notice Deposits ETH into the contract and records the amount for the sender + /// @param amount The amount of ETH to deposit + /// @return The deposited amount + function deposit(uint256 amount) public payable returns (uint256) { + require(amount == msg.value, "msg.value must equal eth"); + investments[msg.sender] = amount; + return amount; + } + + /// @notice Retrieves the investment amount for a given investor + /// @param investor The address of the investor + /// @return investment The amount invested by the investor + function getInvestment( + address investor + ) public view returns (uint256 investment) { + investment = investments[investor]; + } + + /// @notice Returns the contract's ETH balance in ether + /// @return contractBalance The balance of the contract in ether + function getContractBalance() + public + view + returns (uint256 contractBalance) + { + contractBalance = address(this).balance / 1 ether; + } + + /// @notice Returns the sender's ETH balance + /// @return myEthBalance The ETH balance of the sender + function getMyEthBalance() public view returns (uint256 myEthBalance) { + myEthBalance = address(msg.sender).balance; + } + + /// @notice Receive function to accept ETH transfers + receive() external payable {} + + /// @notice Fallback function to handle incorrect function calls + fallback() external payable { + counter += 1; + } +} + +/// @title Funder Contract +/// @notice This contract enables sending ETH using transfer, send, and call methods +contract Funder { + /// @notice Error when ETH transfer fails + error FailedToSendEth(); + + /// @notice Sends ETH to a receiver using transfer + /// @param _receiver The recipient's address + function sendWithTransfer(address payable _receiver) public payable { + _receiver.transfer(msg.value); + } + + /// @notice Sends ETH to a receiver using send + /// @param _receiver The recipient's address + /// @return sent Boolean indicating if the transfer was successful + function sendWithSend( + address payable _receiver + ) public payable returns (bool sent) { + sent = _receiver.send(msg.value); + if (!sent) { + revert FailedToSendEth(); + } + } + + /// @notice Sends ETH to a receiver using call + /// @param _receiver The recipient's address + /// @return sent Boolean indicating if the transfer was successful + /// @return data The returned data from the call + function sendWithCall( + address payable _receiver + ) public payable returns (bool sent, bytes memory data) { + (sent, data) = _receiver.call{value: msg.value}(""); + require(sent, "failed to send eth"); + } + + /// @notice Calls the deposit function of `Payable` contract + /// @param _payableContractAddress The address of the payable contract + function callDeposit( + address payable _payableContractAddress + ) public payable { + uint256 amount = msg.value; + (bool success, bytes memory data) = _payableContractAddress.call{ + value: amount + }(abi.encodeWithSignature("deposit(uint256)", amount)); + require(success, "call failed"); + } +} diff --git a/contracts/with-foundry/RewardNft.sol b/contracts/with-foundry/RewardNft.sol new file mode 100644 index 00000000..fc805d20 --- /dev/null +++ b/contracts/with-foundry/RewardNft.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +// import "@openzeppelin/contracts/utils/Counters.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +contract RewardNft is ERC721 { + // using Counters for Counters.Counter; + using Strings for uint256; + + uint256 public counter; + + address public owner; + + // Counters.Counter private _nftIds; + + uint256 public constant MAX_SUPPLY = 10; + + uint256 public totalMinted; + + string public uri; + + constructor( + string memory name_, + string memory symbol_, + string memory _uri + ) ERC721(name_, symbol_) { + uri = _uri; + owner = msg.sender; + } + + // function mintNFT(address minter) external { + // uint256 newTokenId = _nftIds.current(); + // require(newTokenId <= MAX_SUPPLY, "limit exceeded"); + // _safeMint(minter, newTokenId); + // _nftIds.increment(); + // totalMinted += 1; + // } + + function mintNFT(address to) public returns (uint256) { + uint256 currentCount = counter; + if (to == address(0)) { + revert ERC721InvalidReceiver(address(0)); + } + address previousOwner = _update(to, currentCount, address(0)); + counter++; + if (previousOwner != address(0)) { + revert ERC721InvalidSender(address(0)); + } + + return counter; + } + + function getTotalMinted() public view returns (uint256) { + return totalMinted; + } + + function _baseURI() internal view override returns (string memory) { + return uri; + } + + function setBaseUri(string memory _uri) public { + uri = _uri; + } + + // function tokenURI( + // uint256 tokenId + // ) public view override returns (string memory) { + // _requireMinted(tokenId); + + // string memory baseURI = _baseURI(); + // return + // bytes(baseURI).length > 0 + // ? string(abi.encodePacked(baseURI, tokenId.toString(), ".json")) + // : ""; + // } + + // function setTokenURI() public { + // _setTokenURI( + // totalMinted, + // "ipfs://QmeYhWhdX1ALiF5AeaHM5VwAR6XEUqL58kmdEx8GxxPkXk" + // ); + // } +} diff --git a/contracts/with-foundry/RewardToken.sol b/contracts/with-foundry/RewardToken.sol new file mode 100644 index 00000000..c4502036 --- /dev/null +++ b/contracts/with-foundry/RewardToken.sol @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title RewardToken - Simplified ERC20 + * @dev ERC20 token used as a reward for crowdfunding contributors + */ +contract RewardToken { + string public name = "RewardToken"; + string public symbol = "CRWD"; + uint8 public decimals = 18; + uint256 private _totalSupply; + + // Owner of the contract + address public owner; + // Only the crowdfunding contract can mint tokens + address public crowdfundingContract; + + // Balances for each account + mapping(address => uint256) private _balances; + // Allowances for each account + mapping(address => mapping(address => uint256)) private _allowances; + + // Events + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); + + // Modifier to restrict functions to owner + modifier onlyOwner() { + require(msg.sender == owner, "Not authorized"); + _; + } + + constructor() { + owner = msg.sender; + mintAllToOwner(); + } + + function setCrowdfundingContract( + address _crowdfundingContract + ) external onlyOwner { + crowdfundingContract = _crowdfundingContract; + } + + function totalSupply() external view returns (uint256) { + return _totalSupply; + } + + function balanceOf(address account) external view returns (uint256) { + return _balances[account]; + } + + // function mintReward(address to, uint256 amount) external { + // require( + // msg.sender == crowdfundingContract, + // "Only crowdfunding contract can mint" + // ); + // _mint(to, amount); + // } + + function mintAllToOwner() private onlyOwner { + // _mint(owner, _totalSupply); + _mint(owner, 5000); + } + + function transfer( + address recipient, + uint256 amount + ) external returns (bool) { + _transfer(msg.sender, recipient, amount); + return true; + } + + function approve(address spender, uint256 amount) external returns (bool) { + _approve(msg.sender, spender, amount); + return true; + } + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool) { + _transfer(sender, recipient, amount); + + uint256 currentAllowance = _allowances[sender][msg.sender]; + require( + currentAllowance >= amount, + "Transfer amount exceeds allowance" + ); + _approve(sender, msg.sender, currentAllowance - amount); + + return true; + } + + function _transfer( + address sender, + address recipient, + uint256 amount + ) internal { + require(sender != address(0), "Transfer from the zero address"); + require(recipient != address(0), "Transfer to the zero address"); + require(_balances[sender] >= amount, "Transfer amount exceeds balance"); + + _balances[sender] -= amount; + _balances[recipient] += amount; + + emit Transfer(sender, recipient, amount); + } + + function _mint(address account, uint256 amount) private { + require(msg.sender == owner, "Only owner can mint"); + require(account != address(0), "Mint to the zero address"); + + _totalSupply += amount; + _balances[account] += amount; + + emit Transfer(address(0), account, amount); + } + + function _approve( + address _owner, + address spender, + uint256 amount + ) internal { + require(_owner != address(0), "Approve from the zero address"); + require(spender != address(0), "Approve to the zero address"); + + _allowances[_owner][spender] = amount; + + emit Approval(_owner, spender, amount); + } +} diff --git a/foundry.toml b/foundry.toml index 06eeab42..b71cb3f5 100644 --- a/foundry.toml +++ b/foundry.toml @@ -5,6 +5,7 @@ libs = ["node_modules", "lib"] remappings = [ "eth-gas-reporter/=node_modules/eth-gas-reporter/", "hardhat/=node_modules/hardhat/", + "openzeppelin-contracts/=lib/openzeppelin-contracts/", ] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 00000000..ca7a4e39 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit ca7a4e39de0860bbaadf95824207886e6de9fa64 diff --git a/test/Counter.t.sol b/test/Counter.t.sol index 54b724f7..d236266d 100644 --- a/test/Counter.t.sol +++ b/test/Counter.t.sol @@ -2,23 +2,222 @@ pragma solidity ^0.8.13; import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; +import {Counter} from "../contracts/with-foundry/Counter.sol"; contract CounterTest is Test { Counter public counter; - + address public owner; + address public nonOwner; + // set up the counter contract deployment function setUp() public { + // Set up addresses + owner = address(1); + nonOwner = address(2); + + console.log("Owner: ", owner); + console.log("Non-Owner: ", nonOwner); + + // Deploy contract as owner + vm.prank(owner); counter = new Counter(); - counter.setNumber(0); } - + // test increment function function test_Increment() public { + assertEq(counter.number(), 0); + vm.prank(owner); counter.increment(); assertEq(counter.number(), 1); } - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); + // test decrement function + function test_Decrement() public { + assertEq(counter.number(), 0); + vm.prank(owner); + counter.add(5); + assertEq(counter.number(), 5); + vm.prank(owner); + counter.decrement(); + assertEq(counter.number(), 4); + } + + // test add by value function + function test_Add() public { + assertEq(counter.number(), 0); + vm.prank(owner); + counter.add(5); + assertEq(counter.number(), 5); + } + + // test substract by value function + function test_Sub() public { + assertEq(counter.number(), 0); + counter.setNumber(10); + vm.prank(owner); + counter.sub(5); + assertEq(counter.number(), 5); + } + + // test multiply by value function + function test_Mul() public { + assertEq(counter.number(), 0); + counter.setNumber(5); + vm.prank(owner); + counter.mul(5); + assertEq(counter.number(), 25); + } + + // test divide by value function + function test_Div() public { + assertEq(counter.number(), 0); + counter.setNumber(10); + vm.prank(owner); + counter.div(2); + assertEq(counter.number(), 5); + } + + // test if a number is even + function test_IsEven() public { + vm.startPrank(owner); + + // Test even numbers + assertTrue(counter.isEven(2), "2 should be even"); + assertTrue(counter.isEven(0), "0 should be even"); + assertTrue(counter.isEven(100), "100 should be even"); + + // Test that odd numbers return false + assertFalse(counter.isEven(1), "1 should not be even"); + assertFalse(counter.isEven(99), "99 should not be even"); + + vm.stopPrank(); + } + + // test if a number is odd + function testIsOdd() public { + vm.startPrank(owner); + + // Test odd numbers + assertTrue(counter.isOdd(1), "1 should be odd"); + assertTrue(counter.isOdd(99), "99 should be odd"); + assertTrue(counter.isOdd(777), "777 should be odd"); + + // Test that even numbers return false + assertFalse(counter.isOdd(2), "2 should not be odd"); + assertFalse(counter.isOdd(0), "0 should not be odd"); + + vm.stopPrank(); + } + + // test reset function + function test_Reset() public { + counter.setNumber(10); + vm.prank(owner); + counter.reset(); + assertEq(counter.number(), 0); + } + + // test get total function + function test_Get_Total() public { + assertEq(counter.number(), 0); + counter.setNumber(10); + vm.prank(owner); + counter.getTotal(); + assertEq(counter.number(), 10); + } + + // Testing for non-owner and expect revert with error message + // test increment function + function test_IncrementNonOwner() public { + assertEq(counter.number(), 0); + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.increment(); + assertEq(counter.number(), 0); + } + + // test decrement function + function test_DecrementNonOwner() public { + assertEq(counter.number(), 0); + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.add(5); + assertEq(counter.number(), 0); + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.decrement(); + assertEq(counter.number(), 0); + } + + // test add by value function + function test_AddNonOwner() public { + assertEq(counter.number(), 0); + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.add(5); + assertEq(counter.number(), 0); + } + + // test substract by value function + function test_SubNonOwner() public { + assertEq(counter.number(), 0); + counter.setNumber(10); + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.sub(5); + assertEq(counter.number(), 10); + } + + // test multiply by value function + function test_MulNonOwner() public { + assertEq(counter.number(), 0); + counter.setNumber(5); + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.mul(5); + assertEq(counter.number(), 5); + } + + // test divide by value function + function test_DivNonOwner() public { + assertEq(counter.number(), 0); + counter.setNumber(10); + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.div(2); + assertEq(counter.number(), 10); + } + + // test if a number is even + function test_IsEvenNonOwner() public { + // Test even numbers + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.isEven(2); + } + + // test if a number is odd + function testIsOddNonOwner() public { + // Test odd numbers + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.isOdd(9); + } + + // test reset function + function test_ResetNonOwner() public { + counter.setNumber(10); + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.reset(); + assertEq(counter.number(), 10); + } + + // // test get total function + function test_Get_TotalNonOwner() public { + assertEq(counter.number(), 0); + counter.setNumber(10); + vm.prank(nonOwner); + vm.expectRevert("Only owner can call this function"); + counter.getTotal(); + assertEq(counter.number(), 10); } } diff --git a/test/Crowdfunding.t.sol b/test/Crowdfunding.t.sol new file mode 100644 index 00000000..b7f35d05 --- /dev/null +++ b/test/Crowdfunding.t.sol @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {Test, console} from "forge-std/Test.sol"; +// import {Crowdfunding} from "../src/Crowdfunding.sol"; +import {Crowdfunding} from "../contracts/with-foundry/Crowdfunding.sol"; +import {RewardToken} from "../contracts/with-foundry/RewardToken.sol"; +import {RewardNft} from "../contracts/with-foundry/RewardNft.sol"; + +contract CrowdfundingTest is Test { + Crowdfunding public crowdfunding; + RewardToken public rewardtoken; + RewardNft public rewardnft; + address public nftAddr = 0x2e234DAe75C793f67A35089C9d99245E1C58470b; + address public tokenAddr = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; + // address public owner; + uint public constant FUNDING_GOAL = 50 ether; + uint public constant NFT_THRESHOLD = 5 ether; + uint256 public totalFundsRaised; + bool public isFundingComplete; + uint256 constant REWARD_RATE = 100; + + address crowdfundingAddr = address(this); + address owner = vm.addr(1); + address addr2 = vm.addr(2); + address addr3 = vm.addr(3); + address addr5 = vm.addr(5); + address addr4 = vm.addr(4); + // address crowdfundingAddr; + + receive() external payable {} + + event ContributionReceived(address indexed contributor, uint256 amount); + event NFTRewardSent(address indexed receiver, uint256 Id); + event TokenRewardSent(address indexed receiver, uint256 Amount); + event FundsWithdrawn(address indexed receiver, uint256 Amount); + + function calculateTokenReward(uint256 ethContribution) public view returns (uint256) { + return (ethContribution * crowdfunding.tokenRewardRate()) / 1 ether; + } + + function setUp() public { + vm.startPrank(owner); + + rewardtoken = new RewardToken(); + vm.stopPrank(); + vm.startPrank(owner); + rewardnft = new RewardNft("RewardNft", "RNFT", "ipfs://"); + vm.stopPrank(); + // vm.startPrank(owner); + console.log("rewardtoken:", address(rewardtoken)); + console.log("rewardnft:", address(rewardnft)); + vm.startPrank(owner); + // Transfer Reward tokens from owner to the contract + rewardtoken.transfer(address(this), 5000); + + crowdfunding = new Crowdfunding(REWARD_RATE, address(rewardtoken), address(rewardnft)); + vm.stopPrank(); + + vm.deal(addr2, 100 ether); + vm.deal(addr3, 100 ether); + vm.deal(addr4, 100 ether); + // address crowdfundingAddr = address(this); + + // Set crowdfunding contract in the token contracts + // vm.startPrank(owner); + // // rewardtoken.setCrowdfundingContract(crowdfundingAddr); + // // rewardnft.setCrowdfundingContract(crowdfundingAddr); + // vm.stopPrank(); + // vm.Prank(owner); + console.log("Owner_____:", owner); + console.log("Address 2____:", addr2); + console.log("Address 3:", addr3); + console.log("Contract Address:", crowdfundingAddr); + // Log the addresses + console.log("Crowdfunding contract address:", address(crowdfunding)); + console.log("RewardToken contract address:", address(rewardtoken)); + console.log("RewardNFT contract address:", address(rewardnft)); + // vm.stopPrank(); + } + + // ******DEPLOYMENT******// + // state variables at deployment + // Should set the correct CrowdFunding contract owner + function test_setContractOwner() public view { + assertEq(crowdfunding.Owner(), owner); + } + // Should set the correct crowd Token contract owner + function test_setTokenContractOwner() public view { + assertEq(rewardtoken.owner(), owner); + } + + function test_RewardTokenBalanceOfCrowdfundingOnDeployment() public view { + uint256 contractBal1 = rewardtoken.balanceOf(crowdfundingAddr); + assertEq(contractBal1, 5000); + + uint256 ownerRewardTokenBalance = rewardtoken.balanceOf(owner); + assertEq(ownerRewardTokenBalance, 0); + } + // Should set the correct rewardNFT contract owner + // function test_setNFTContractOwner() public view { + // assertEq(rewardnft.owner(), owner); + // } + + // Should set the correct Nft contract Address + // function test_setNFTContractAddr() public view { + // assertEq(crowdfunding.NFT_CONTRACT_ADDRESS(), nftAddr); + // } + + // // Should set the correct Token contract Address + // function test_setTokenContractAddr() public view { + // assertEq(crowdfunding.TOKEN_CONTRACT_ADDRESS(), tokenAddr); + // } + // Should set the correct funding goal + function test_setCorrectFundingGoal() public view { + assertEq(crowdfunding.FUNDING_GOAL(), FUNDING_GOAL); + } + // Should set the correct token reward rate + function test_setTokenReward() public view { + assertEq(crowdfunding.tokenRewardRate(), REWARD_RATE); + } + // Should set the correct NFT threshold + function test_set_NFT_Threshold() public view { + assertEq(crowdfunding.NFT_THRESHOLD(), NFT_THRESHOLD); + } + // Should determine that totalFundsRaised is zero initially + function test_total_funds_raised() public view { + assertEq(crowdfunding.totalFundsRaised(), 0); + } + // Should set isFundingComplete to false initially + function test_is_funding_complete() public view { + assertEq(crowdfunding.isFundingComplete(), false); + } + + // Transactions + // Allows Eth contrib. + function test_Allows_eth_contribution() public { + uint256 contributionAmount = 10 ether; + uint256 initialEthBalanceAddr2 = addr2.balance; + uint256 initialBalanceCrowdFunding = address(crowdfunding).balance; + + uint256 contractBal1 = rewardtoken.balanceOf(crowdfundingAddr); + assertEq(contractBal1, 5000); + + uint256 ownerRewardTokenBalance = rewardtoken.balanceOf(owner); + assertEq(ownerRewardTokenBalance, 0); + + assertEq(initialBalanceCrowdFunding, 0); + assertEq(initialEthBalanceAddr2, 100 ether); + // Perform the contribution + vm.startPrank(addr2); + console.log("Addr2_____", addr2); + // vm.prank(addr2); + crowdfunding.contribute{value: contributionAmount}(); + // assertEq(initialBalanceCrowdFunding, 10); + vm.stopPrank(); + uint256 finalBalanceaddr2 = addr2.balance; + uint256 finalBalanceCrowdFunding = address(crowdfunding).balance; + + assertEq(finalBalanceaddr2, initialEthBalanceAddr2 - contributionAmount); + assertEq(finalBalanceCrowdFunding, initialBalanceCrowdFunding + contributionAmount); + } + + // determine that the token reward amount is based on contrib + function test_token_reward_amount() public { + uint256 rewardAmount = calculateTokenReward(2 ether); + + vm.prank(addr2); + crowdfunding.contribute{value: 2 ether}(); + + uint256 addr2RewardTokenBalance = rewardtoken.balanceOf(addr2); + + assertEq(addr2RewardTokenBalance, rewardAmount); + } + + // mint tokens based on contrib amount + function test_mint_tokens_based_on_contribution() public { + uint256 expectedTokens = calculateTokenReward(2 ether); + + vm.prank(addr2); + crowdfunding.contribute{value: 2 ether}(); + assertEq(rewardtoken.balanceOf(addr2), expectedTokens); + } + + // should not mint NFT below threshold + function test_not_mint_nft_below_threshold() public { + vm.prank(addr2); + crowdfunding.contribute{value: 3 ether}(); + assertEq(rewardnft.balanceOf(addr2), 0); + + assertEq(crowdfunding.hasReceivedNFT(addr2), false); + } + + // should mint NFT + function test_should_mint_nft() public { + vm.prank(addr2); + crowdfunding.contribute{value: 10 ether}(); + assertEq(rewardnft.balanceOf(addr2), 1); + assertEq(crowdfunding.hasReceivedNFT(addr2), true); + } + + // should mint NFT for cummulative contributions + function test_mint_for_cummulative() public { + vm.prank(addr2); + crowdfunding.contribute{value: 2 ether}(); + assertEq(rewardnft.balanceOf(addr2), 0); + + vm.prank(addr2); + crowdfunding.contribute{value: 4 ether}(); + assertEq(rewardnft.balanceOf(addr2), 1); + } + + // should not mint additional NFT + function test_should_not_mint_additional_nft() public { + vm.prank(addr2); + crowdfunding.contribute{value: 8 ether}(); + assertEq(rewardnft.balanceOf(addr2), 1); + + vm.prank(addr2); + crowdfunding.contribute{value: 10 ether}(); + assertEq(rewardnft.balanceOf(addr2), 1); + } + + // should track individual contributions + function test_track_individual_contributions() public { + vm.prank(addr2); + crowdfunding.contribute{value: 10 ether}(); + assertEq(crowdfunding.getContribution(addr2), 10 ether); + + vm.prank(addr3); + crowdfunding.contribute{value: 20 ether}(); + assertEq(crowdfunding.getContribution(addr3), 20 ether); + } + + // should track multiple contributions + function test_track_multiple_contributions() public { + vm.prank(addr2); + crowdfunding.contribute{value: 10 ether}(); + assertEq(crowdfunding.getContribution(addr2), 10 ether); + + vm.prank(addr2); + crowdfunding.contribute{value: 20 ether}(); + assertEq(crowdfunding.getContribution(addr2), 30 ether); + } + + // should track funding progress + function test_track_funding_progress() public { + vm.prank(addr2); + crowdfunding.contribute{value: 40 ether}(); + assertEq(crowdfunding.totalFundsRaised(), 40 ether); + assertEq(crowdfunding.isFundingComplete(), false); + + vm.prank(addr2); + crowdfunding.contribute{value: 10 ether}(); + assertEq(crowdfunding.totalFundsRaised(), 50 ether); + assertEq(crowdfunding.isFundingComplete(), true); + } + + // should allow owner to withdraw funds + function test_allow_owner_to_withdraw() public { + uint256 initialOwnerBalance = owner.balance; + console.log("Initial Owner Balance:", initialOwnerBalance); + assertEq(owner.balance, 0); + + vm.startPrank(addr2); + crowdfunding.contribute{value: FUNDING_GOAL}(); + vm.stopPrank(); + assertEq(addr2.balance, 50 ether); + + assertEq(crowdfunding.totalFundsRaised(), FUNDING_GOAL); + assertEq(crowdfunding.contributions(addr2), 50 ether); + // vm.prank(address(crowdfunding)); + // vm.startPrank(owner); + // console.log("Owner Address:", owner); + + // crowdfunding.withdrawFunds(); + // vm.stopPrank(); + // assertEq(owner.balance, initialOwnerBalance + FUNDING_GOAL); + } + + // should not allow withdrawal if funding goal not reached + function test_reject_withdrawal_if_funding_not_reached() public { + vm.prank(addr2); + crowdfunding.contribute{value: 20 ether}(); + assertEq(addr2.balance, 80 ether); + + vm.expectRevert("Funding goal not yet reached"); + + vm.prank(address(this)); + crowdfunding.withdrawFunds(); + } + + // should not allow non-owner to withdraw funds + function test_withdrawal_for_nonOwner() public { + vm.prank(addr2); + crowdfunding.contribute{value: 50 ether}(); + vm.expectRevert("Only project owner can withdraw"); + vm.prank(addr2); + crowdfunding.withdrawFunds(); + } + + function test_correctly_track_individual_contributions() public { + vm.prank(addr2); + crowdfunding.contribute{value: 12 ether}(); + + vm.prank(addr3); + crowdfunding.contribute{value: 12 ether}(); + + // vm.prank(addr5); + // crowdfunding.contribute{value: 12 ether}(); + + assertEq(crowdfunding.getContribution(addr2), 12 ether); + assertEq(crowdfunding.getContribution(addr3), 12 ether); + // assertEq(crowdfunding.getContribution(addr5), 12 ether); + } + + function test_contribution_amount_for_repeat_contributions() public { + vm.prank(addr2); + crowdfunding.contribute{value: 12 ether}(); + + vm.prank(addr2); + crowdfunding.contribute{value: 12 ether}(); + + assertEq(crowdfunding.getContribution(addr2), 24 ether); + } + + // validations + function test_should_reject_zero_contribution() public { + vm.expectRevert("Contribution must be greater than 0"); + vm.prank(addr2); + crowdfunding.contribute{value: 0 ether}(); + } + + function test_reject_contributions_after_funding_goal_is_reached() public { + vm.prank(addr2); + crowdfunding.contribute{value: 50 ether}(); + + vm.expectRevert("Funding goal already reached"); + vm.prank(addr3); + crowdfunding.contribute{value: 0.00001 ether}(); + crowdfunding.withdrawFunds(); + } + + function test_refund_excess_contribution() public { + // First contribute most of the funding goal + uint256 initialContribution = 45 ether; + vm.prank(addr2); + crowdfunding.contribute{value: initialContribution}(); + + // Verify initial contribution state + assertEq(crowdfunding.totalFundsRaised(), initialContribution); + + // Calculate remaining amount needed and prepare second contribution + uint256 secondContribution = 10 ether; + uint256 remainingToGoal = FUNDING_GOAL - initialContribution; // Should be 5 ether + uint256 expectedRefund = secondContribution - remainingToGoal; // Should be 5 ether + + // Record addr3's balance before contribution + uint256 addr3BalanceBefore = addr3.balance; + + // Make contribution that should trigger partial refund + vm.prank(addr3); + crowdfunding.contribute{value: secondContribution}(); + + // Verify final states + assertEq(crowdfunding.totalFundsRaised(), FUNDING_GOAL); + assertEq(crowdfunding.isFundingComplete(), true); + assertEq(crowdfunding.getContribution(addr3), remainingToGoal); + + // Verify addr3 received the correct refund + // Final balance should be: initial balance - contribution + refund + uint256 expectedBalance = addr3BalanceBefore - secondContribution + expectedRefund; + assertEq(addr3.balance, expectedBalance); + } + + // Events + // Should emit FundsWithdrawn event + function test_emit_funds_withdrawn_event() public { + // First reach the funding goal + vm.prank(addr2); + crowdfunding.contribute{value: FUNDING_GOAL}(); + + // Set up the event check + vm.expectEmit(true, true, false, true, address(crowdfunding)); + + // Emit the expected event with expected arguments + emit FundsWithdrawn(address(this), FUNDING_GOAL); + + // Withdraw the funds - using address(this) as owner + crowdfunding.withdrawFunds(); + } + // Should emit TokenRewardSent event + function test_emit_token_reward_sent_event() public { + // Calculate expected tokens based on reward rate + uint256 expectedTokens = (2 ether * REWARD_RATE) / 1 ether; + + // Set up the event check + vm.expectEmit(true, true, false, true, address(crowdfunding)); + + // Emit the expected event with expected arguments + emit TokenRewardSent(addr2, expectedTokens); + + // Make the contribution that should trigger the token reward + vm.prank(addr2); + crowdfunding.contribute{value: 2 ether}(); + } + // Should emit ContributionReceived event + function test_emit_contribution_received_event() public { + vm.expectEmit(true, true, false, true, address(crowdfunding)); + + // Emit the expected event with the expected arguments + emit ContributionReceived(addr2, 20 ether); + + // Perform the action that should emit the event + vm.prank(addr2); + crowdfunding.contribute{value: 20 ether}(); + } + + // Should emit NFTRewardSent event + function test_emit_nft_reward_sent_event() public { + // Set up the event check - we want to verify the NFTRewardSent event + vm.expectEmit(true, true, true, true, address(crowdfunding)); + + // Emit the expected event with expected arguments + emit NFTRewardSent(addr2, 0); // First NFT should have ID 0 + + // Make the contribution that should trigger the NFT reward + vm.prank(addr2); + crowdfunding.contribute{value: NFT_THRESHOLD}(); + } +} diff --git a/test/Payable.t.sol b/test/Payable.t.sol new file mode 100644 index 00000000..f77108f5 --- /dev/null +++ b/test/Payable.t.sol @@ -0,0 +1,70 @@ +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.24; + +// import {Test, console} from "forge-std/Test.sol"; +// import {Payable} from "../src/Payable.sol"; + +// contract PayableTest is Test { +// Payable public payableContract; +// address public owner; +// address public user1; +// address public user2; + +// function setUp() public { +// owner = vm.addr(1); +// user1 = vm.addr(2); +// user2 = vm.addr(3); + +// console.log("Owner: ", owner); +// console.log("User 1: ", user1); +// console.log("User 2: ", user2); + +// vm.prank(owner); +// payableContract = new Payable(); +// } + +// function test_Deposit() public { +// vm.deal(user1, 1 ether); // Give user1 some ETH +// vm.startPrank(user1); + +// payableContract.deposit{value: 0.1 ether}(0.1 ether); + +// assertEq(payableContract.getInvestment(user1), 0.1 ether); +// assertEq(payableContract.getContractBalance(), 0.1 ether); + +// vm.stopPrank(); +// } + +// function test_GetInvestment() public { +// vm.deal(user1, 1 ether); // Give user1 some ETH +// vm.startPrank(user1); + +// payableContract.deposit{value: 0.1 ether}(0.1 ether); + +// assertEq(payableContract.getInvestment(user1), 0.1 ether); + +// vm.stopPrank(); +// } + +// function test_GetContractBalance() public { +// vm.deal(user1, 1 ether); // Give user1 some ETH +// vm.startPrank(user1); + +// payableContract.deposit{value: 0.1 ether}(0.1 ether); + +// assertEq(payableContract.getContractBalance(), 0.1 ether); + +// vm.stopPrank(); +// } + +// function test_GetMyEthBalance() public { +// vm.deal(user1, 1 ether); // Give user1 some ETH +// vm.startPrank(user1); + +// payableContract.deposit{value: 0.1 ether}(0.1 ether); + +// assertEq(payableContract.getMyEthBalance(), 0.9 ether); + +// vm.stopPrank(); +// } +// } From bc75fbeb6ed816d11f223f628ba101b2557b6d78 Mon Sep 17 00:00:00 2001 From: alexohre Date: Tue, 11 Mar 2025 13:02:39 +0100 Subject: [PATCH 5/9] refac: modularize checkNftEligibilty logic Signed-off-by: alexohre --- contracts/with-foundry/Crowdfunding.sol | 84 ++++++------ test/Crowdfunding.t.sol | 169 +++++++++++++----------- 2 files changed, 137 insertions(+), 116 deletions(-) diff --git a/contracts/with-foundry/Crowdfunding.sol b/contracts/with-foundry/Crowdfunding.sol index 819df009..034f7b20 100644 --- a/contracts/with-foundry/Crowdfunding.sol +++ b/contracts/with-foundry/Crowdfunding.sol @@ -11,10 +11,6 @@ contract Crowdfunding { uint public constant NFT_THRESHOLD = 5 ether; uint256 public totalFundsRaised; bool public isFundingComplete; - // address public constant NFT_CONTRACT_ADDRESS = - // 0x2e234DAe75C793f67A35089C9d99245E1C58470b; - // address public constant TOKEN_CONTRACT_ADDRESS = - // 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; RewardToken public rewardToken; RewardNft public rewardNFT; @@ -30,28 +26,24 @@ contract Crowdfunding { event NFTRewardSent(address indexed contributor, uint256 tokenId); event FundsWithdrawn(address indexed projectOwner, uint256 amount); - constructor( - uint256 _tokenRewardRate, - address _rewardToken, - address _rewardNft - ) { + constructor(uint256 _tokenRewardRate, address _rewardToken, address _rewardNft) { Owner = msg.sender; rewardToken = RewardToken(_rewardToken); rewardNFT = RewardNft(_rewardNft); tokenRewardRate = _tokenRewardRate; } - function contribute() external payable { + function contribute() external payable returns (bool) { // console.log("Ether Value contribution___%s", msg.value); require(msg.value > 0, "Contribution must be greater than 0"); require(!isFundingComplete, "Funding goal already reached"); // Calculate contribution amount and process any refunds - uint256 contributionAmount = _calculateContributionAndRefund(msg.value); - // console.log("contributed Amount____%s", contributionAmount); + uint256 refundableAmount = _determineIfAmountIsRefundable(msg.value); + // console.log("contributed Amount____%s", refundableAmount); // Update contribution record - contributions[msg.sender] += contributionAmount; - totalFundsRaised += contributionAmount; + contributions[msg.sender] += refundableAmount; + totalFundsRaised += refundableAmount; // console.log("total funds raised____%s", totalFundsRaised); // Check if funding goal is reached @@ -63,29 +55,38 @@ contract Crowdfunding { // Calculate token reward uint256 tokenReward = calculateReward(msg.value); - console.log("token reward____%s", tokenReward); + // console.log("token reward____%s", tokenReward); if (tokenReward > 0) { - console.log("the contract caller____%s", msg.sender); + // console.log("the contract caller____%s", msg.sender); sendRewardToken(tokenReward, msg.sender); - console.log("token reward____%s", tokenReward); + // console.log("token reward____%s", tokenReward); emit TokenRewardSent(msg.sender, tokenReward); + return true; } // Check for NFT eligibility - if ( - contributions[msg.sender] >= NFT_THRESHOLD && - !hasReceivedNFT[msg.sender] - ) { - uint256 tokenId = rewardNFT.mintNFT(msg.sender); - hasReceivedNFT[msg.sender] = true; - emit NFTRewardSent(msg.sender, tokenId); - } + mintNft(msg.sender); emit ContributionReceived(msg.sender, msg.value); } - function calculateReward(uint256 _value) private returns (uint256) { + function checkNftEligibilty(address _address) private returns (bool) { + if (contributions[_address] >= NFT_THRESHOLD && !hasReceivedNFT[_address]) { + return true; + } + return false; + } + + function mintNft(address _contributor) private returns (bool) { + require(checkNftEligibilty(_contributor), "Not eligible for NFT reward"); + uint256 tokenId = rewardNFT.mintNFT(_contributor); + hasReceivedNFT[_contributor] = true; + emit NFTRewardSent(_contributor, tokenId); + return true; + } + + function calculateReward(uint256 _value) private view returns (uint256) { uint256 tokenReward = (_value * tokenRewardRate) / 1 ether; return tokenReward; @@ -96,21 +97,24 @@ contract Crowdfunding { rewardToken.transferFrom(address(this), _recipient, rewardAmount); } - function _calculateContributionAndRefund( - uint256 _contributionAmount - ) private returns (uint256) { + function _determineIfAmountIsRefundable(uint256 _contributionAmount) private returns (uint256) { // Calculate the remaining amount needed to complete the funding goal - uint256 remainingAmount = FUNDING_GOAL - totalFundsRaised; - uint256 contributionAmount = _contributionAmount; - - // If contribution exceeds remaining goal, adjust contribution and refund excess - if (_contributionAmount > remainingAmount) { - contributionAmount = remainingAmount; - uint256 refundAmount = _contributionAmount - remainingAmount; - payable(msg.sender).transfer(refundAmount); + // return refundableAmount; + uint256 amountToReachThreshold = FUNDING_GOAL - totalFundsRaised; + if (_contributionAmount > amountToReachThreshold) { + // return the excess amount + uint256 refundAmount = _contributionAmount - amountToReachThreshold; + return refundAmount; } + return 0; + } - return contributionAmount; + function transferRefundableAmount(uint256 _amount, address _contributor) private { + uint256 refundable = _determineIfAmountIsRefundable(_amount); + if (refundable > 0) { + (bool success, ) = _contributor.call{value: refundable}(""); + require(success, "Transfer failed"); + } } function withdrawFunds() external { @@ -124,9 +128,7 @@ contract Crowdfunding { emit FundsWithdrawn(Owner, amount); } - function getContribution( - address contributor - ) external view returns (uint256) { + function getContribution(address contributor) external view returns (uint256) { return contributions[contributor]; } } diff --git a/test/Crowdfunding.t.sol b/test/Crowdfunding.t.sol index b7f35d05..22f1dd61 100644 --- a/test/Crowdfunding.t.sol +++ b/test/Crowdfunding.t.sol @@ -8,25 +8,23 @@ import {RewardToken} from "../contracts/with-foundry/RewardToken.sol"; import {RewardNft} from "../contracts/with-foundry/RewardNft.sol"; contract CrowdfundingTest is Test { + // Crowdfunding contract state variables Crowdfunding public crowdfunding; RewardToken public rewardtoken; RewardNft public rewardnft; - address public nftAddr = 0x2e234DAe75C793f67A35089C9d99245E1C58470b; - address public tokenAddr = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; - // address public owner; uint public constant FUNDING_GOAL = 50 ether; uint public constant NFT_THRESHOLD = 5 ether; uint256 public totalFundsRaised; bool public isFundingComplete; uint256 constant REWARD_RATE = 100; + // Addresses for testing address crowdfundingAddr = address(this); address owner = vm.addr(1); address addr2 = vm.addr(2); address addr3 = vm.addr(3); - address addr5 = vm.addr(5); address addr4 = vm.addr(4); - // address crowdfundingAddr; + address addr5 = vm.addr(5); receive() external payable {} @@ -43,14 +41,14 @@ contract CrowdfundingTest is Test { vm.startPrank(owner); rewardtoken = new RewardToken(); - vm.stopPrank(); - vm.startPrank(owner); + // vm.stopPrank(); + // vm.startPrank(owner); rewardnft = new RewardNft("RewardNft", "RNFT", "ipfs://"); - vm.stopPrank(); + // vm.stopPrank(); // vm.startPrank(owner); console.log("rewardtoken:", address(rewardtoken)); console.log("rewardnft:", address(rewardnft)); - vm.startPrank(owner); + // vm.startPrank(owner); // Transfer Reward tokens from owner to the contract rewardtoken.transfer(address(this), 5000); @@ -60,27 +58,22 @@ contract CrowdfundingTest is Test { vm.deal(addr2, 100 ether); vm.deal(addr3, 100 ether); vm.deal(addr4, 100 ether); - // address crowdfundingAddr = address(this); + vm.deal(addr5, 100 ether); - // Set crowdfunding contract in the token contracts - // vm.startPrank(owner); - // // rewardtoken.setCrowdfundingContract(crowdfundingAddr); - // // rewardnft.setCrowdfundingContract(crowdfundingAddr); - // vm.stopPrank(); - // vm.Prank(owner); console.log("Owner_____:", owner); - console.log("Address 2____:", addr2); - console.log("Address 3:", addr3); - console.log("Contract Address:", crowdfundingAddr); + console.log("Address 2_____:", addr2); + console.log("Address 3_____:", addr3); + console.log("Address 4_____:", addr4); + console.log("Address 5_____:", addr5); + console.log("Contract Address_____:", crowdfundingAddr); // Log the addresses - console.log("Crowdfunding contract address:", address(crowdfunding)); - console.log("RewardToken contract address:", address(rewardtoken)); - console.log("RewardNFT contract address:", address(rewardnft)); - // vm.stopPrank(); + console.log("Crowdfunding contract address______:", address(crowdfunding)); + console.log("RewardToken contract address______:", address(rewardtoken)); + console.log("RewardNFT contract address______:", address(rewardnft)); } // ******DEPLOYMENT******// - // state variables at deployment + // Test state variables at deployment // Should set the correct CrowdFunding contract owner function test_setContractOwner() public view { assertEq(crowdfunding.Owner(), owner); @@ -90,6 +83,7 @@ contract CrowdfundingTest is Test { assertEq(rewardtoken.owner(), owner); } + // should transfer the correct amount of reward tokens to the crowdfunding contract function test_RewardTokenBalanceOfCrowdfundingOnDeployment() public view { uint256 contractBal1 = rewardtoken.balanceOf(crowdfundingAddr); assertEq(contractBal1, 5000); @@ -97,89 +91,113 @@ contract CrowdfundingTest is Test { uint256 ownerRewardTokenBalance = rewardtoken.balanceOf(owner); assertEq(ownerRewardTokenBalance, 0); } + // Should set the correct rewardNFT contract owner - // function test_setNFTContractOwner() public view { - // assertEq(rewardnft.owner(), owner); - // } - - // Should set the correct Nft contract Address - // function test_setNFTContractAddr() public view { - // assertEq(crowdfunding.NFT_CONTRACT_ADDRESS(), nftAddr); - // } - - // // Should set the correct Token contract Address - // function test_setTokenContractAddr() public view { - // assertEq(crowdfunding.TOKEN_CONTRACT_ADDRESS(), tokenAddr); - // } + function test_setNFTContractOwner() public view { + assertEq(rewardnft.owner(), owner); + } + // Should set the correct funding goal function test_setCorrectFundingGoal() public view { assertEq(crowdfunding.FUNDING_GOAL(), FUNDING_GOAL); } + // Should set the correct token reward rate function test_setTokenReward() public view { assertEq(crowdfunding.tokenRewardRate(), REWARD_RATE); } + // Should set the correct NFT threshold function test_set_NFT_Threshold() public view { assertEq(crowdfunding.NFT_THRESHOLD(), NFT_THRESHOLD); } + // Should determine that totalFundsRaised is zero initially function test_total_funds_raised() public view { assertEq(crowdfunding.totalFundsRaised(), 0); } + // Should set isFundingComplete to false initially function test_is_funding_complete() public view { assertEq(crowdfunding.isFundingComplete(), false); } - // Transactions - // Allows Eth contrib. - function test_Allows_eth_contribution() public { + // ********* TRANSACTIONS *********// + // Allows Eth contribution from user + function test_allow_eth_contribution() public { uint256 contributionAmount = 10 ether; - uint256 initialEthBalanceAddr2 = addr2.balance; - uint256 initialBalanceCrowdFunding = address(crowdfunding).balance; + uint256 addr2InitialEthBal = addr2.balance; // address 2 initial balance - uint256 contractBal1 = rewardtoken.balanceOf(crowdfundingAddr); - assertEq(contractBal1, 5000); + uint256 initialEthBalanceCrowdFunding = address(crowdfunding).balance; // initial balance of crowdfunding contract + console.log("Initial Eth Balance CrowdFunding_____:", initialEthBalanceCrowdFunding); + + uint256 contractRewardTokenBal = rewardtoken.balanceOf(crowdfundingAddr); + assertEq(contractRewardTokenBal, 5000); uint256 ownerRewardTokenBalance = rewardtoken.balanceOf(owner); assertEq(ownerRewardTokenBalance, 0); - assertEq(initialBalanceCrowdFunding, 0); - assertEq(initialEthBalanceAddr2, 100 ether); + assertEq(initialEthBalanceCrowdFunding, 0); + assertEq(addr2InitialEthBal, 100 ether); // Perform the contribution - vm.startPrank(addr2); - console.log("Addr2_____", addr2); - // vm.prank(addr2); + vm.prank(addr2); + // console.log("Addr2_____:", addr2); crowdfunding.contribute{value: contributionAmount}(); - // assertEq(initialBalanceCrowdFunding, 10); - vm.stopPrank(); - uint256 finalBalanceaddr2 = addr2.balance; - uint256 finalBalanceCrowdFunding = address(crowdfunding).balance; - assertEq(finalBalanceaddr2, initialEthBalanceAddr2 - contributionAmount); - assertEq(finalBalanceCrowdFunding, initialBalanceCrowdFunding + contributionAmount); + uint256 addr2EthBalAfterContr = addr2.balance; // address 2 balance after contribution + uint256 crowdfundingBalAfterContr = address(crowdfunding).balance; // crowdfunding balance after contribution + + assertEq(addr2EthBalAfterContr, addr2InitialEthBal - contributionAmount); + assertEq(crowdfundingBalAfterContr, initialEthBalanceCrowdFunding + contributionAmount); } - // determine that the token reward amount is based on contrib - function test_token_reward_amount() public { - uint256 rewardAmount = calculateTokenReward(2 ether); + // determine that the token reward amount is based on contribution + function test_calculate_token_reward_amount() public { + // first calculate the token reward for 2 ether + uint256 rewardAmount1 = calculateTokenReward(2 ether); + assertEq(rewardAmount1, 200); - vm.prank(addr2); - crowdfunding.contribute{value: 2 ether}(); + // Calculate the token reward for 5 ether + uint256 rewardAmount2 = calculateTokenReward(5 ether); + assertEq(rewardAmount2, 500); - uint256 addr2RewardTokenBalance = rewardtoken.balanceOf(addr2); - - assertEq(addr2RewardTokenBalance, rewardAmount); + // Calculate the token reward for 10 ether + uint256 rewardAmount3 = calculateTokenReward(10 ether); + assertEq(rewardAmount3, 1000); } - // mint tokens based on contrib amount - function test_mint_tokens_based_on_contribution() public { - uint256 expectedTokens = calculateTokenReward(2 ether); + // Should send the correct token reward to the contributor + function test_user_receive_accurate_token_reward_on_contribution() public { + uint256 contributionAmount = 10 ether; + uint256 addr3InitialEthBal = addr3.balance; // address 3 initial balance + uint256 addr3InitialTokenBal = rewardtoken.balanceOf(addr3); // address 3 initial token balance + console.log("Initial Token Balance Address 3_____:", addr3InitialTokenBal); + uint256 rewardAmount = calculateTokenReward(10 ether); + console.log("Reward Amount______:", rewardAmount); - vm.prank(addr2); - crowdfunding.contribute{value: 2 ether}(); - assertEq(rewardtoken.balanceOf(addr2), expectedTokens); + uint256 initialEthBalanceCrowdFunding = address(crowdfunding).balance; // initial balance of crowdfunding contract + console.log("Initial Eth Balance CrowdFunding_____:", initialEthBalanceCrowdFunding); + + uint256 contractRewardTokenBal = rewardtoken.balanceOf(crowdfundingAddr); + assertEq(contractRewardTokenBal, 5000); + + uint256 ownerRewardTokenBalance = rewardtoken.balanceOf(owner); + assertEq(ownerRewardTokenBalance, 0); + + assertEq(initialEthBalanceCrowdFunding, 0); + assertEq(addr3InitialEthBal, 100 ether); + + // Perform the contribution with address 3 + vm.prank(addr3); + crowdfunding.contribute{value: contributionAmount}(); + + uint256 addr3EthBalAfterContr = addr3.balance; // address 3 balance after contribution + uint256 addr3TokenBalAfterContr = rewardtoken.balanceOf(addr3); // address 3 initial token balance + uint256 crowdfundingBalAfterContrtion = address(crowdfunding).balance; // crowdfunding balance after contribution + + assertEq(addr3TokenBalAfterContr, rewardAmount); + assertEq(addr3EthBalAfterContr, addr3InitialEthBal - contributionAmount); + assertEq(crowdfundingBalAfterContrtion, initialEthBalanceCrowdFunding + contributionAmount); } // should not mint NFT below threshold @@ -286,7 +304,7 @@ contract CrowdfundingTest is Test { vm.expectRevert("Funding goal not yet reached"); - vm.prank(address(this)); + vm.prank(owner); crowdfunding.withdrawFunds(); } @@ -338,6 +356,8 @@ contract CrowdfundingTest is Test { vm.expectRevert("Funding goal already reached"); vm.prank(addr3); crowdfunding.contribute{value: 0.00001 ether}(); + + vm.prank(owner); crowdfunding.withdrawFunds(); } @@ -373,7 +393,7 @@ contract CrowdfundingTest is Test { assertEq(addr3.balance, expectedBalance); } - // Events + // ********* EVENTS *********// // Should emit FundsWithdrawn event function test_emit_funds_withdrawn_event() public { // First reach the funding goal @@ -383,10 +403,9 @@ contract CrowdfundingTest is Test { // Set up the event check vm.expectEmit(true, true, false, true, address(crowdfunding)); - // Emit the expected event with expected arguments - emit FundsWithdrawn(address(this), FUNDING_GOAL); + emit FundsWithdrawn(owner, FUNDING_GOAL); // emit the expected arguments - // Withdraw the funds - using address(this) as owner + vm.prank(owner); // Prank the owner to withdraw funds crowdfunding.withdrawFunds(); } // Should emit TokenRewardSent event @@ -422,7 +441,7 @@ contract CrowdfundingTest is Test { vm.expectEmit(true, true, true, true, address(crowdfunding)); // Emit the expected event with expected arguments - emit NFTRewardSent(addr2, 0); // First NFT should have ID 0 + emit NFTRewardSent(addr2, 1); // First NFT should have ID 1 // Make the contribution that should trigger the NFT reward vm.prank(addr2); From ca16528096eea26b56571a900ed02dd49e84b7ef Mon Sep 17 00:00:00 2001 From: alexohre Date: Tue, 11 Mar 2025 14:49:17 +0100 Subject: [PATCH 6/9] refac: update CrowdFunding Contract Signed-off-by: alexohre --- contracts/with-foundry/Crowdfunding.sol | 50 ++++++++++++++++++------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/contracts/with-foundry/Crowdfunding.sol b/contracts/with-foundry/Crowdfunding.sol index 034f7b20..34674a52 100644 --- a/contracts/with-foundry/Crowdfunding.sol +++ b/contracts/with-foundry/Crowdfunding.sol @@ -39,17 +39,25 @@ contract Crowdfunding { require(!isFundingComplete, "Funding goal already reached"); // Calculate contribution amount and process any refunds + uint256 refundableAmount = _determineIfAmountIsRefundable(msg.value); + + // check if refundable amount is > 0 + if (refundableAmount > 0) { + transferRefundableAmount(refundableAmount, msg.sender); + } + // console.log("contributed Amount____%s", refundableAmount); // Update contribution record - contributions[msg.sender] += refundableAmount; - totalFundsRaised += refundableAmount; - // console.log("total funds raised____%s", totalFundsRaised); + uint256 contributionsValue = msg.value - refundableAmount; + contributions[msg.sender] += contributionsValue; + console.log("E work ooooo______", contributions[msg.sender]); + totalFundsRaised += contributionsValue; + console.log("total funds raised____%s", totalFundsRaised); // Check if funding goal is reached if (totalFundsRaised >= FUNDING_GOAL) { isFundingComplete = true; - // console.log("isComplete____%s", isFundingComplete); } // Calculate token reward @@ -59,19 +67,25 @@ contract Crowdfunding { if (tokenReward > 0) { // console.log("the contract caller____%s", msg.sender); - sendRewardToken(tokenReward, msg.sender); + bool isTransfered = sendRewardToken(tokenReward, msg.sender); + require(isTransfered, "Token transfer failed"); // console.log("token reward____%s", tokenReward); - emit TokenRewardSent(msg.sender, tokenReward); - return true; - } - // Check for NFT eligibility - mintNft(msg.sender); + // Check for NFT eligibility + bool isNftTransfered = mintNft(msg.sender); + require(isNftTransfered, "NFT transfer failed"); - emit ContributionReceived(msg.sender, msg.value); + emit ContributionReceived(msg.sender, msg.value); + } else { + return false; + } } function checkNftEligibilty(address _address) private returns (bool) { + console.log("contributor Amount______:", contributions[_address]); + console.log("nft threshold___", NFT_THRESHOLD); + console.log("Has receivedNft___", !hasReceivedNFT[_address]); + if (contributions[_address] >= NFT_THRESHOLD && !hasReceivedNFT[_address]) { return true; } @@ -92,21 +106,29 @@ contract Crowdfunding { return tokenReward; } - function sendRewardToken(uint256 _amount, address _recipient) private { + function sendRewardToken(uint256 _amount, address _recipient) private returns (bool) { uint256 rewardAmount = calculateReward(_amount); rewardToken.transferFrom(address(this), _recipient, rewardAmount); + emit TokenRewardSent(msg.sender, rewardAmount); + + return true; } function _determineIfAmountIsRefundable(uint256 _contributionAmount) private returns (uint256) { // Calculate the remaining amount needed to complete the funding goal // return refundableAmount; + // console.log("contribution Amount____%s", _contributionAmount); uint256 amountToReachThreshold = FUNDING_GOAL - totalFundsRaised; - if (_contributionAmount > amountToReachThreshold) { + // console.log("amount to reach threshold____%s", amountToReachThreshold); + // console.log("funding goal____%s", FUNDING_GOAL); + if (_contributionAmount >= amountToReachThreshold) { // return the excess amount uint256 refundAmount = _contributionAmount - amountToReachThreshold; + // console.log("refundable amount____%s", refundAmount); return refundAmount; } - return 0; + // return 0; + return _contributionAmount; } function transferRefundableAmount(uint256 _amount, address _contributor) private { From fe9db07783877dc696224c2aa3e1e7be441f72bf Mon Sep 17 00:00:00 2001 From: alexohre Date: Wed, 12 Mar 2025 11:57:11 +0100 Subject: [PATCH 7/9] test: validate excess contribution refund logic Signed-off-by: alexohre --- contracts/with-foundry/Crowdfunding.sol | 60 ++++++---------- test/Crowdfunding.t.sol | 96 ++++++++++++++----------- 2 files changed, 75 insertions(+), 81 deletions(-) diff --git a/contracts/with-foundry/Crowdfunding.sol b/contracts/with-foundry/Crowdfunding.sol index 34674a52..46157e35 100644 --- a/contracts/with-foundry/Crowdfunding.sol +++ b/contracts/with-foundry/Crowdfunding.sol @@ -34,26 +34,23 @@ contract Crowdfunding { } function contribute() external payable returns (bool) { - // console.log("Ether Value contribution___%s", msg.value); require(msg.value > 0, "Contribution must be greater than 0"); require(!isFundingComplete, "Funding goal already reached"); // Calculate contribution amount and process any refunds uint256 refundableAmount = _determineIfAmountIsRefundable(msg.value); + uint256 actualContribution = msg.value - refundableAmount; // check if refundable amount is > 0 if (refundableAmount > 0) { transferRefundableAmount(refundableAmount, msg.sender); } - // console.log("contributed Amount____%s", refundableAmount); // Update contribution record - uint256 contributionsValue = msg.value - refundableAmount; - contributions[msg.sender] += contributionsValue; - console.log("E work ooooo______", contributions[msg.sender]); - totalFundsRaised += contributionsValue; - console.log("total funds raised____%s", totalFundsRaised); + // uint256 contributionsValue = msg.value - refundableAmount; + contributions[msg.sender] += actualContribution; + totalFundsRaised += actualContribution; // Check if funding goal is reached if (totalFundsRaised >= FUNDING_GOAL) { @@ -61,39 +58,30 @@ contract Crowdfunding { } // Calculate token reward - uint256 tokenReward = calculateReward(msg.value); - - // console.log("token reward____%s", tokenReward); + uint256 tokenReward = calculateReward(actualContribution); if (tokenReward > 0) { - // console.log("the contract caller____%s", msg.sender); bool isTransfered = sendRewardToken(tokenReward, msg.sender); require(isTransfered, "Token transfer failed"); - // console.log("token reward____%s", tokenReward); // Check for NFT eligibility - bool isNftTransfered = mintNft(msg.sender); - require(isNftTransfered, "NFT transfer failed"); + if (checkNftEligibility(msg.sender)) { + mintNft(msg.sender); + } - emit ContributionReceived(msg.sender, msg.value); + emit ContributionReceived(msg.sender, actualContribution); + return true; } else { return false; } } - function checkNftEligibilty(address _address) private returns (bool) { - console.log("contributor Amount______:", contributions[_address]); - console.log("nft threshold___", NFT_THRESHOLD); - console.log("Has receivedNft___", !hasReceivedNFT[_address]); - - if (contributions[_address] >= NFT_THRESHOLD && !hasReceivedNFT[_address]) { - return true; - } - return false; + function checkNftEligibility(address _address) private view returns (bool) { + return contributions[_address] >= NFT_THRESHOLD && !hasReceivedNFT[_address]; } function mintNft(address _contributor) private returns (bool) { - require(checkNftEligibilty(_contributor), "Not eligible for NFT reward"); + // require(checkNftEligibilty(_contributor), "Not eligible for NFT reward"); uint256 tokenId = rewardNFT.mintNFT(_contributor); hasReceivedNFT[_contributor] = true; emit NFTRewardSent(_contributor, tokenId); @@ -102,37 +90,31 @@ contract Crowdfunding { function calculateReward(uint256 _value) private view returns (uint256) { uint256 tokenReward = (_value * tokenRewardRate) / 1 ether; - return tokenReward; } - function sendRewardToken(uint256 _amount, address _recipient) private returns (bool) { - uint256 rewardAmount = calculateReward(_amount); - rewardToken.transferFrom(address(this), _recipient, rewardAmount); - emit TokenRewardSent(msg.sender, rewardAmount); + function sendRewardToken(uint256 _tokenReward, address _recipient) private returns (bool) { + bool success = rewardToken.transfer(_recipient, _tokenReward); + require(success, "Token transfer failed"); + emit TokenRewardSent(msg.sender, _tokenReward); return true; } - function _determineIfAmountIsRefundable(uint256 _contributionAmount) private returns (uint256) { + function _determineIfAmountIsRefundable(uint256 _contributionAmount) private view returns (uint256) { // Calculate the remaining amount needed to complete the funding goal - // return refundableAmount; - // console.log("contribution Amount____%s", _contributionAmount); uint256 amountToReachThreshold = FUNDING_GOAL - totalFundsRaised; - // console.log("amount to reach threshold____%s", amountToReachThreshold); - // console.log("funding goal____%s", FUNDING_GOAL); if (_contributionAmount >= amountToReachThreshold) { // return the excess amount uint256 refundAmount = _contributionAmount - amountToReachThreshold; - // console.log("refundable amount____%s", refundAmount); return refundAmount; } - // return 0; - return _contributionAmount; + return 0; } function transferRefundableAmount(uint256 _amount, address _contributor) private { - uint256 refundable = _determineIfAmountIsRefundable(_amount); + // uint256 refundable = _determineIfAmountIsRefundable(_amount); + uint256 refundable = _amount; if (refundable > 0) { (bool success, ) = _contributor.call{value: refundable}(""); require(success, "Transfer failed"); diff --git a/test/Crowdfunding.t.sol b/test/Crowdfunding.t.sol index 22f1dd61..c1a02f43 100644 --- a/test/Crowdfunding.t.sol +++ b/test/Crowdfunding.t.sol @@ -41,35 +41,18 @@ contract CrowdfundingTest is Test { vm.startPrank(owner); rewardtoken = new RewardToken(); - // vm.stopPrank(); - // vm.startPrank(owner); + rewardnft = new RewardNft("RewardNft", "RNFT", "ipfs://"); - // vm.stopPrank(); - // vm.startPrank(owner); - console.log("rewardtoken:", address(rewardtoken)); - console.log("rewardnft:", address(rewardnft)); - // vm.startPrank(owner); - // Transfer Reward tokens from owner to the contract - rewardtoken.transfer(address(this), 5000); crowdfunding = new Crowdfunding(REWARD_RATE, address(rewardtoken), address(rewardnft)); + // Transfer Reward tokens from owner to the contract + rewardtoken.transfer(address(crowdfunding), 5000); vm.stopPrank(); vm.deal(addr2, 100 ether); vm.deal(addr3, 100 ether); vm.deal(addr4, 100 ether); vm.deal(addr5, 100 ether); - - console.log("Owner_____:", owner); - console.log("Address 2_____:", addr2); - console.log("Address 3_____:", addr3); - console.log("Address 4_____:", addr4); - console.log("Address 5_____:", addr5); - console.log("Contract Address_____:", crowdfundingAddr); - // Log the addresses - console.log("Crowdfunding contract address______:", address(crowdfunding)); - console.log("RewardToken contract address______:", address(rewardtoken)); - console.log("RewardNFT contract address______:", address(rewardnft)); } // ******DEPLOYMENT******// @@ -85,7 +68,7 @@ contract CrowdfundingTest is Test { // should transfer the correct amount of reward tokens to the crowdfunding contract function test_RewardTokenBalanceOfCrowdfundingOnDeployment() public view { - uint256 contractBal1 = rewardtoken.balanceOf(crowdfundingAddr); + uint256 contractBal1 = rewardtoken.balanceOf(address(crowdfunding)); assertEq(contractBal1, 5000); uint256 ownerRewardTokenBalance = rewardtoken.balanceOf(owner); @@ -129,9 +112,8 @@ contract CrowdfundingTest is Test { uint256 addr2InitialEthBal = addr2.balance; // address 2 initial balance uint256 initialEthBalanceCrowdFunding = address(crowdfunding).balance; // initial balance of crowdfunding contract - console.log("Initial Eth Balance CrowdFunding_____:", initialEthBalanceCrowdFunding); - uint256 contractRewardTokenBal = rewardtoken.balanceOf(crowdfundingAddr); + uint256 contractRewardTokenBal = rewardtoken.balanceOf(address(crowdfunding)); assertEq(contractRewardTokenBal, 5000); uint256 ownerRewardTokenBalance = rewardtoken.balanceOf(owner); @@ -141,7 +123,6 @@ contract CrowdfundingTest is Test { assertEq(addr2InitialEthBal, 100 ether); // Perform the contribution vm.prank(addr2); - // console.log("Addr2_____:", addr2); crowdfunding.contribute{value: contributionAmount}(); uint256 addr2EthBalAfterContr = addr2.balance; // address 2 balance after contribution @@ -152,7 +133,7 @@ contract CrowdfundingTest is Test { } // determine that the token reward amount is based on contribution - function test_calculate_token_reward_amount() public { + function test_calculate_token_reward_amount() public view { // first calculate the token reward for 2 ether uint256 rewardAmount1 = calculateTokenReward(2 ether); assertEq(rewardAmount1, 200); @@ -168,17 +149,46 @@ contract CrowdfundingTest is Test { // Should send the correct token reward to the contributor function test_user_receive_accurate_token_reward_on_contribution() public { - uint256 contributionAmount = 10 ether; + uint256 contributionAmount = 4 ether; + uint256 addr3InitialEthBal = addr3.balance; // address 3 initial balance + uint256 addr3InitialTokenBal = rewardtoken.balanceOf(addr3); // address 3 initial token balance + uint256 rewardAmount = calculateTokenReward(4 ether); + + uint256 initialEthBalanceCrowdFunding = address(crowdfunding).balance; // initial balance of crowdfunding contract + + uint256 contractRewardTokenBal = rewardtoken.balanceOf(address(crowdfunding)); + assertEq(contractRewardTokenBal, 5000); + + uint256 ownerRewardTokenBalance = rewardtoken.balanceOf(owner); + assertEq(ownerRewardTokenBalance, 0); + + assertEq(initialEthBalanceCrowdFunding, 0); + assertEq(addr3InitialEthBal, 100 ether); + + // Perform the contribution with address 3 + vm.prank(addr3); + crowdfunding.contribute{value: contributionAmount}(); + + uint256 addr3EthBalAfterContr = addr3.balance; // address 3 balance after contribution + uint256 addr3TokenBalAfterContr = rewardtoken.balanceOf(addr3); // address 3 initial token balance + uint256 crowdfundingBalAfterContrtion = address(crowdfunding).balance; // crowdfunding balance after contribution + + assertEq(addr3TokenBalAfterContr, rewardAmount); + assertEq(addr3EthBalAfterContr, addr3InitialEthBal - contributionAmount); + assertEq(crowdfundingBalAfterContrtion, initialEthBalanceCrowdFunding + contributionAmount); + } + + // Should send the correct token reward and nft to the contributor + function test_user_receive_accurate_token_reward_and_nft_on_contribution() public { + uint256 contributionAmount = 7 ether; uint256 addr3InitialEthBal = addr3.balance; // address 3 initial balance uint256 addr3InitialTokenBal = rewardtoken.balanceOf(addr3); // address 3 initial token balance - console.log("Initial Token Balance Address 3_____:", addr3InitialTokenBal); - uint256 rewardAmount = calculateTokenReward(10 ether); - console.log("Reward Amount______:", rewardAmount); + uint256 addr3InitialNftBal = rewardnft.balanceOf(addr3); // address 3 initial token balance + uint256 rewardAmount = calculateTokenReward(7 ether); uint256 initialEthBalanceCrowdFunding = address(crowdfunding).balance; // initial balance of crowdfunding contract - console.log("Initial Eth Balance CrowdFunding_____:", initialEthBalanceCrowdFunding); - uint256 contractRewardTokenBal = rewardtoken.balanceOf(crowdfundingAddr); + uint256 contractRewardTokenBal = rewardtoken.balanceOf(address(crowdfunding)); assertEq(contractRewardTokenBal, 5000); uint256 ownerRewardTokenBalance = rewardtoken.balanceOf(owner); @@ -193,9 +203,11 @@ contract CrowdfundingTest is Test { uint256 addr3EthBalAfterContr = addr3.balance; // address 3 balance after contribution uint256 addr3TokenBalAfterContr = rewardtoken.balanceOf(addr3); // address 3 initial token balance + uint256 addr3NftBalAfterContr = rewardnft.balanceOf(addr3); // address 3 initial token balance uint256 crowdfundingBalAfterContrtion = address(crowdfunding).balance; // crowdfunding balance after contribution assertEq(addr3TokenBalAfterContr, rewardAmount); + assertEq(addr3NftBalAfterContr, 1); assertEq(addr3EthBalAfterContr, addr3InitialEthBal - contributionAmount); assertEq(crowdfundingBalAfterContrtion, initialEthBalanceCrowdFunding + contributionAmount); } @@ -277,23 +289,18 @@ contract CrowdfundingTest is Test { // should allow owner to withdraw funds function test_allow_owner_to_withdraw() public { uint256 initialOwnerBalance = owner.balance; - console.log("Initial Owner Balance:", initialOwnerBalance); assertEq(owner.balance, 0); - vm.startPrank(addr2); + vm.prank(addr2); crowdfunding.contribute{value: FUNDING_GOAL}(); - vm.stopPrank(); assertEq(addr2.balance, 50 ether); assertEq(crowdfunding.totalFundsRaised(), FUNDING_GOAL); assertEq(crowdfunding.contributions(addr2), 50 ether); - // vm.prank(address(crowdfunding)); - // vm.startPrank(owner); - // console.log("Owner Address:", owner); - // crowdfunding.withdrawFunds(); - // vm.stopPrank(); - // assertEq(owner.balance, initialOwnerBalance + FUNDING_GOAL); + vm.prank(owner); + crowdfunding.withdrawFunds(); + assertEq(owner.balance, initialOwnerBalance + FUNDING_GOAL); } // should not allow withdrawal if funding goal not reached @@ -361,6 +368,7 @@ contract CrowdfundingTest is Test { crowdfunding.withdrawFunds(); } + // should refund excess contribution to the contributor function test_refund_excess_contribution() public { // First contribute most of the funding goal uint256 initialContribution = 45 ether; @@ -375,10 +383,11 @@ contract CrowdfundingTest is Test { uint256 remainingToGoal = FUNDING_GOAL - initialContribution; // Should be 5 ether uint256 expectedRefund = secondContribution - remainingToGoal; // Should be 5 ether - // Record addr3's balance before contribution + // Ascertain that addr3's balance is unchanged uint256 addr3BalanceBefore = addr3.balance; + assertEq(addr3BalanceBefore, 100 ether); - // Make contribution that should trigger partial refund + // Make contribution that should trigger refund vm.prank(addr3); crowdfunding.contribute{value: secondContribution}(); @@ -386,6 +395,9 @@ contract CrowdfundingTest is Test { assertEq(crowdfunding.totalFundsRaised(), FUNDING_GOAL); assertEq(crowdfunding.isFundingComplete(), true); assertEq(crowdfunding.getContribution(addr3), remainingToGoal); + uint256 crowdfundinBal2 = address(crowdfunding).balance; + + assertEq(crowdfundinBal2, FUNDING_GOAL); // Verify addr3 received the correct refund // Final balance should be: initial balance - contribution + refund From c8f07cdfe5bfe39364a7283ab00dffd42fe0834e Mon Sep 17 00:00:00 2001 From: FRANKHOOD Date: Wed, 12 Mar 2025 21:11:52 +0100 Subject: [PATCH 8/9] feat: add receive and fallback functions with their respective tests --- contracts/with-foundry/Crowdfunding.sol | 10 +++++++++- contracts/with-foundry/RewardToken.sol | 8 -------- test/Crowdfunding.t.sol | 17 ++++++++++++++++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/contracts/with-foundry/Crowdfunding.sol b/contracts/with-foundry/Crowdfunding.sol index 46157e35..9afea2bb 100644 --- a/contracts/with-foundry/Crowdfunding.sol +++ b/contracts/with-foundry/Crowdfunding.sol @@ -33,7 +33,7 @@ contract Crowdfunding { tokenRewardRate = _tokenRewardRate; } - function contribute() external payable returns (bool) { + function contribute() public payable returns (bool) { require(msg.value > 0, "Contribution must be greater than 0"); require(!isFundingComplete, "Funding goal already reached"); @@ -135,4 +135,12 @@ contract Crowdfunding { function getContribution(address contributor) external view returns (uint256) { return contributions[contributor]; } + + receive() external payable{ + contribute(); + } + + fallback() external payable{ + contribute(); + } } diff --git a/contracts/with-foundry/RewardToken.sol b/contracts/with-foundry/RewardToken.sol index c4502036..dc784a59 100644 --- a/contracts/with-foundry/RewardToken.sol +++ b/contracts/with-foundry/RewardToken.sol @@ -54,14 +54,6 @@ contract RewardToken { return _balances[account]; } - // function mintReward(address to, uint256 amount) external { - // require( - // msg.sender == crowdfundingContract, - // "Only crowdfunding contract can mint" - // ); - // _mint(to, amount); - // } - function mintAllToOwner() private onlyOwner { // _mint(owner, _totalSupply); _mint(owner, 5000); diff --git a/test/Crowdfunding.t.sol b/test/Crowdfunding.t.sol index c1a02f43..7cc1de1d 100644 --- a/test/Crowdfunding.t.sol +++ b/test/Crowdfunding.t.sol @@ -19,7 +19,6 @@ contract CrowdfundingTest is Test { uint256 constant REWARD_RATE = 100; // Addresses for testing - address crowdfundingAddr = address(this); address owner = vm.addr(1); address addr2 = vm.addr(2); address addr3 = vm.addr(3); @@ -459,4 +458,20 @@ contract CrowdfundingTest is Test { vm.prank(addr2); crowdfunding.contribute{value: NFT_THRESHOLD}(); } + + function test_receive() external { + vm.prank(addr2); + (bool success, ) = (address(crowdfunding)).call{value: 2 ether}(""); + require(success, "revert transfer"); + + assertEq(crowdfunding.getContribution(addr2), 2 ether); + } + + function test_fallback() external { + vm.prank(addr2); + (bool success, bytes memory hello) = (address(crowdfunding)).call{value: 2 ether}(abi.encode("hello solidity")); + require(success, "revert transfer"); + + assertEq(crowdfunding.getContribution(addr2), 2 ether); + } } From 4e44291745f5928e5664faa649475878ccdd121d Mon Sep 17 00:00:00 2001 From: FRANKHOOD Date: Wed, 12 Mar 2025 21:25:37 +0100 Subject: [PATCH 9/9] feat: add receive and fallback functions with their respective tests --- test/Crowdfunding.t.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/Crowdfunding.t.sol b/test/Crowdfunding.t.sol index 7cc1de1d..05b4f25b 100644 --- a/test/Crowdfunding.t.sol +++ b/test/Crowdfunding.t.sol @@ -465,6 +465,9 @@ contract CrowdfundingTest is Test { require(success, "revert transfer"); assertEq(crowdfunding.getContribution(addr2), 2 ether); + + //Assert that other addresses do not receive the ether + assertEq(crowdfunding.getContribution(addr3), 0 ether); } function test_fallback() external { @@ -473,5 +476,8 @@ contract CrowdfundingTest is Test { require(success, "revert transfer"); assertEq(crowdfunding.getContribution(addr2), 2 ether); + + //Assert that other addresses do not receive the ether + assertEq(crowdfunding.getContribution(addr3), 0 ether); } }