-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add wallet contract #2314
base: v1.6-dev-ugly
Are you sure you want to change the base?
Changes from all commits
7315c91
2e16647
08cb2e5
e9b8601
c265209
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"extends": "airbnb-base", | ||
"rules": { | ||
"no-plusplus": 0, | ||
"eol-last": [ | ||
"error", | ||
"always" | ||
], | ||
"class-methods-use-this": "off", | ||
"curly": [ | ||
"error", | ||
"all" | ||
] | ||
}, | ||
"globals": { | ||
"BigInt": true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
require: test/bootstrap.js | ||
recursive: true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "wallet-contract" | ||
description = "Wallet data contract schema and tools" | ||
version = "1.5.0" | ||
edition = "2021" | ||
rust-version.workspace = true | ||
license = "MIT" | ||
|
||
[dependencies] | ||
thiserror = "1.0.64" | ||
platform-version = { path = "../rs-platform-version" } | ||
serde_json = { version = "1.0" } | ||
platform-value = { path = "../rs-platform-value" } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2019 Dash Core Group, Inc. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the "Software"), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
the Software, and to permit persons to whom the Software is furnished to do so, | ||
subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Wallet Contract | ||
|
||
[![Build Status](https://github.com/dashpay/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashpay/platform/actions/workflows/release.yml) | ||
[![NPM version](https://img.shields.io/npm/v/@dashevo/wallet-contract.svg?style=flat-square)](https://npmjs.org/package/@dashevo/wallet-contract) | ||
|
||
JSON Contracts for Dash Wallet apps | ||
|
||
## Table of Contents | ||
|
||
- [Install](#install) | ||
- [Usage](#usage) | ||
- [Contributing](#contributing) | ||
- [License](#license) | ||
|
||
## Install | ||
|
||
```sh | ||
npm install @dashevo/wallet-contract | ||
``` | ||
Comment on lines
+17
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Package installation command needs to be updated The package
🔗 Analysis chainVerify package installation command The installation command references a package that might not be published yet. Let me check the package.json to understand if this is a local package that needs to be published. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify if the package exists on npm registry
# Test: Check if the package exists and get its details
npm view @dashevo/wallet-contract --json || echo "Package not found"
Length of output: 1022 Script: #!/bin/bash
# Check package.json in the wallet-contract directory
cat packages/wallet-contract/package.json
Length of output: 779 |
||
|
||
## Usage | ||
|
||
```sh | ||
# TODO ... | ||
``` | ||
Comment on lines
+21
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Replace TODO with comprehensive documentation Based on the contract's purposes, please replace the TODO with detailed documentation covering: -## Usage
-
-```sh
-# TODO ...
-```
+## Usage
+
+### Transaction Metadata Storage
+
+```javascript
+// Example: Storing encrypted transaction metadata
+const metadata = {
+ txId: "your-tx-id",
+ encryptedData: "encrypted-payload"
+};
+
+await walletContract.storeTransactionMetadata(metadata);
+
+// Example: Retrieving transaction metadata
+const storedMetadata = await walletContract.getTransactionMetadata(txId);
+```
+
+### Username Request Management
+
+```javascript
+// Example: Storing username request information
+const requestInfo = {
+ username: "alice",
+ normalizedLabel: "alice",
+ parentDomain: "dash",
+ socialMediaUrl: "https://twitter.com/alice/status/123"
+};
+
+await walletContract.storeUsernameRequest(requestInfo);
+
+// Example: Retrieving username requests for voting
+const requests = await walletContract.getUsernameRequests();
+```
+
+### Error Handling
+
+```javascript
+try {
+ await walletContract.storeTransactionMetadata(metadata);
+} catch (error) {
+ if (error.code === 'INVALID_METADATA') {
+ // Handle validation errors
+ }
+ // Handle other errors
+}
|
||
|
||
## Contributing | ||
|
||
Feel free to dive in! [Open an issue](https://github.com/dashpay/platform/issues/new/choose) or submit PRs. | ||
|
||
## License | ||
|
||
[MIT](LICENSE) © Dash Core Group, Inc. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
module.exports = { | ||
ownerId: '11111111111111111111111111111111', | ||
contractId: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec' | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"name": "@dashevo/wallet-contract", | ||
"version": "1.5.0", | ||
"description": "A contract and helper scripts for Wallet DApp", | ||
"scripts": { | ||
"lint": "eslint .", | ||
"test": "yarn run test:unit", | ||
"test:unit": "mocha 'test/unit/**/*.spec.js'" | ||
}, | ||
"contributors": [ | ||
{ | ||
"name": "Eric Britten", | ||
"email": "[email protected]", | ||
"url": "https://github.com/hashengineering" | ||
} | ||
], | ||
"license": "MIT", | ||
"devDependencies": { | ||
"@dashevo/wasm-dpp": "workspace:*", | ||
"chai": "^4.3.10", | ||
"dirty-chai": "^2.0.1", | ||
"eslint": "^8.53.0", | ||
"eslint-config-airbnb-base": "^15.0.0", | ||
"eslint-plugin-import": "^2.29.0", | ||
"mocha": "^10.2.0", | ||
"sinon": "^17.0.1", | ||
"sinon-chai": "^3.7.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,125 @@ | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"txMetadata": { | ||||||||||||||||||||||||||||||||||
"type": "object", | ||||||||||||||||||||||||||||||||||
"indices": [ | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"name": "ownerId", | ||||||||||||||||||||||||||||||||||
"properties": [ | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"$ownerId": "asc" | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"name": "ownerIdAndCreatedAt", | ||||||||||||||||||||||||||||||||||
"properties": [ | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"$ownerId": "asc" | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"$createdAt": "asc" | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
], | ||||||||||||||||||||||||||||||||||
"properties": { | ||||||||||||||||||||||||||||||||||
"keyIndex": { | ||||||||||||||||||||||||||||||||||
"type": "integer", | ||||||||||||||||||||||||||||||||||
"minimum": 0, | ||||||||||||||||||||||||||||||||||
"description": "The index of the owners identity public key used to derive the encryption key.", | ||||||||||||||||||||||||||||||||||
"position": 0 | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
Comment on lines
+26
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add maximum bounds for key indices. The Apply this diff: "keyIndex": {
"type": "integer",
"minimum": 0,
+ "maximum": 2147483647,
"description": "The index of the owners identity public key used to derive the encryption key.",
"position": 0
},
"encryptionKeyIndex": {
"type": "integer",
"minimum": 0,
+ "maximum": 2147483647,
"description": "The secondary index used to derive the encryption key that is used to encrypt and decrypt encryptedData.",
"position": 1
}, Also applies to: 32-37 |
||||||||||||||||||||||||||||||||||
"encryptionKeyIndex": { | ||||||||||||||||||||||||||||||||||
"type": "integer", | ||||||||||||||||||||||||||||||||||
"minimum": 0, | ||||||||||||||||||||||||||||||||||
"description": "The secondary index used to derive the encryption key that is used to encrypt and decrypt encryptedData.", | ||||||||||||||||||||||||||||||||||
"position": 1 | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
"encryptedMetadata": { | ||||||||||||||||||||||||||||||||||
"type": "array", | ||||||||||||||||||||||||||||||||||
"byteArray": true, | ||||||||||||||||||||||||||||||||||
"minItems": 32, | ||||||||||||||||||||||||||||||||||
"maxItems": 4096, | ||||||||||||||||||||||||||||||||||
"description": "encrypted metadata using AES-CBC-256", | ||||||||||||||||||||||||||||||||||
"position": 2 | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
"required": [ | ||||||||||||||||||||||||||||||||||
"keyIndex", | ||||||||||||||||||||||||||||||||||
"encryptionKeyIndex", | ||||||||||||||||||||||||||||||||||
"encryptedMetadata", | ||||||||||||||||||||||||||||||||||
"$createdAt" | ||||||||||||||||||||||||||||||||||
], | ||||||||||||||||||||||||||||||||||
"additionalProperties": false | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
"identityVerify": { | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is it for? How is it related to the wallet? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the DashPay app, when a user requests a contested username, we allow the user to specify a social media post to "prove" an identity. If two people wanted Masternode owners using DashPay can view this see this URL in the username request details page of the Username Voting screen. This contact might not be the best place for it. If it isn't, I am not sure if it should be in a new contract or the dashpay contract. |
||||||||||||||||||||||||||||||||||
"documentsMutable": true, | ||||||||||||||||||||||||||||||||||
"canBeDeleted": true, | ||||||||||||||||||||||||||||||||||
"type": "object", | ||||||||||||||||||||||||||||||||||
"properties": { | ||||||||||||||||||||||||||||||||||
"normalizedLabel": { | ||||||||||||||||||||||||||||||||||
"position": 0, | ||||||||||||||||||||||||||||||||||
"type": "string", | ||||||||||||||||||||||||||||||||||
"pattern": "^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-]{0,61}[a-hj-km-np-z0-9]$", | ||||||||||||||||||||||||||||||||||
"maxLength": 63, | ||||||||||||||||||||||||||||||||||
"description": "Domain label converted to lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'b0b'", | ||||||||||||||||||||||||||||||||||
"$comment": "Must match a domain document to provide further information. Must be equal to the label in lowercase. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\"." | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
"normalizedParentDomainName": { | ||||||||||||||||||||||||||||||||||
"type": "string", | ||||||||||||||||||||||||||||||||||
"pattern": "^$|^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-\\.]{0,61}[a-hj-km-np-z0-9]$", | ||||||||||||||||||||||||||||||||||
"minLength": 0, | ||||||||||||||||||||||||||||||||||
"maxLength": 63, | ||||||||||||||||||||||||||||||||||
"position": 1, | ||||||||||||||||||||||||||||||||||
"description": "A parent domain name in lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'dash'", | ||||||||||||||||||||||||||||||||||
"$comment": "Must either be equal to an existing domain or empty to create a top level domain. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\". Only the data contract owner can create top level domains." | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
"url": { | ||||||||||||||||||||||||||||||||||
"position": 2, | ||||||||||||||||||||||||||||||||||
"type": "string", | ||||||||||||||||||||||||||||||||||
"description": "The identity verification URL to be stored.", | ||||||||||||||||||||||||||||||||||
"maxLength": 128, | ||||||||||||||||||||||||||||||||||
"pattern": "^https?://.*", | ||||||||||||||||||||||||||||||||||
"format": "uri" | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Comment on lines
+77
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enforce HTTPS-only URLs for security. The current URL pattern allows both HTTP and HTTPS protocols. For security reasons, especially when dealing with identity verification, only HTTPS URLs should be allowed. Apply this diff: "url": {
"position": 2,
"type": "string",
"description": "The identity verification URL to be stored.",
"maxLength": 128,
- "pattern": "^https?://.*",
+ "pattern": "^https://.*",
"format": "uri"
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
"indices": [ | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"name": "ownerId", | ||||||||||||||||||||||||||||||||||
"properties": [ | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"$ownerId": "asc" | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"name": "ownerId_NormDomainName_NormLabel", | ||||||||||||||||||||||||||||||||||
"properties": [ | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"$ownerId": "asc" | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"normalizedParentDomainName": "asc" | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"normalizedLabel": "asc" | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"name": "uniqueUsernameIndex", | ||||||||||||||||||||||||||||||||||
"properties": [ | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
"normalizedLabel": "asc" | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
], | ||||||||||||||||||||||||||||||||||
"required": [ | ||||||||||||||||||||||||||||||||||
"url", | ||||||||||||||||||||||||||||||||||
"normalizedLabel", | ||||||||||||||||||||||||||||||||||
"normalizedParentDomainName" | ||||||||||||||||||||||||||||||||||
], | ||||||||||||||||||||||||||||||||||
"additionalProperties": false | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
use platform_version::version::FeatureVersion; | ||
|
||
#[derive(thiserror::Error, Debug)] | ||
pub enum Error { | ||
/// Platform expected some specific versions | ||
#[error("platform unknown version on {method}, received: {received}")] | ||
UnknownVersionMismatch { | ||
/// method | ||
method: String, | ||
/// the allowed versions for this method | ||
known_versions: Vec<FeatureVersion>, | ||
/// requested core height | ||
received: FeatureVersion, | ||
}, | ||
#[error("schema deserialize error: {0}")] | ||
InvalidSchemaJson(#[from] serde_json::Error), | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
mod error; | ||
pub mod v1; | ||
|
||
pub use crate::error::Error; | ||
use platform_value::{Identifier, IdentifierBytes32}; | ||
use platform_version::version::PlatformVersion; | ||
use serde_json::Value; | ||
|
||
pub const ID_BYTES: [u8; 32] = [ | ||
162, 48, 73, 255, 116, 241, 166, 155, 131, 121, 132, 39, 129, 40, 127, 6, 103, 164, 72, 139, | ||
143, 116, 163, 19, 81, 193, 38, 248, 116, 244, 59, 196 | ||
]; | ||
|
||
pub const OWNER_ID_BYTES: [u8; 32] = [0; 32]; | ||
|
||
pub const ID: Identifier = Identifier(IdentifierBytes32(ID_BYTES)); | ||
pub const OWNER_ID: Identifier = Identifier(IdentifierBytes32(OWNER_ID_BYTES)); | ||
pub fn load_definitions(platform_version: &PlatformVersion) -> Result<Option<Value>, Error> { | ||
match platform_version.system_data_contracts.withdrawals { | ||
1 => Ok(None), | ||
version => Err(Error::UnknownVersionMismatch { | ||
method: "wallet_contract::load_definitions".to_string(), | ||
known_versions: vec![1], | ||
received: version, | ||
}), | ||
} | ||
} | ||
pub fn load_documents_schemas(platform_version: &PlatformVersion) -> Result<Value, Error> { | ||
match platform_version.system_data_contracts.withdrawals { | ||
1 => v1::load_documents_schemas(), | ||
version => Err(Error::UnknownVersionMismatch { | ||
method: "wallet_contract::load_documents_schemas".to_string(), | ||
known_versions: vec![1], | ||
received: version, | ||
}), | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
use crate::Error; | ||
use serde_json::Value; | ||
|
||
pub mod document_types { | ||
pub mod tx_metadata { | ||
pub const NAME: &str = "tx_metadata"; | ||
|
||
pub mod properties { | ||
pub const KEY_INDEX: &str = "keyIndex"; | ||
pub const ENCRYPTION_KEY_INDEX: &str = "encryptionKeyIndex"; | ||
pub const ENCRYPTED_METADATA: &str = "encryptedMetadata"; | ||
} | ||
} | ||
} | ||
|
||
pub fn load_documents_schemas() -> Result<Value, Error> { | ||
serde_json::from_str(include_str!("../../schema/v1/wallet-contract-documents.json")) | ||
.map_err(Error::InvalidSchemaJson) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"env": { | ||
"node": true, | ||
"mocha": true | ||
}, | ||
"rules": { | ||
"import/no-extraneous-dependencies": "off" | ||
}, | ||
"globals": { | ||
"expect": true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,30 @@ | ||||||||||||||||
const sinon = require('sinon'); | ||||||||||||||||
const sinonChai = require('sinon-chai'); | ||||||||||||||||
|
||||||||||||||||
const { expect, use } = require('chai'); | ||||||||||||||||
const dirtyChai = require('dirty-chai'); | ||||||||||||||||
|
||||||||||||||||
const { | ||||||||||||||||
default: loadWasmDpp, | ||||||||||||||||
} = require('@dashevo/wasm-dpp'); | ||||||||||||||||
|
||||||||||||||||
use(dirtyChai); | ||||||||||||||||
use(sinonChai); | ||||||||||||||||
|
||||||||||||||||
exports.mochaHooks = { | ||||||||||||||||
beforeAll: loadWasmDpp, | ||||||||||||||||
|
||||||||||||||||
Comment on lines
+14
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Make beforeAll hook async to handle WebAssembly loading. The beforeAll hook should be async to properly handle the WebAssembly loading promise. exports.mochaHooks = {
- beforeAll: loadWasmDpp,
+ beforeAll: async function() {
+ await loadWasmDpp();
+ }, 📝 Committable suggestion
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @shumkov, thank you for pointing that out. Since (^.^)? ✏️ Learnings added
|
||||||||||||||||
beforeEach() { | ||||||||||||||||
if (!this.sinon) { | ||||||||||||||||
this.sinon = sinon.createSandbox(); | ||||||||||||||||
} else { | ||||||||||||||||
this.sinon.restore(); | ||||||||||||||||
} | ||||||||||||||||
}, | ||||||||||||||||
|
||||||||||||||||
afterEach() { | ||||||||||||||||
this.sinon.restore(); | ||||||||||||||||
}, | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
global.expect = expect; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance the package description
The current description is too brief. Consider expanding it to better reflect the contract's key functionalities:
📝 Committable suggestion