Skip to content
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: implement ICP verfiable Credential with Dacade as relying party #1284

Merged
merged 23 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3eb9e96
feat: implement ICP verfiable Credential with Dacade as relying party
Jonath-z Aug 7, 2024
d827bff
chore: upgrade node version for verifiable credential SDK to work
Jonath-z Aug 7, 2024
e097bda
chore: change moduleResolution to bundler for the verifiable credenti…
Jonath-z Aug 7, 2024
d0f58e7
feat: add check to ensure the feature only for ICP achievement
Jonath-z Aug 7, 2024
7edf22b
feat: add local testing canisters
Jonath-z Aug 27, 2024
79842a9
fix: make sure the user authenticate first
Jonath-z Aug 27, 2024
afec598
chore: update the verifiable credential SDK to fix the build error
Jonath-z Sep 9, 2024
0da6c3f
feat: remove unused variable
Jonath-z Sep 10, 2024
3f9230d
feat: add production issuer canister and icp identity provider
Jonath-z Oct 24, 2024
caae4c5
feat: add course completion logic to the issuer
Jonath-z Nov 11, 2024
41812f4
chore: upgrade the node verson in the CI
Jonath-z Nov 15, 2024
2e1ccad
chore: upgrade to node 22
Jonath-z Nov 15, 2024
5617aa6
chore: trigger deployment
Jonath-z Nov 18, 2024
3dda8c0
fix: correct the issuer canister existance check
Jonath-z Nov 18, 2024
aa11662
feat: add loading state for completion pending
Jonath-z Nov 18, 2024
c8b19b6
fix: disable the record ICP certificate button when add completion co…
Jonath-z Nov 18, 2024
356aed3
feat: open the login window in the popup
Jonath-z Nov 18, 2024
0228735
fix: correct the typo in the endpoint
Jonath-z Nov 19, 2024
5827fc3
chore: enhence error message when the record failed
Jonath-z Nov 19, 2024
8b8550a
chore: use api instead of axios instance for complete call
Jonath-z Nov 19, 2024
9d19e81
refactor: move the complete call to the certificate service
Jonath-z Nov 19, 2024
5b424b2
refactor: remove .well-known folder
Jonath-z Nov 19, 2024
797ae20
refactor: use primary-outline variant and change button text
serapieTuyishime Nov 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v20.14.0
v22.6.0
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
},
"dependencies": {
"@coingecko/cryptoformat": "^0.5.4",
"@dfinity/agent": "^2.0.0",
"@dfinity/auth-client": "^2.0.0",
"@dfinity/candid": "^2.0.0",
"@dfinity/identity": "^2.0.0",
"@dfinity/principal": "^2.0.0",
"@dfinity/verifiable-credentials": "^0.0.2",
"@reduxjs/toolkit": "^1.9.3",
"@stefanprobst/remark-extract-toc": "^2.2.0",
"@sumsub/websdk": "^1.4.0",
Expand Down
77 changes: 77 additions & 0 deletions src/hooks/useIcpAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Identity } from "@dfinity/agent";
import { AuthClient } from "@dfinity/auth-client";
import { Principal } from "@dfinity/principal";
import { useEffect } from "react";

type Auth = {
client: AuthClient;
isAuthenticated: boolean;
identity: Identity;
principal: Principal;
principalText: string;
};

declare global {
interface Window {
auth: Auth;
canister: any;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using any type for global declarations.

Using any for window.auth and window.canister can lead to runtime errors. Consider defining more specific types.

- window.auth = {} as any;
- window.canister = {};
+ window.auth = {} as Auth;
+ window.canister = {}; // Define a specific type if possible

Committable suggestion was skipped due to low confidence.


export const MAX_TTL = BigInt(7 * 24 * 60 * 60 * 1000 * 1000 * 1000);

/**
* For production ready we shall use https://identity.ic0.app/ as identity provider
*/
export const IDENTITY_PROVIDER = "https://fgte5-ciaaa-aaaad-aaatq-cai.ic0.app/";
const useIcpAuth = () => {
useEffect(() => {
if (!window) return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use typeof window check for universal rendering environments.

To prevent potential ReferenceError when window is undefined in server-side rendering, use if (typeof window === 'undefined') return;.

Apply this diff to fix the issue:

- if (!window) return;
+ if (typeof window === 'undefined') return;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!window) return;
if (typeof window === 'undefined') return;

async function initializeContract() {
const authClient = await AuthClient.create();
window.auth = {} as any;
window.canister = {};
window.auth.client = authClient;
window.auth.isAuthenticated = await authClient.isAuthenticated();
window.auth.identity = authClient.getIdentity();
window.auth.principal = authClient.getIdentity()?.getPrincipal();
window.auth.principalText = authClient.getIdentity()?.getPrincipal().toText();
}

initializeContract();
}, []);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure proper error handling in initializeContract.

The initializeContract function lacks error handling, which can lead to unhandled promise rejections.

async function initializeContract() {
  try {
    const authClient = await AuthClient.create();
    window.auth = {} as Auth;
    window.canister = {};
    window.auth.client = authClient;
    window.auth.isAuthenticated = await authClient.isAuthenticated();
    window.auth.identity = authClient.getIdentity();
    window.auth.principal = authClient.getIdentity()?.getPrincipal();
    window.auth.principalText = authClient.getIdentity()?.getPrincipal().toText();
  } catch (error) {
    console.error("Failed to initialize contract:", error);
  }
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (!window) return;
async function initializeContract() {
const authClient = await AuthClient.create();
window.auth = {} as any;
window.canister = {};
window.auth.client = authClient;
window.auth.isAuthenticated = await authClient.isAuthenticated();
window.auth.identity = authClient.getIdentity();
window.auth.principal = authClient.getIdentity()?.getPrincipal();
window.auth.principalText = authClient.getIdentity()?.getPrincipal().toText();
}
initializeContract();
}, []);
useEffect(() => {
if (!window) return;
async function initializeContract() {
try {
const authClient = await AuthClient.create();
window.auth = {} as Auth;
window.canister = {};
window.auth.client = authClient;
window.auth.isAuthenticated = await authClient.isAuthenticated();
window.auth.identity = authClient.getIdentity();
window.auth.principal = authClient.getIdentity()?.getPrincipal();
window.auth.principalText = authClient.getIdentity()?.getPrincipal().toText();
} catch (error) {
console.error("Failed to initialize contract:", error);
}
}
initializeContract();
}, []);


async function login(callback: (principal: string) => void) {
if (!window) return;
const authClient = window.auth.client;

const isAuthenticated = await authClient.isAuthenticated();

if (!isAuthenticated) {
await authClient?.login({
maxTimeToLive: MAX_TTL,
identityProvider: IDENTITY_PROVIDER,
onSuccess: async () => {
window.auth.isAuthenticated = await authClient.isAuthenticated();
const principal = await authClient.getIdentity()?.getPrincipal().toText();
callback(principal);
},
});
}
const principal = await authClient.getIdentity()?.getPrincipal().toText();
callback(principal);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential errors in login function.

The login function should include error handling to manage potential failures during authentication.

async function login(callback: (principal: string) => void) {
  if (!window) return;
  const authClient = window.auth.client;

  try {
    const isAuthenticated = await authClient.isAuthenticated();

    if (!isAuthenticated) {
      await authClient?.login({
        maxTimeToLive: MAX_TTL,
        identityProvider: IDENTITY_PROVIDER,
        onSuccess: async () => {
          window.auth.isAuthenticated = await authClient.isAuthenticated();
          const principal = await authClient.getIdentity()?.getPrincipal().toText();
          callback(principal);
        },
      });
    } else {
      const principal = await authClient.getIdentity()?.getPrincipal().toText();
      callback(principal);
    }
  } catch (error) {
    console.error("Login failed:", error);
  }
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function login(callback: (principal: string) => void) {
if (!window) return;
const authClient = window.auth.client;
const isAuthenticated = await authClient.isAuthenticated();
if (!isAuthenticated) {
await authClient?.login({
maxTimeToLive: MAX_TTL,
identityProvider: IDENTITY_PROVIDER,
onSuccess: async () => {
window.auth.isAuthenticated = await authClient.isAuthenticated();
const principal = await authClient.getIdentity()?.getPrincipal().toText();
callback(principal);
},
});
}
const principal = await authClient.getIdentity()?.getPrincipal().toText();
callback(principal);
}
async function login(callback: (principal: string) => void) {
if (!window) return;
const authClient = window.auth.client;
try {
const isAuthenticated = await authClient.isAuthenticated();
if (!isAuthenticated) {
await authClient?.login({
maxTimeToLive: MAX_TTL,
identityProvider: IDENTITY_PROVIDER,
onSuccess: async () => {
window.auth.isAuthenticated = await authClient.isAuthenticated();
const principal = await authClient.getIdentity()?.getPrincipal().toText();
callback(principal);
},
});
} else {
const principal = await authClient.getIdentity()?.getPrincipal().toText();
callback(principal);
}
} catch (error) {
console.error("Login failed:", error);
}
}


async function logout() {
if (!window) return;
const authClient = window.auth.client;
authClient.logout();
}
Comment on lines +88 to +92
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure authClient is initialized before using it in logout function.

As with the login function, ensure that authClient is initialized before calling logout to prevent runtime errors.

Consider adding a check to confirm authClient is available or delaying the logout call until initialization is complete.


⚠️ Potential issue

Await the authClient.logout() call in the logout function.

The logout method is asynchronous and should be awaited to ensure it completes properly.

Apply this diff to fix the issue:

 async function logout() {
   if (typeof window === 'undefined') return;
   const authClient = window.auth.client;
-  authClient.logout();
+  await authClient.logout();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function logout() {
if (!window) return;
const authClient = window.auth.client;
authClient.logout();
}
async function logout() {
if (typeof window === 'undefined') return;
const authClient = window.auth.client;
await authClient.logout();
}


return {
login,
logout,
};
};

export default useIcpAuth;
45 changes: 45 additions & 0 deletions src/pages/achievements/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import MintCertificate from "@/components/sections/profile/modals/MintCertificat
import { Certificate } from "@/types/certificate";
import { User } from "@/types/bounty";
import { IRootState } from "@/store";
import Button from "@/components/ui/button";
import useIcpAuth, { IDENTITY_PROVIDER } from "@/hooks/useIcpAuth";
import { requestVerifiablePresentation, type VerifiablePresentationResponse } from "@dfinity/verifiable-credentials/request-verifiable-presentation";
import { Principal } from "@dfinity/principal";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider updating the dependency imports for consistency and maintenance.

The imports for requestVerifiablePresentation and Principal are direct imports from their respective packages. For better maintainability and to avoid potential issues with package updates, consider importing them from a centralized module if available.


/**
* interface for Achievement multiSelector
Expand All @@ -45,6 +49,7 @@ const Achievement = () => {
const [showMintCertificate, setShowMintCertificate] = useState(false);
const dispatch = useDispatch();
const { locale, query } = useRouter();
const { login } = useIcpAuth();

const findCertificateById = useCallback(async () => {
await dispatch(findCertificate({ id: query.id as string }));
Expand Down Expand Up @@ -104,6 +109,45 @@ const Achievement = () => {
return !achievement?.metadata?.image?.includes("/img/certificates/");
}, [achievement]);

const requestVC = (principal: string) => {
if (!achievement?.metadata) return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix unsafe optional chaining usage.

The use of optional chaining with achievement?.metadata can lead to a TypeError if it evaluates to undefined. Consider using a guard clause to ensure achievement and achievement.metadata are defined before accessing their properties.

-  if (!achievement?.metadata) return;
+  if (!achievement || !achievement.metadata) return;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const requestVC = (principal: string) => {
if (!achievement?.metadata) return;
const requestVC = (principal: string) => {
if (!achievement || !achievement.metadata) return;

const { issuedOn, issuerName, linkToWork, recipientName, feedbacks, name } = achievement?.metadata;
console.log({ issuedOn, issuerName, linkToWork, recipientName, feedbacks, name });
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix unsafe optional chaining usage.

The use of optional chaining with achievement?.metadata can lead to a TypeError if it evaluates to undefined. Consider using a guard clause to ensure achievement and achievement.metadata are defined before accessing their properties.

-  if (!achievement?.metadata) return;
+  if (!achievement || !achievement.metadata) return;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const requestVC = (principal: string) => {
if (!achievement?.metadata) return;
const { issuedOn, issuerName, linkToWork, recipientName, feedbacks, name } = achievement?.metadata;
console.log({ issuedOn, issuerName, linkToWork, recipientName, feedbacks, name });
const requestVC = (principal: string) => {
if (!achievement || !achievement.metadata) return;
const { issuedOn, issuerName, linkToWork, recipientName, feedbacks, name } = achievement?.metadata;
console.log({ issuedOn, issuerName, linkToWork, recipientName, feedbacks, name });
Tools
Biome

[error] 114-114: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)


requestVerifiablePresentation({
onSuccess: async (verifiablePresentation: VerifiablePresentationResponse) => {
console.log({ verifiablePresentation });
},
onError() {
console.log("An error occurred");
},
issuerData: {
/**
* This issuer is for testing.
* In this case dacade acts as a relying party requesting for verifiable identification
* @link https://internetcomputer.org/docs/current/developer-docs/identity/verifiable-credentials/how-it-works#test-verifiable-credentials
*/
origin: "https://qdiif-2iaaa-aaaap-ahjaq-cai.icp0.io",
canisterId: Principal.fromText("qdiif-2iaaa-aaaap-ahjaq-cai"),
},
credentialData: {
credentialSpec: {
credentialType: `Verified ${name} completion on Dacade`,
arguments: {
issuedOn,
issuerName,
linkToWork,
recipientName,
name,
},
},
credentialSubject: Principal.fromText(principal),
},
identityProvider: new URL(IDENTITY_PROVIDER),
// derivationOrigin: window.location.href,
});
};

return (
<>
<Head>
Expand Down Expand Up @@ -187,6 +231,7 @@ const Achievement = () => {
</AchievementViewItem>
</div>
)}
<Button onClick={() => login((principal) => requestVC(principal))}>Generate VC</Button>
</div>
</div>
</div>
Expand Down
30 changes: 24 additions & 6 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
Expand All @@ -15,11 +19,25 @@
"jsx": "preserve",
"incremental": true,
"paths": {
"@/*": ["./src/*"],
"react": ["./node_modules/@types/react"],
"@__mocks__/*": ["./__mocks__/*"]
"@/*": [
"./src/*"
],
"react": [
"./node_modules/@types/react"
],
"@__mocks__/*": [
"./__mocks__/*"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "src/components/sections/learning-modules/_partials/MarkDown.jsx", "jest.config.js"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"src/components/sections/learning-modules/_partials/MarkDown.jsx",
"jest.config.js"
],
"exclude": [
"node_modules"
]
}
101 changes: 99 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,53 @@
dependencies:
"@jridgewell/trace-mapping" "0.3.9"

"@dfinity/agent@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@dfinity/agent/-/agent-2.0.0.tgz#2c28d52b179af09f8b8998761ddaf81fcbd6d9c6"
integrity sha512-Cc2VDAMfxCNIQoaEeKPf0rbS/Y21HAF+eEFj6GfijNnUxS42i4zwUF2PUvEJZqaB2FQwA9SC7MDyuwL8BR2oJA==
dependencies:
"@noble/curves" "^1.4.0"
"@noble/hashes" "^1.3.1"
base64-arraybuffer "^0.2.0"
borc "^2.1.1"
buffer "^6.0.3"
simple-cbor "^0.4.1"

"@dfinity/auth-client@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@dfinity/auth-client/-/auth-client-2.0.0.tgz#09371b15c3e2e70efaa42297b1c9a58aefca0f8d"
integrity sha512-mF1Dqc2k94FJhwKum/K6NPJZ5txuTTa9HP8OgOwaA/Zc8rKfzRtUz+ORTjLC4i/4bPyp5zGhrp/8mCihwvNBTg==
dependencies:
idb "^7.0.2"

"@dfinity/candid@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@dfinity/candid/-/candid-2.0.0.tgz#e65a6b8ff565e82ec1dab4d9187b024b893aa532"
integrity sha512-poxIEnzErcKBM5yroabQ3VqQpiYZnqYZdLJWFuIQZzioGdP1mlnVLHx7IbgHN4HBlqVXYPFmEzMU02FnyYUA1Q==

"@dfinity/identity@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@dfinity/identity/-/identity-2.0.0.tgz#0a920742fc46be95d0fa8d3c0d26001b66b7a1c1"
integrity sha512-6NBdqG/Z2H6zEwBlpwWcWKsffgisIVzy2AMY7fXu5Jq31niFAoxv//oRsejeW45ChahVJlhvP/UYrNMJur8GpQ==
dependencies:
"@noble/curves" "^1.2.0"
"@noble/hashes" "^1.3.1"
borc "^2.1.1"

"@dfinity/principal@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@dfinity/principal/-/principal-2.0.0.tgz#3c67c34710d9e57fb40f50af0e9534b35312f153"
integrity sha512-cqJ5kOrPpxco+wvJC4TFBhdr4lkw9mnwKGAYunesyqdzSYi8lnFB4dShNqlHFWdwoxNAw6OZkt/B+0nLa+7Yqw==
dependencies:
"@noble/hashes" "^1.3.1"

"@dfinity/verifiable-credentials@^0.0.2":
version "0.0.2"
resolved "https://registry.yarnpkg.com/@dfinity/verifiable-credentials/-/verifiable-credentials-0.0.2.tgz#a4a0f941a4d482003998bc5e97d112e2403f3375"
integrity sha512-9vJYyXRPaXSPO74CS5zFLMoqtAaKINUSaYY9+rvRLvBXoGppNQQNWn/ZMCS+zCRyqfan01dlRlG8nap7xPx7KQ==
dependencies:
nanoid "^5.0.7"

"@emotion/babel-plugin@^11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c"
Expand Down Expand Up @@ -2849,7 +2896,7 @@
dependencies:
"@noble/hashes" "1.4.0"

"@noble/curves@1.4.2", "@noble/curves@~1.4.0":
"@noble/curves@1.4.2", "@noble/curves@^1.2.0", "@noble/curves@^1.4.0", "@noble/curves@~1.4.0":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9"
integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==
Expand Down Expand Up @@ -5223,6 +5270,11 @@ base-x@^3.0.2, base-x@^3.0.8:
dependencies:
safe-buffer "^5.0.1"

base64-arraybuffer@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45"
integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==

base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
Expand Down Expand Up @@ -5298,6 +5350,19 @@ boolbase@^1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==

borc@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/borc/-/borc-2.1.2.tgz#6ce75e7da5ce711b963755117dd1b187f6f8cf19"
integrity sha512-Sy9eoUi4OiKzq7VovMn246iTo17kzuyHJKomCfpWMlI6RpfN1gk95w7d7gH264nApVLg0HZfcpz62/g4VH1Y4w==
dependencies:
bignumber.js "^9.0.0"
buffer "^5.5.0"
commander "^2.15.0"
ieee754 "^1.1.13"
iso-url "~0.4.7"
json-text-sequence "~0.1.0"
readable-stream "^3.6.0"

brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
Expand Down Expand Up @@ -5771,6 +5836,11 @@ commander@11.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-11.0.0.tgz#43e19c25dbedc8256203538e8d7e9346877a6f67"
integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==

commander@^2.15.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==

commander@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
Expand Down Expand Up @@ -6310,6 +6380,11 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==

delimit-stream@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/delimit-stream/-/delimit-stream-0.1.0.tgz#9b8319477c0e5f8aeb3ce357ae305fc25ea1cd2b"
integrity sha512-a02fiQ7poS5CnjiJBAsjGLPp5EwVoGHNeu9sziBd9huppRfsAFIpv5zNLv0V1gbop53ilngAf5Kf331AwcoRBQ==

depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
Expand Down Expand Up @@ -8480,7 +8555,7 @@ idb@7.0.1:
resolved "https://registry.yarnpkg.com/idb/-/idb-7.0.1.tgz#d2875b3a2f205d854ee307f6d196f246fea590a7"
integrity sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==

idb@7.1.1:
idb@7.1.1, idb@^7.0.2:
version "7.1.1"
resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b"
integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==
Expand Down Expand Up @@ -8951,6 +9026,11 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==

iso-url@~0.4.7:
version "0.4.7"
resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-0.4.7.tgz#de7e48120dae46921079fe78f325ac9e9217a385"
integrity sha512-27fFRDnPAMnHGLq36bWTpKET+eiXct3ENlCcdcMdk+mjXrb2kw3mhBUg1B7ewAC0kVzlOPhADzQgz1SE6Tglog==

isomorphic-unfetch@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f"
Expand Down Expand Up @@ -9554,6 +9634,13 @@ json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==

json-text-sequence@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.1.1.tgz#a72f217dc4afc4629fff5feb304dc1bd51a2f3d2"
integrity sha512-L3mEegEWHRekSHjc7+sc8eJhba9Clq1PZ8kMkzf8OxElhXc8O4TS5MwcVlj9aEbm5dr81N90WHC5nAz3UO971w==
dependencies:
delimit-stream "0.1.0"

json5@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
Expand Down Expand Up @@ -10931,6 +11018,11 @@ nanoid@^3.3.4, nanoid@^3.3.7:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==

nanoid@^5.0.7:
version "5.0.7"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.7.tgz#6452e8c5a816861fd9d2b898399f7e5fd6944cc6"
integrity sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==

napi-wasm@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/napi-wasm/-/napi-wasm-1.1.0.tgz#bbe617823765ae9c1bc12ff5942370eae7b2ba4e"
Expand Down Expand Up @@ -12797,6 +12889,11 @@ signal-exit@^4.0.1, signal-exit@^4.1.0:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==

simple-cbor@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/simple-cbor/-/simple-cbor-0.4.1.tgz#0c88312e87db52b94e0e92f6bd1cf634e86f8a22"
integrity sha512-rijcxtwx2b4Bje3sqeIqw5EeW7UlOIC4YfOdwqIKacpvRQ/D78bWg/4/0m5e0U91oKvlGh7LlJuZCu07ISCC7w==

simple-concat@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
Expand Down
Loading