Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
00b9eea9ec Version Packages (#1980)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-21 13:37:37 -08:00
iketwandGitHub bf27b88728 [RN] Improves error messages for embedded wallet (#1989) 2023-11-21 19:41:23 +00:00
Tair AsimandGitHub 17fe41d42b fix: api key caching ttl (#1988) 2023-11-21 17:38:59 +00:00
Tair AsimandGitHub 36600decef chore: add logging to authenticate (#1973) 2023-11-21 17:08:22 +00:00
FirekeeperandGitHub e09221f1e0 [Unity] SW Account Override Support (#1986) 2023-11-21 12:16:28 +00:00
FirekeeperandGitHub 62cc785c4a [Unity] Support Add/Remove Admin and Create Session Key for SW (#1985) 2023-11-21 10:42:57 +00:00
Adam MajmudarandGitHub 7c45941b57 Add support for executeMetaTransaction (#1974) 2023-11-20 19:12:11 +00:00
FirekeeperandGitHub 7a2e0edab4 [Unity] Apple and Facebook logins (#1981) 2023-11-20 18:45:33 +00:00
Manan TankandGitHub fb0cb676b9 add ConnectEmbed + Fix double connection issue with Safe/Smart in ConnectWallet (#1952) 2023-11-20 17:56:55 +00:00
12d85dec10 [React/RN] Move usePersonalWalletAddress hook to core (#1971)
Co-authored-by: Manan Tank <[email protected]>
2023-11-20 17:29:32 +00:00
1094a03a04 [wallet, react] Add Coin98 Wallet (#1921)
Co-authored-by: Manan Tank <[email protected]>
2023-11-20 16:27:00 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
6cc38b0680 Version Packages (#1970)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-18 14:30:05 -08:00
Joaquim VergesandGitHub 7abb36a7ec [Wallets] Enforce authenticated users when getting EW signers (#1978) 2023-11-18 17:21:15 +00:00
Joaquim VergesandGitHub 6832b19fc5 [Wallets] Fix potential embedded wallet autoconnect race condition (#1977) 2023-11-18 07:18:30 +00:00
nkrishangandGitHub 1c4cbedb6a Override signMessage for SmartWallet class (#1953) 2023-11-18 06:04:02 +00:00
Phillip HoandGitHub 8ff87be631 fix: Reuse response body in service utils helper (#1976) 2023-11-18 01:50:37 +00:00
YashandGitHub e43beec36a [SDK] Resolve diamond pattern facets abi (#1935) 2023-11-17 15:56:29 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c5b7fef5ba Version Packages (#1966)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-17 10:40:41 +01:00
Manan TankandGitHub 4c5e40ae4e [react] Fix ConnectWallet text alignment issues in paper/embedded wallet (#1969) 2023-11-17 09:22:45 +00:00
Jonas DanielsandGitHub ab2dfd4f5c Initialize @thirdweb-dev/crypto and @thirdweb-dev/merkletree (#1861)
Signed-off-by: Jonas Daniels <[email protected]>
2023-11-16 22:32:24 +00:00
Manan TankandGitHub 13029e3286 [react] ConnectWallet UI adjustments, expose hook to fetch embeddedWallet email (#1965) 2023-11-16 20:25:25 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
44c9ccd65d Version Packages (#1963)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-16 10:21:09 -08:00
Manan TankandGitHub 7912c26323 [react] use custom theme context in styled components (#1954) 2023-11-16 17:53:44 +00:00
69d68f5a25 [RN/React] Enables FB and Apple sign in for embedded wallet (#1945)
Co-authored-by: Manan Tank <[email protected]>
2023-11-16 13:31:22 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
836be52724 Version Packages (#1959)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-15 20:26:55 -08:00
Joaquim VergesandGitHub 9f993dcc1e [EmbeddedWallet] fix switch chains logic (#1958) 2023-11-15 16:56:19 -08:00
255 changed files with 10244 additions and 2048 deletions
+11 -1
View File
@@ -15,7 +15,17 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["e2e-cra-5", "e2e-next-13", "e2e-vite-3"],
"ignore": [
"e2e-cra-5",
"e2e-next-13",
"e2e-vite-3",
"bundlers-esbuild",
"bundlers-vite",
"bundlers-webpack",
"bundlers-swc",
"runtimes-node-commonjs",
"runtimes-node-module"
],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"updateInternalDependents": "always",
"onlyUpdatePeerDependentsWhenOutOfRange": true
+22
View File
@@ -31,6 +31,11 @@ jobs:
with:
fetch-depth: 25
- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install
uses: ./.github/composite-actions/install
@@ -59,6 +64,11 @@ jobs:
node-version: 20
check-latest: true
- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- uses: pnpm/action-setup@v2
with:
version: 8
@@ -84,6 +94,12 @@ jobs:
node-version: 20
check-latest: true
# we use bun for some test suites
- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- uses: pnpm/action-setup@v2
with:
version: 8
@@ -114,6 +130,12 @@ jobs:
node-version: 20
check-latest: true
# we use bun for some test suites
- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- uses: pnpm/action-setup@v2
with:
version: 8
+5
View File
@@ -26,6 +26,11 @@ jobs:
with:
fetch-depth: 0
- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install
uses: ./.github/composite-actions/install
+5
View File
@@ -67,6 +67,11 @@ jobs:
repository: ${{ fromJson(steps.get_pull_request_ref.outputs.data).head.repo.full_name }}
ref: ${{ fromJson(steps.get_pull_request_ref.outputs.data).head.ref }}
- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install
uses: ./.github/composite-actions/install
+5
View File
@@ -26,6 +26,11 @@ jobs:
with:
fetch-depth: 0
- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install
uses: ./.github/composite-actions/install
+37
View File
@@ -1,5 +1,42 @@
# @thirdweb-dev/auth
## 4.1.11
### Patch Changes
- [#1973](https://github.com/thirdweb-dev/js/pull/1973) [`36600dec`](https://github.com/thirdweb-dev/js/commit/36600decefca7b9ebb1924757b4186b559e439f6) Thanks [@assimovt](https://github.com/assimovt)! - add logging to authenticate
- Updated dependencies [[`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1)]:
- @thirdweb-dev/wallets@2.1.8
## 4.1.10
### Patch Changes
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96)]:
- @thirdweb-dev/wallets@2.1.7
## 4.1.9
### Patch Changes
- Updated dependencies [[`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4)]:
- @thirdweb-dev/wallets@2.1.6
## 4.1.8
### Patch Changes
- Updated dependencies [[`69d68f5a`](https://github.com/thirdweb-dev/js/commit/69d68f5a25877bf5ea694719d2cf4ca8388f3091)]:
- @thirdweb-dev/wallets@2.1.5
## 4.1.7
### Patch Changes
- Updated dependencies [[`9f993dcc`](https://github.com/thirdweb-dev/js/commit/9f993dcc1e60cc087850b8fb9ce09524073ce0b5)]:
- @thirdweb-dev/wallets@2.1.4
## 4.1.6
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "4.1.6",
"version": "4.1.11",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
@@ -58,7 +58,7 @@
"scripts": {
"build": "preconstruct build",
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/",
"lint": "eslint src/ && bunx publint --strict --level warning",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/ && rm -rf node_modules/",
"test": "mocha --config './test/.mocharc.json' --timeout 30000 --parallel './test/**/*.test.ts'",
@@ -62,6 +62,7 @@ export async function getUser<
const token = getToken(req);
if (!token) {
console.error("Error: No auth token found");
return null;
}
@@ -75,6 +76,7 @@ export async function getUser<
},
});
} catch (err) {
console.error(`Authenticate Error: ${(err as Error)?.message}`);
return null;
}
+3 -20
View File
@@ -3,24 +3,13 @@
"version": "0.1.58",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"browser": {
"./dist/thirdweb-dev-chains.esm.js": "./dist/thirdweb-dev-chains.browser.esm.js"
},
"exports": {
".": {
"module": {
"browser": "./dist/thirdweb-dev-chains.browser.esm.js",
"default": "./dist/thirdweb-dev-chains.esm.js"
},
"module": "./dist/thirdweb-dev-chains.esm.js",
"default": "./dist/thirdweb-dev-chains.cjs.js"
},
"./package.json": "./package.json"
},
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/chains",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/thirdweb-dev/js/issues"
},
"author": "thirdweb eng <[email protected]>",
"devDependencies": {
"@babel/core": "^7.22.9",
@@ -29,11 +18,9 @@
"@babel/runtime": "^7.22.6",
"@preconstruct/cli": "2.7.0",
"@thirdweb-dev/tsconfig": "workspace:*",
"@types/jest": "^29.5.3",
"axios": "^1.4.0",
"deepmerge": "^4.3.1",
"eslint-config-thirdweb": "workspace:*",
"jest": "^29.6.2",
"plop": "^3.1.2",
"typescript": "^5.1.6"
},
@@ -41,11 +28,7 @@
"entrypoints": [
"index.ts"
],
"exports": {
"envConditions": [
"browser"
]
}
"exports": true
},
"sideEffects": false,
"scripts": {
@@ -56,7 +39,7 @@
"plop": "plop",
"add-chain": "plop add-chain",
"override-chain": "plop override-chain",
"test": "jest"
"test": "bun test"
},
"engines": {
"node": ">=18"
+44
View File
@@ -1,5 +1,49 @@
# thirdweb
## 0.13.19
### Patch Changes
- Updated dependencies [[`36600dec`](https://github.com/thirdweb-dev/js/commit/36600decefca7b9ebb1924757b4186b559e439f6), [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1), [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4)]:
- @thirdweb-dev/auth@4.1.11
- @thirdweb-dev/wallets@2.1.8
- @thirdweb-dev/sdk@4.0.16
## 0.13.18
### Patch Changes
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96), [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
- @thirdweb-dev/wallets@2.1.7
- @thirdweb-dev/sdk@4.0.15
- @thirdweb-dev/auth@4.1.10
## 0.13.17
### Patch Changes
- Updated dependencies [[`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4), [`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4)]:
- @thirdweb-dev/sdk@4.0.14
- @thirdweb-dev/wallets@2.1.6
- @thirdweb-dev/storage@2.0.5
- @thirdweb-dev/auth@4.1.9
## 0.13.16
### Patch Changes
- Updated dependencies [[`69d68f5a`](https://github.com/thirdweb-dev/js/commit/69d68f5a25877bf5ea694719d2cf4ca8388f3091)]:
- @thirdweb-dev/wallets@2.1.5
- @thirdweb-dev/auth@4.1.8
## 0.13.15
### Patch Changes
- Updated dependencies [[`9f993dcc`](https://github.com/thirdweb-dev/js/commit/9f993dcc1e60cc087850b8fb9ce09524073ce0b5)]:
- @thirdweb-dev/wallets@2.1.4
- @thirdweb-dev/auth@4.1.7
## 0.13.14
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.13.14",
"version": "0.13.19",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+75 -32
View File
@@ -1,4 +1,3 @@
import { LocalWallet } from "@thirdweb-dev/wallets";
import assert from "assert";
import chalk from "chalk";
import fs from "fs";
@@ -30,7 +29,7 @@ type ConfigPaths = {
credsConfigPath: string;
cliWalletPath: string;
tokenPath: string;
}
};
export async function loginUser(
configPaths: ConfigPaths,
@@ -56,12 +55,21 @@ export async function loginUser(
}
}
export async function logoutUser(credsConfigPath: string, tokenPath: string, cliWalletPath: string) {
export async function logoutUser(
credsConfigPath: string,
tokenPath: string,
cliWalletPath: string,
) {
try {
ora("Logging out...").start();
const dirExists = fs.existsSync(credsConfigPath) && fs.existsSync(tokenPath) && fs.existsSync(cliWalletPath);
const dirExists =
fs.existsSync(credsConfigPath) &&
fs.existsSync(tokenPath) &&
fs.existsSync(cliWalletPath);
if (!dirExists) {
ora().warn(chalk.yellow("You are already logged out, did you mean to login?"));
ora().warn(
chalk.yellow("You are already logged out, did you mean to login?"),
);
return;
}
fs.unlinkSync(credsConfigPath);
@@ -89,7 +97,9 @@ export const authenticateUser = async (
props: LoginProps = defaultLoginProps,
) => {
const { credsConfigPath, cliWalletPath, tokenPath } = props.configPaths;
const waitForDashboard = spinner("Waiting for a response from the dashboard").clear();
const waitForDashboard = spinner(
"Waiting for a response from the dashboard",
).clear();
// Get or generate a localwallet.
const wallet = await getOrGenerateLocalWallet(credsConfigPath, cliWalletPath);
@@ -112,10 +122,16 @@ export const authenticateUser = async (
let loginTimeoutHandle: NodeJS.Timeout;
const timerPromise = new Promise<void>((resolve, reject) => {
loginTimeoutHandle = setTimeout(() => {
logger.error("Login session timed out, server didn't receive a response in 5 minutes. Please try again.");
logger.error(
"Login session timed out, server didn't receive a response in 5 minutes. Please try again.",
);
server.close();
clearTimeout(loginTimeoutHandle);
reject(new Error("Login session timed out, server didn't receive a response in 5 minutes. Please try again."));
reject(
new Error(
"Login session timed out, server didn't receive a response in 5 minutes. Please try again.",
),
);
}, 300000);
});
@@ -129,12 +145,15 @@ export const authenticateUser = async (
}
});
}
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Origin', "https://thirdweb.com");
res.setHeader('Access-Control-Allow-Methods', 'POST');
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Allow-Origin", "https://thirdweb.com");
res.setHeader("Access-Control-Allow-Methods", "POST");
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Headers', 'content-type, baggage, sentry-trace');
if (req.method === "OPTIONS") {
res.setHeader(
"Access-Control-Allow-Headers",
"content-type, baggage, sentry-trace",
);
res.writeHead(200);
res.end();
return;
@@ -148,12 +167,18 @@ export const authenticateUser = async (
res.writeHead(500, "Unable to authenticate with the dashboard!");
res.end("Unable to authenticate with the dashboard!", () => {
finish(new Error("Unable to authenticate with the dashboard!"));
})
reject(chalk.red("Something went wrong! Unable to authenticate with the dashboard."));
});
reject(
chalk.red(
"Something went wrong! Unable to authenticate with the dashboard.",
),
);
waitForDashboard.stop();
}
if (query.token) {
const token = Array.isArray(query.token) ? query.token[0] : query.token;
const token = Array.isArray(query.token)
? query.token[0]
: query.token;
const theirState = Array.isArray(query.state)
? query.state[0]
: query.state;
@@ -171,13 +196,18 @@ export const authenticateUser = async (
} else {
// Save the token to the config file.
// eslint-disable-next-line no-unused-expressions
fs.writeFileSync(tokenPath, token), {
encoding: "utf8",
mode: 0o600,
};
fs.writeFileSync(tokenPath, token),
{
encoding: "utf8",
mode: 0o600,
};
res.end(() => {
waitForDashboard.clear();
console.log(chalk.green(`Successfully linked your account to this device`));
console.log(
chalk.green(
`Successfully linked your account to this device`,
),
);
finish();
});
logger.info(chalk.green(`\nSuccessfully logged in.`));
@@ -197,15 +227,21 @@ export const authenticateUser = async (
server.listen(8976);
});
console.log(`Automatically attempting to open a link to authenticate with our dashboard...\n`);
console.log(
`Automatically attempting to open a link to authenticate with our dashboard...\n`,
);
waitForDashboard.start();
// Adding this timeout since it feels weird for the browser to open before the spinner.
setTimeout(async () => {
await open(urlToOpen);
}, 2000);
console.log(chalk.yellow(`If the browser doesn't open, please use this link to authenticate:\n`));
console.log(chalk.yellow(urlToOpen + '\n'));
console.log(
chalk.yellow(
`If the browser doesn't open, please use this link to authenticate:\n`,
),
);
console.log(chalk.yellow(urlToOpen + "\n"));
return Promise.race([timerPromise, loginPromise]);
};
@@ -229,9 +265,13 @@ async function getOrCreatePassword(configCredsPath: string): Promise<string> {
}
}
async function getOrGenerateLocalWallet(configCredsPath: string, cliWalletPath: string) {
async function getOrGenerateLocalWallet(
configCredsPath: string,
cliWalletPath: string,
) {
// Get or prompt for password.
const password = await getOrCreatePassword(configCredsPath);
const { LocalWallet } = await import("@thirdweb-dev/wallets");
const wallet = new LocalWallet();
const foundWallet = fs.existsSync(cliWalletPath);
@@ -265,9 +305,13 @@ async function getOrGenerateLocalWallet(configCredsPath: string, cliWalletPath:
});
// write password
fs.writeFileSync(configCredsPath, JSON.stringify({
password: password,
}), "utf8");
fs.writeFileSync(
configCredsPath,
JSON.stringify({
password: password,
}),
"utf8",
);
return wallet;
}
@@ -280,14 +324,13 @@ export const validateKey = async (apiSecretKey: string) => {
headers: {
"Content-Type": "application/json",
"x-secret-key": apiSecretKey,
}
})
},
});
if (response.status !== 200) {
throw new Error("Unauthorized key");
}
} catch (error) {
throw new Error(chalk.red("Unauthorized key"));
}
};
};
@@ -1,4 +1,4 @@
import got from "got/dist/source";
import got from "got";
export const getStartOrDev = async (name: string) => {
try {
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWFactory__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWFactory__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWFactory__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWFactory__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWFactory__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWFactory__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWFactory__factory.esm.js"
}
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWFee__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWFee__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWFee__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWFee__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWFee__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWFee__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWFee__factory.esm.js"
}
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMinimalFactory__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMinimalFactory__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWMinimalFactory__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWMinimalFactory__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWMinimalFactory__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWMinimalFactory__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWMinimalFactory__factory.esm.js"
}
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.esm.js"
}
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.esm.js"
}
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.esm.js"
}
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.esm.js"
}
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWProxy__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWProxy__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWProxy__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWProxy__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWProxy__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWProxy__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWProxy__factory.esm.js"
}
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWRegistry__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWRegistry__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWRegistry__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWRegistry__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWRegistry__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWRegistry__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWRegistry__factory.esm.js"
}
@@ -1,8 +0,0 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWRouter__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWRouter__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWRouter__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWRouter__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWRouter__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWRouter__factory.browser.esm.js"
}
}
@@ -1,8 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWStatelessFactory__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWStatelessFactory__factory.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js-factories-TWStatelessFactory__factory.cjs.js": "./dist/thirdweb-dev-contracts-js-factories-TWStatelessFactory__factory.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js-factories-TWStatelessFactory__factory.esm.js": "./dist/thirdweb-dev-contracts-js-factories-TWStatelessFactory__factory.browser.esm.js"
}
"module": "dist/thirdweb-dev-contracts-js-factories-TWStatelessFactory__factory.esm.js"
}
+423 -7
View File
@@ -3,9 +3,422 @@
"version": "1.3.16",
"main": "dist/thirdweb-dev-contracts-js.cjs.js",
"module": "dist/thirdweb-dev-contracts-js.esm.js",
"browser": {
"./dist/thirdweb-dev-contracts-js.cjs.js": "./dist/thirdweb-dev-contracts-js.browser.cjs.js",
"./dist/thirdweb-dev-contracts-js.esm.js": "./dist/thirdweb-dev-contracts-js.browser.esm.js"
"exports": {
".": {
"module": "./dist/thirdweb-dev-contracts-js.esm.js",
"default": "./dist/thirdweb-dev-contracts-js.cjs.js"
},
"./factories/TWFee__factory": {
"module": "./factories/TWFee__factory/dist/thirdweb-dev-contracts-js-factories-TWFee__factory.esm.js",
"default": "./factories/TWFee__factory/dist/thirdweb-dev-contracts-js-factories-TWFee__factory.cjs.js"
},
"./factories/TWProxy__factory": {
"module": "./factories/TWProxy__factory/dist/thirdweb-dev-contracts-js-factories-TWProxy__factory.esm.js",
"default": "./factories/TWProxy__factory/dist/thirdweb-dev-contracts-js-factories-TWProxy__factory.cjs.js"
},
"./factories/TWFactory__factory": {
"module": "./factories/TWFactory__factory/dist/thirdweb-dev-contracts-js-factories-TWFactory__factory.esm.js",
"default": "./factories/TWFactory__factory/dist/thirdweb-dev-contracts-js-factories-TWFactory__factory.cjs.js"
},
"./factories/TWRegistry__factory": {
"module": "./factories/TWRegistry__factory/dist/thirdweb-dev-contracts-js-factories-TWRegistry__factory.esm.js",
"default": "./factories/TWRegistry__factory/dist/thirdweb-dev-contracts-js-factories-TWRegistry__factory.cjs.js"
},
"./factories/TWMinimalFactory__factory": {
"module": "./factories/TWMinimalFactory__factory/dist/thirdweb-dev-contracts-js-factories-TWMinimalFactory__factory.esm.js",
"default": "./factories/TWMinimalFactory__factory/dist/thirdweb-dev-contracts-js-factories-TWMinimalFactory__factory.cjs.js"
},
"./factories/TWStatelessFactory__factory": {
"module": "./factories/TWStatelessFactory__factory/dist/thirdweb-dev-contracts-js-factories-TWStatelessFactory__factory.esm.js",
"default": "./factories/TWStatelessFactory__factory/dist/thirdweb-dev-contracts-js-factories-TWStatelessFactory__factory.cjs.js"
},
"./factories/TWMultichainRegistry__factory": {
"module": "./factories/TWMultichainRegistry__factory/dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.esm.js",
"default": "./factories/TWMultichainRegistry__factory/dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.cjs.js"
},
"./factories/TWMultichainRegistryLogic__factory": {
"module": "./factories/TWMultichainRegistryLogic__factory/dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.esm.js",
"default": "./factories/TWMultichainRegistryLogic__factory/dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.cjs.js"
},
"./factories/TWMultichainRegistryRouter__factory": {
"module": "./factories/TWMultichainRegistryRouter__factory/dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.esm.js",
"default": "./factories/TWMultichainRegistryRouter__factory/dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.cjs.js"
},
"./factories/TWMultichainRegistryStorage__factory": {
"module": "./factories/TWMultichainRegistryStorage__factory/dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.esm.js",
"default": "./factories/TWMultichainRegistryStorage__factory/dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.cjs.js"
},
"./package.json": "./package.json",
"./dist/abis/SharedMetadataBatch.json": "./dist/abis/SharedMetadataBatch.json",
"./dist/abis/Staking721Upgradeable.json": "./dist/abis/Staking721Upgradeable.json",
"./dist/abis/Staking1155.json": "./dist/abis/Staking1155.json",
"./dist/abis/IPluginMap.json": "./dist/abis/IPluginMap.json",
"./dist/abis/SignatureAction.json": "./dist/abis/SignatureAction.json",
"./dist/abis/IDirectListings.json": "./dist/abis/IDirectListings.json",
"./dist/abis/Multiwrap.json": "./dist/abis/Multiwrap.json",
"./dist/abis/ERC20Votes.json": "./dist/abis/ERC20Votes.json",
"./dist/abis/ITWFee.json": "./dist/abis/ITWFee.json",
"./dist/abis/Permissions.json": "./dist/abis/Permissions.json",
"./dist/abis/Pack.json": "./dist/abis/Pack.json",
"./dist/abis/RulesEngineExtension.json": "./dist/abis/RulesEngineExtension.json",
"./dist/abis/PrimarySale.json": "./dist/abis/PrimarySale.json",
"./dist/abis/EntryPoint.json": "./dist/abis/EntryPoint.json",
"./dist/abis/DropSinglePhase1155.json": "./dist/abis/DropSinglePhase1155.json",
"./dist/abis/IAirdropERC721.json": "./dist/abis/IAirdropERC721.json",
"./dist/abis/TWFee.json": "./dist/abis/TWFee.json",
"./dist/abis/IContractPublisher.json": "./dist/abis/IContractPublisher.json",
"./dist/abis/IOwnable.json": "./dist/abis/IOwnable.json",
"./dist/abis/SignatureDrop_V4.json": "./dist/abis/SignatureDrop_V4.json",
"./dist/abis/ITokenERC20.json": "./dist/abis/ITokenERC20.json",
"./dist/abis/OperatorFilterToggle.json": "./dist/abis/OperatorFilterToggle.json",
"./dist/abis/EnumerableSet.json": "./dist/abis/EnumerableSet.json",
"./dist/abis/ERC721A__IERC721ReceiverUpgradeable.json": "./dist/abis/ERC721A__IERC721ReceiverUpgradeable.json",
"./dist/abis/AccountFactory.json": "./dist/abis/AccountFactory.json",
"./dist/abis/MerkleProof.json": "./dist/abis/MerkleProof.json",
"./dist/abis/SafeCast.json": "./dist/abis/SafeCast.json",
"./dist/abis/Staking721.json": "./dist/abis/Staking721.json",
"./dist/abis/ERC721A.json": "./dist/abis/ERC721A.json",
"./dist/abis/UserOperationLib.json": "./dist/abis/UserOperationLib.json",
"./dist/abis/ERC721Multiwrap.json": "./dist/abis/ERC721Multiwrap.json",
"./dist/abis/SignatureDrop.json": "./dist/abis/SignatureDrop.json",
"./dist/abis/IMintableERC721.json": "./dist/abis/IMintableERC721.json",
"./dist/abis/IBurnToClaim.json": "./dist/abis/IBurnToClaim.json",
"./dist/abis/AccountPermissions.json": "./dist/abis/AccountPermissions.json",
"./dist/abis/IStakeManager.json": "./dist/abis/IStakeManager.json",
"./dist/abis/IClaimConditionsSinglePhase.json": "./dist/abis/IClaimConditionsSinglePhase.json",
"./dist/abis/PrimarySaleInit.json": "./dist/abis/PrimarySaleInit.json",
"./dist/abis/IERC2771Context.json": "./dist/abis/IERC2771Context.json",
"./dist/abis/IAccountCore.json": "./dist/abis/IAccountCore.json",
"./dist/abis/IERC165.json": "./dist/abis/IERC165.json",
"./dist/abis/BurnToClaimDrop721Logic.json": "./dist/abis/BurnToClaimDrop721Logic.json",
"./dist/abis/ERC721DelayedReveal.json": "./dist/abis/ERC721DelayedReveal.json",
"./dist/abis/IClaimCondition_V1.json": "./dist/abis/IClaimCondition_V1.json",
"./dist/abis/IERC1155.json": "./dist/abis/IERC1155.json",
"./dist/abis/IStaking1155.json": "./dist/abis/IStaking1155.json",
"./dist/abis/Math.json": "./dist/abis/Math.json",
"./dist/abis/IRoyaltyEngineV1.json": "./dist/abis/IRoyaltyEngineV1.json",
"./dist/abis/DelayedRevealStorage.json": "./dist/abis/DelayedRevealStorage.json",
"./dist/abis/ISignatureMintERC1155.json": "./dist/abis/ISignatureMintERC1155.json",
"./dist/abis/IPack.json": "./dist/abis/IPack.json",
"./dist/abis/SenderCreator.json": "./dist/abis/SenderCreator.json",
"./dist/abis/IMintableERC1155.json": "./dist/abis/IMintableERC1155.json",
"./dist/abis/ReentrancyGuardLogic.json": "./dist/abis/ReentrancyGuardLogic.json",
"./dist/abis/IERC1155Receiver.json": "./dist/abis/IERC1155Receiver.json",
"./dist/abis/IERC4906.json": "./dist/abis/IERC4906.json",
"./dist/abis/ERC2771ContextLogic.json": "./dist/abis/ERC2771ContextLogic.json",
"./dist/abis/TWBitMaps.json": "./dist/abis/TWBitMaps.json",
"./dist/abis/IERC2981.json": "./dist/abis/IERC2981.json",
"./dist/abis/ILazyMintWithTier.json": "./dist/abis/ILazyMintWithTier.json",
"./dist/abis/ERC721SignatureMint.json": "./dist/abis/ERC721SignatureMint.json",
"./dist/abis/LazyMintWithTier_V1.json": "./dist/abis/LazyMintWithTier_V1.json",
"./dist/abis/EvolvingNFTLogic.json": "./dist/abis/EvolvingNFTLogic.json",
"./dist/abis/IPrimarySale.json": "./dist/abis/IPrimarySale.json",
"./dist/abis/IRouterState.json": "./dist/abis/IRouterState.json",
"./dist/abis/NFTMetadataRenderer.json": "./dist/abis/NFTMetadataRenderer.json",
"./dist/abis/SignatureActionUpgradeable.json": "./dist/abis/SignatureActionUpgradeable.json",
"./dist/abis/IPermissions.json": "./dist/abis/IPermissions.json",
"./dist/abis/EnglishAuctionsLogic.json": "./dist/abis/EnglishAuctionsLogic.json",
"./dist/abis/IMulticall.json": "./dist/abis/IMulticall.json",
"./dist/abis/Exec.json": "./dist/abis/Exec.json",
"./dist/abis/IAccountFactory.json": "./dist/abis/IAccountFactory.json",
"./dist/abis/ERC721LazyMint.json": "./dist/abis/ERC721LazyMint.json",
"./dist/abis/IDropSinglePhase_V1.json": "./dist/abis/IDropSinglePhase_V1.json",
"./dist/abis/ERC721AQueryableInit.json": "./dist/abis/ERC721AQueryableInit.json",
"./dist/abis/ERC721AQueryableUpgradeable.json": "./dist/abis/ERC721AQueryableUpgradeable.json",
"./dist/abis/DropERC721.json": "./dist/abis/DropERC721.json",
"./dist/abis/AirdropERC721.json": "./dist/abis/AirdropERC721.json",
"./dist/abis/IDropSinglePhase.json": "./dist/abis/IDropSinglePhase.json",
"./dist/abis/zora_IERC721Drop.json": "./dist/abis/zora_IERC721Drop.json",
"./dist/abis/TWStringSet.json": "./dist/abis/TWStringSet.json",
"./dist/abis/InitStorage.json": "./dist/abis/InitStorage.json",
"./dist/abis/IDropSinglePhase1155.json": "./dist/abis/IDropSinglePhase1155.json",
"./dist/abis/AccountCore.json": "./dist/abis/AccountCore.json",
"./dist/abis/TWStorageSlot.json": "./dist/abis/TWStorageSlot.json",
"./dist/abis/TWMinimalFactory.json": "./dist/abis/TWMinimalFactory.json",
"./dist/abis/TWRegistry.json": "./dist/abis/TWRegistry.json",
"./dist/abis/OpenEditionERC721.json": "./dist/abis/OpenEditionERC721.json",
"./dist/abis/ERC2771Context.json": "./dist/abis/ERC2771Context.json",
"./dist/abis/IERC721Receiver.json": "./dist/abis/IERC721Receiver.json",
"./dist/abis/ISharedMetadataBatch.json": "./dist/abis/ISharedMetadataBatch.json",
"./dist/abis/ERC1155Holder.json": "./dist/abis/ERC1155Holder.json",
"./dist/abis/BurnToClaim.json": "./dist/abis/BurnToClaim.json",
"./dist/abis/Forwarder.json": "./dist/abis/Forwarder.json",
"./dist/abis/ERC2771ContextInit.json": "./dist/abis/ERC2771ContextInit.json",
"./dist/abis/SharedMetadataBatchStorage.json": "./dist/abis/SharedMetadataBatchStorage.json",
"./dist/abis/PlatformFeeLogic.json": "./dist/abis/PlatformFeeLogic.json",
"./dist/abis/Royalty.json": "./dist/abis/Royalty.json",
"./dist/abis/IMintableERC20.json": "./dist/abis/IMintableERC20.json",
"./dist/abis/PermissionsLogic.json": "./dist/abis/PermissionsLogic.json",
"./dist/abis/Staking20Base.json": "./dist/abis/Staking20Base.json",
"./dist/abis/IContext.json": "./dist/abis/IContext.json",
"./dist/abis/Staking20Upgradeable.json": "./dist/abis/Staking20Upgradeable.json",
"./dist/abis/Proxy.json": "./dist/abis/Proxy.json",
"./dist/abis/ERC1967Proxy.json": "./dist/abis/ERC1967Proxy.json",
"./dist/abis/IRoyaltyPayments.json": "./dist/abis/IRoyaltyPayments.json",
"./dist/abis/BaseRouter.json": "./dist/abis/BaseRouter.json",
"./dist/abis/TWAddress.json": "./dist/abis/TWAddress.json",
"./dist/abis/EvolvingNFT.json": "./dist/abis/EvolvingNFT.json",
"./dist/abis/ContractMetadata.json": "./dist/abis/ContractMetadata.json",
"./dist/abis/IOffers.json": "./dist/abis/IOffers.json",
"./dist/abis/Staking1155Base.json": "./dist/abis/Staking1155Base.json",
"./dist/abis/PermissionsEnumerableImpl.json": "./dist/abis/PermissionsEnumerableImpl.json",
"./dist/abis/TWFactory.json": "./dist/abis/TWFactory.json",
"./dist/abis/IERC6551Account.json": "./dist/abis/IERC6551Account.json",
"./dist/abis/ILoyaltyCard.json": "./dist/abis/ILoyaltyCard.json",
"./dist/abis/SafeMath.json": "./dist/abis/SafeMath.json",
"./dist/abis/MarketplaceV3.json": "./dist/abis/MarketplaceV3.json",
"./dist/abis/ReentrancyGuardInit.json": "./dist/abis/ReentrancyGuardInit.json",
"./dist/abis/DropERC721_V3.json": "./dist/abis/DropERC721_V3.json",
"./dist/abis/PermissionOverrideCoreRouter.json": "./dist/abis/PermissionOverrideCoreRouter.json",
"./dist/abis/OperatorFilterer.json": "./dist/abis/OperatorFilterer.json",
"./dist/abis/IDelayedReveal.json": "./dist/abis/IDelayedReveal.json",
"./dist/abis/TokenERC721.json": "./dist/abis/TokenERC721.json",
"./dist/abis/PlatformFeeStorage.json": "./dist/abis/PlatformFeeStorage.json",
"./dist/abis/ERC6551AccountLib.json": "./dist/abis/ERC6551AccountLib.json",
"./dist/abis/ITWRegistry.json": "./dist/abis/ITWRegistry.json",
"./dist/abis/DropERC20.json": "./dist/abis/DropERC20.json",
"./dist/abis/TWStatelessFactory.json": "./dist/abis/TWStatelessFactory.json",
"./dist/abis/IDropERC721.json": "./dist/abis/IDropERC721.json",
"./dist/abis/IMarketplace.json": "./dist/abis/IMarketplace.json",
"./dist/abis/IThirdwebContract.json": "./dist/abis/IThirdwebContract.json",
"./dist/abis/LazyMint_V1.json": "./dist/abis/LazyMint_V1.json",
"./dist/abis/PrimarySaleStorage.json": "./dist/abis/PrimarySaleStorage.json",
"./dist/abis/IClaimConditionMultiPhase.json": "./dist/abis/IClaimConditionMultiPhase.json",
"./dist/abis/ReentrancyGuardStorage.json": "./dist/abis/ReentrancyGuardStorage.json",
"./dist/abis/LoyaltyPoints.json": "./dist/abis/LoyaltyPoints.json",
"./dist/abis/IBurnableERC20.json": "./dist/abis/IBurnableERC20.json",
"./dist/abis/Initializable.json": "./dist/abis/Initializable.json",
"./dist/abis/IAirdropERC20Claimable.json": "./dist/abis/IAirdropERC20Claimable.json",
"./dist/abis/AirdropERC1155.json": "./dist/abis/AirdropERC1155.json",
"./dist/abis/ERC721AUpgradeable.json": "./dist/abis/ERC721AUpgradeable.json",
"./dist/abis/RouterStorage.json": "./dist/abis/RouterStorage.json",
"./dist/abis/ILoyaltyPoints.json": "./dist/abis/ILoyaltyPoints.json",
"./dist/abis/TokenERC20.json": "./dist/abis/TokenERC20.json",
"./dist/abis/AirdropERC20Claimable.json": "./dist/abis/AirdropERC20Claimable.json",
"./dist/abis/SignatureMintERC20.json": "./dist/abis/SignatureMintERC20.json",
"./dist/abis/Staking1155Upgradeable.json": "./dist/abis/Staking1155Upgradeable.json",
"./dist/abis/IEntryPoint.json": "./dist/abis/IEntryPoint.json",
"./dist/abis/EnglishAuctionsStorage.json": "./dist/abis/EnglishAuctionsStorage.json",
"./dist/abis/IDropERC721_V3.json": "./dist/abis/IDropERC721_V3.json",
"./dist/abis/EIP712.json": "./dist/abis/EIP712.json",
"./dist/abis/DelayedReveal.json": "./dist/abis/DelayedReveal.json",
"./dist/abis/IAccount.json": "./dist/abis/IAccount.json",
"./dist/abis/OffersStorage.json": "./dist/abis/OffersStorage.json",
"./dist/abis/IClaimableERC721.json": "./dist/abis/IClaimableERC721.json",
"./dist/abis/IBurnableERC1155.json": "./dist/abis/IBurnableERC1155.json",
"./dist/abis/ContractMetadataInit.json": "./dist/abis/ContractMetadataInit.json",
"./dist/abis/ERC165.json": "./dist/abis/ERC165.json",
"./dist/abis/RouterImmutable.json": "./dist/abis/RouterImmutable.json",
"./dist/abis/IStaking20.json": "./dist/abis/IStaking20.json",
"./dist/abis/IERC721Supply.json": "./dist/abis/IERC721Supply.json",
"./dist/abis/ITokenERC721.json": "./dist/abis/ITokenERC721.json",
"./dist/abis/ERC2771ContextUpgradeable.json": "./dist/abis/ERC2771ContextUpgradeable.json",
"./dist/abis/IDropClaimCondition_V2.json": "./dist/abis/IDropClaimCondition_V2.json",
"./dist/abis/Upgradeable.json": "./dist/abis/Upgradeable.json",
"./dist/abis/DynamicAccount.json": "./dist/abis/DynamicAccount.json",
"./dist/abis/LoyaltyCard.json": "./dist/abis/LoyaltyCard.json",
"./dist/abis/IClaimCondition.json": "./dist/abis/IClaimCondition.json",
"./dist/abis/IStaking721.json": "./dist/abis/IStaking721.json",
"./dist/abis/IEnglishAuctions.json": "./dist/abis/IEnglishAuctions.json",
"./dist/abis/TWMultichainRegistry.json": "./dist/abis/TWMultichainRegistry.json",
"./dist/abis/StakeManager.json": "./dist/abis/StakeManager.json",
"./dist/abis/BurnToClaimDropERC721.json": "./dist/abis/BurnToClaimDropERC721.json",
"./dist/abis/ERC20SignatureMint.json": "./dist/abis/ERC20SignatureMint.json",
"./dist/abis/ISignatureMintERC721_V1.json": "./dist/abis/ISignatureMintERC721_V1.json",
"./dist/abis/ERC20DropVote.json": "./dist/abis/ERC20DropVote.json",
"./dist/abis/ERC721AStorage.json": "./dist/abis/ERC721AStorage.json",
"./dist/abis/BaseAccountFactory.json": "./dist/abis/BaseAccountFactory.json",
"./dist/abis/ERC1155PresetUpgradeable.json": "./dist/abis/ERC1155PresetUpgradeable.json",
"./dist/abis/PlatformFeeImpl.json": "./dist/abis/PlatformFeeImpl.json",
"./dist/abis/SafeERC20.json": "./dist/abis/SafeERC20.json",
"./dist/abis/IAirdropERC1155.json": "./dist/abis/IAirdropERC1155.json",
"./dist/abis/OffersLogic.json": "./dist/abis/OffersLogic.json",
"./dist/abis/Marketplace.json": "./dist/abis/Marketplace.json",
"./dist/abis/TWStrings.json": "./dist/abis/TWStrings.json",
"./dist/abis/DirectListingsStorage.json": "./dist/abis/DirectListingsStorage.json",
"./dist/abis/TieredDrop.json": "./dist/abis/TieredDrop.json",
"./dist/abis/ERC1155Receiver.json": "./dist/abis/ERC1155Receiver.json",
"./dist/abis/AccountCoreStorage.json": "./dist/abis/AccountCoreStorage.json",
"./dist/abis/Drop1155.json": "./dist/abis/Drop1155.json",
"./dist/abis/IDelayedRevealDeprecated.json": "./dist/abis/IDelayedRevealDeprecated.json",
"./dist/abis/DropERC1155.json": "./dist/abis/DropERC1155.json",
"./dist/abis/PermissionsInit.json": "./dist/abis/PermissionsInit.json",
"./dist/abis/Account.json": "./dist/abis/Account.json",
"./dist/abis/ERC20Base.json": "./dist/abis/ERC20Base.json",
"./dist/abis/INFTStake.json": "./dist/abis/INFTStake.json",
"./dist/abis/BytesLib.json": "./dist/abis/BytesLib.json",
"./dist/abis/LazyMintStorage.json": "./dist/abis/LazyMintStorage.json",
"./dist/abis/IDropERC20_V2.json": "./dist/abis/IDropERC20_V2.json",
"./dist/abis/ERC2771ContextUpgradeableStorage.json": "./dist/abis/ERC2771ContextUpgradeableStorage.json",
"./dist/abis/IClaimableERC1155.json": "./dist/abis/IClaimableERC1155.json",
"./dist/abis/ILazyMint.json": "./dist/abis/ILazyMint.json",
"./dist/abis/IOperatorFilterToggle.json": "./dist/abis/IOperatorFilterToggle.json",
"./dist/abis/IERC1155Metadata.json": "./dist/abis/IERC1155Metadata.json",
"./dist/abis/IBeacon.json": "./dist/abis/IBeacon.json",
"./dist/abis/BaseAccount.json": "./dist/abis/BaseAccount.json",
"./dist/abis/ITokenBundle.json": "./dist/abis/ITokenBundle.json",
"./dist/abis/ReentrancyGuard.json": "./dist/abis/ReentrancyGuard.json",
"./dist/abis/AirdropERC20.json": "./dist/abis/AirdropERC20.json",
"./dist/abis/ERC2771ContextStorage.json": "./dist/abis/ERC2771ContextStorage.json",
"./dist/abis/SignatureMintERC1155Upgradeable.json": "./dist/abis/SignatureMintERC1155Upgradeable.json",
"./dist/abis/IPackVRFDirect.json": "./dist/abis/IPackVRFDirect.json",
"./dist/abis/MetaTx.json": "./dist/abis/MetaTx.json",
"./dist/abis/IDropERC20.json": "./dist/abis/IDropERC20.json",
"./dist/abis/ERC721A__Initializable.json": "./dist/abis/ERC721A__Initializable.json",
"./dist/abis/TokenBoundAccount.json": "./dist/abis/TokenBoundAccount.json",
"./dist/abis/ISharedMetadata.json": "./dist/abis/ISharedMetadata.json",
"./dist/abis/IERC721Metadata.json": "./dist/abis/IERC721Metadata.json",
"./dist/abis/DropSinglePhase_V1.json": "./dist/abis/DropSinglePhase_V1.json",
"./dist/abis/PermissionsEnumerable.json": "./dist/abis/PermissionsEnumerable.json",
"./dist/abis/IAccountFactoryCore.json": "./dist/abis/IAccountFactoryCore.json",
"./dist/abis/TokenStake.json": "./dist/abis/TokenStake.json",
"./dist/abis/AppURI.json": "./dist/abis/AppURI.json",
"./dist/abis/AirdropERC721Claimable.json": "./dist/abis/AirdropERC721Claimable.json",
"./dist/abis/DropSinglePhase.json": "./dist/abis/DropSinglePhase.json",
"./dist/abis/OperatorFiltererUpgradeable.json": "./dist/abis/OperatorFiltererUpgradeable.json",
"./dist/abis/IAirdropERC721Claimable.json": "./dist/abis/IAirdropERC721Claimable.json",
"./dist/abis/IOperatorFilterRegistry.json": "./dist/abis/IOperatorFilterRegistry.json",
"./dist/abis/IContractFactory.json": "./dist/abis/IContractFactory.json",
"./dist/abis/ERC20Drop.json": "./dist/abis/ERC20Drop.json",
"./dist/abis/LazyMintWithTier.json": "./dist/abis/LazyMintWithTier.json",
"./dist/abis/ManagedAccount.json": "./dist/abis/ManagedAccount.json",
"./dist/abis/EIP712ChainlessDomain.json": "./dist/abis/EIP712ChainlessDomain.json",
"./dist/abis/IDropERC1155.json": "./dist/abis/IDropERC1155.json",
"./dist/abis/IPermissionsEnumerable.json": "./dist/abis/IPermissionsEnumerable.json",
"./dist/abis/SignatureMintERC1155.json": "./dist/abis/SignatureMintERC1155.json",
"./dist/abis/ForwarderConsumer.json": "./dist/abis/ForwarderConsumer.json",
"./dist/abis/IAggregator.json": "./dist/abis/IAggregator.json",
"./dist/abis/PermissionsEnumerableStorage.json": "./dist/abis/PermissionsEnumerableStorage.json",
"./dist/abis/IEditionStake.json": "./dist/abis/IEditionStake.json",
"./dist/abis/ERC1155Drop.json": "./dist/abis/ERC1155Drop.json",
"./dist/abis/DropStorage.json": "./dist/abis/DropStorage.json",
"./dist/abis/PackVRFDirect.json": "./dist/abis/PackVRFDirect.json",
"./dist/abis/AccountExtension.json": "./dist/abis/AccountExtension.json",
"./dist/abis/SoulboundERC721A.json": "./dist/abis/SoulboundERC721A.json",
"./dist/abis/ISignatureAction.json": "./dist/abis/ISignatureAction.json",
"./dist/abis/ERC721AInit.json": "./dist/abis/ERC721AInit.json",
"./dist/abis/IRoyalty.json": "./dist/abis/IRoyalty.json",
"./dist/abis/IAccountPermissions_V1.json": "./dist/abis/IAccountPermissions_V1.json",
"./dist/abis/CoreRouter.json": "./dist/abis/CoreRouter.json",
"./dist/abis/ContractMetadataLogic.json": "./dist/abis/ContractMetadataLogic.json",
"./dist/abis/ERC1155LazyMint.json": "./dist/abis/ERC1155LazyMint.json",
"./dist/abis/TokenERC1155.json": "./dist/abis/TokenERC1155.json",
"./dist/abis/IAccountPermissions.json": "./dist/abis/IAccountPermissions.json",
"./dist/abis/Staking20.json": "./dist/abis/Staking20.json",
"./dist/abis/RoyaltyStorage.json": "./dist/abis/RoyaltyStorage.json",
"./dist/abis/ERC6551BytecodeLib.json": "./dist/abis/ERC6551BytecodeLib.json",
"./dist/abis/TWMultichainRegistryStorage.json": "./dist/abis/TWMultichainRegistryStorage.json",
"./dist/abis/ForwarderEOAOnly.json": "./dist/abis/ForwarderEOAOnly.json",
"./dist/abis/TokenStore.json": "./dist/abis/TokenStore.json",
"./dist/abis/TokenBundle.json": "./dist/abis/TokenBundle.json",
"./dist/abis/IPaymaster.json": "./dist/abis/IPaymaster.json",
"./dist/abis/RoyaltyInit.json": "./dist/abis/RoyaltyInit.json",
"./dist/abis/SharedMetadata.json": "./dist/abis/SharedMetadata.json",
"./dist/abis/ManagedAccountFactory.json": "./dist/abis/ManagedAccountFactory.json",
"./dist/abis/IFeeTierPlacementExtension.json": "./dist/abis/IFeeTierPlacementExtension.json",
"./dist/abis/TokenCallbackHandler.json": "./dist/abis/TokenCallbackHandler.json",
"./dist/abis/PaymentSplitterUpgradeable.json": "./dist/abis/PaymentSplitterUpgradeable.json",
"./dist/abis/ERC1155Base.json": "./dist/abis/ERC1155Base.json",
"./dist/abis/ForwarderChainlessDomain.json": "./dist/abis/ForwarderChainlessDomain.json",
"./dist/abis/ERC1967Upgrade.json": "./dist/abis/ERC1967Upgrade.json",
"./dist/abis/ExtensionManager.json": "./dist/abis/ExtensionManager.json",
"./dist/abis/NonceManager.json": "./dist/abis/NonceManager.json",
"./dist/abis/IAirdropERC1155Claimable.json": "./dist/abis/IAirdropERC1155Claimable.json",
"./dist/abis/IVotes.json": "./dist/abis/IVotes.json",
"./dist/abis/PluginMap.json": "./dist/abis/PluginMap.json",
"./dist/abis/PermissionsEnumerableLogic.json": "./dist/abis/PermissionsEnumerableLogic.json",
"./dist/abis/IContractMetadata.json": "./dist/abis/IContractMetadata.json",
"./dist/abis/IDropClaimCondition.json": "./dist/abis/IDropClaimCondition.json",
"./dist/abis/SignatureMintERC20Upgradeable.json": "./dist/abis/SignatureMintERC20Upgradeable.json",
"./dist/abis/DynamicAccountFactory.json": "./dist/abis/DynamicAccountFactory.json",
"./dist/abis/OwnableInit.json": "./dist/abis/OwnableInit.json",
"./dist/abis/VoteERC20.json": "./dist/abis/VoteERC20.json",
"./dist/abis/IMultiwrap.json": "./dist/abis/IMultiwrap.json",
"./dist/abis/BurnToClaimDrop721Storage.json": "./dist/abis/BurnToClaimDrop721Storage.json",
"./dist/abis/IBurnableERC721.json": "./dist/abis/IBurnableERC721.json",
"./dist/abis/AccountPermissionsStorage.json": "./dist/abis/AccountPermissionsStorage.json",
"./dist/abis/FeeType.json": "./dist/abis/FeeType.json",
"./dist/abis/Split.json": "./dist/abis/Split.json",
"./dist/abis/IERC20Metadata.json": "./dist/abis/IERC20Metadata.json",
"./dist/abis/EditionStake.json": "./dist/abis/EditionStake.json",
"./dist/abis/ERC721A__InitializableStorage.json": "./dist/abis/ERC721A__InitializableStorage.json",
"./dist/abis/TWMultichainRegistryLogic.json": "./dist/abis/TWMultichainRegistryLogic.json",
"./dist/abis/IERC1155Enumerable.json": "./dist/abis/IERC1155Enumerable.json",
"./dist/abis/SignatureMintERC721.json": "./dist/abis/SignatureMintERC721.json",
"./dist/abis/IERC1155Supply.json": "./dist/abis/IERC1155Supply.json",
"./dist/abis/AirdropERC1155Claimable.json": "./dist/abis/AirdropERC1155Claimable.json",
"./dist/abis/ISignatureMintERC20.json": "./dist/abis/ISignatureMintERC20.json",
"./dist/abis/ERC1155.json": "./dist/abis/ERC1155.json",
"./dist/abis/IRulesEngine.json": "./dist/abis/IRulesEngine.json",
"./dist/abis/Router.json": "./dist/abis/Router.json",
"./dist/abis/IPlatformFee.json": "./dist/abis/IPlatformFee.json",
"./dist/abis/Base64.json": "./dist/abis/Base64.json",
"./dist/abis/ProxyForUpgradeable.json": "./dist/abis/ProxyForUpgradeable.json",
"./dist/abis/Helpers.json": "./dist/abis/Helpers.json",
"./dist/abis/VRFV2WrapperConsumerBase.json": "./dist/abis/VRFV2WrapperConsumerBase.json",
"./dist/abis/ITokenERC1155.json": "./dist/abis/ITokenERC1155.json",
"./dist/abis/BatchMintMetadata.json": "./dist/abis/BatchMintMetadata.json",
"./dist/abis/NFTMetadata.json": "./dist/abis/NFTMetadata.json",
"./dist/abis/ERC20Permit.json": "./dist/abis/ERC20Permit.json",
"./dist/abis/IAppURI.json": "./dist/abis/IAppURI.json",
"./dist/abis/ERC1155DelayedReveal.json": "./dist/abis/ERC1155DelayedReveal.json",
"./dist/abis/BatchMintMetadata_V1.json": "./dist/abis/BatchMintMetadata_V1.json",
"./dist/abis/ContractPublisher.json": "./dist/abis/ContractPublisher.json",
"./dist/abis/Drop.json": "./dist/abis/Drop.json",
"./dist/abis/MockContractPublisher.json": "./dist/abis/MockContractPublisher.json",
"./dist/abis/ERC721Holder.json": "./dist/abis/ERC721Holder.json",
"./dist/abis/IWETH.json": "./dist/abis/IWETH.json",
"./dist/abis/ERC1155SignatureMint.json": "./dist/abis/ERC1155SignatureMint.json",
"./dist/abis/TWProxy.json": "./dist/abis/TWProxy.json",
"./dist/abis/CurrencyTransferLib.json": "./dist/abis/CurrencyTransferLib.json",
"./dist/abis/MinimalForwarderEOAOnly.json": "./dist/abis/MinimalForwarderEOAOnly.json",
"./dist/abis/PermissionsEnumerableInit.json": "./dist/abis/PermissionsEnumerableInit.json",
"./dist/abis/INonceManager.json": "./dist/abis/INonceManager.json",
"./dist/abis/IERC721.json": "./dist/abis/IERC721.json",
"./dist/abis/IERC721AQueryableUpgradeable.json": "./dist/abis/IERC721AQueryableUpgradeable.json",
"./dist/abis/DropERC1155_V2.json": "./dist/abis/DropERC1155_V2.json",
"./dist/abis/PermissionsStorage.json": "./dist/abis/PermissionsStorage.json",
"./dist/abis/IDrop1155.json": "./dist/abis/IDrop1155.json",
"./dist/abis/IERC721Enumerable.json": "./dist/abis/IERC721Enumerable.json",
"./dist/abis/IDropSinglePhase1155_V1.json": "./dist/abis/IDropSinglePhase1155_V1.json",
"./dist/abis/ERC1271.json": "./dist/abis/ERC1271.json",
"./dist/abis/DropSinglePhase1155_V1.json": "./dist/abis/DropSinglePhase1155_V1.json",
"./dist/abis/Ownable.json": "./dist/abis/Ownable.json",
"./dist/abis/ERC721Base.json": "./dist/abis/ERC721Base.json",
"./dist/abis/OperatorFilterToggleStorage.json": "./dist/abis/OperatorFilterToggleStorage.json",
"./dist/abis/NFTStake.json": "./dist/abis/NFTStake.json",
"./dist/abis/ContractMetadataStorage.json": "./dist/abis/ContractMetadataStorage.json",
"./dist/abis/DropERC20_V2.json": "./dist/abis/DropERC20_V2.json",
"./dist/abis/PlatformFee.json": "./dist/abis/PlatformFee.json",
"./dist/abis/IERC20.json": "./dist/abis/IERC20.json",
"./dist/abis/Staking721Base.json": "./dist/abis/Staking721Base.json",
"./dist/abis/RulesEngine.json": "./dist/abis/RulesEngine.json",
"./dist/abis/LazyMint.json": "./dist/abis/LazyMint.json",
"./dist/abis/BurnToClaimStorage.json": "./dist/abis/BurnToClaimStorage.json",
"./dist/abis/Multicall.json": "./dist/abis/Multicall.json",
"./dist/abis/PlatformFeeInit.json": "./dist/abis/PlatformFeeInit.json",
"./dist/abis/TWMultichainRegistryRouter.json": "./dist/abis/TWMultichainRegistryRouter.json",
"./dist/abis/OwnableStorage.json": "./dist/abis/OwnableStorage.json",
"./dist/abis/IERC1822Proxiable.json": "./dist/abis/IERC1822Proxiable.json",
"./dist/abis/ERC20Vote.json": "./dist/abis/ERC20Vote.json",
"./dist/abis/IAirdropERC20.json": "./dist/abis/IAirdropERC20.json",
"./dist/abis/DirectListingsLogic.json": "./dist/abis/DirectListingsLogic.json",
"./dist/abis/IERC20Permit.json": "./dist/abis/IERC20Permit.json",
"./dist/abis/INFTMetadata.json": "./dist/abis/INFTMetadata.json",
"./dist/abis/IDrop.json": "./dist/abis/IDrop.json",
"./dist/abis/ISignatureMintERC721.json": "./dist/abis/ISignatureMintERC721.json",
"./dist/abis/RulesEngineStorage.json": "./dist/abis/RulesEngineStorage.json",
"./dist/abis/ERC20SignatureMintVote.json": "./dist/abis/ERC20SignatureMintVote.json",
"./dist/abis/BatchMintMetadataStorage.json": "./dist/abis/BatchMintMetadataStorage.json",
"./dist/abis/ITokenStake.json": "./dist/abis/ITokenStake.json",
"./dist/abis/ITWMultichainRegistry.json": "./dist/abis/ITWMultichainRegistry.json",
"./dist/abis/SignatureMintERC721Upgradeable.json": "./dist/abis/SignatureMintERC721Upgradeable.json",
"./dist/abis/IERC721A.json": "./dist/abis/IERC721A.json",
"./dist/abis/IDropERC1155_V2.json": "./dist/abis/IDropERC1155_V2.json",
"./dist/abis/IContractDeployer.json": "./dist/abis/IContractDeployer.json",
"./dist/abis/ERC721Drop.json": "./dist/abis/ERC721Drop.json",
"./dist/abis/ERC2771ContextUpgradeableLogic.json": "./dist/abis/ERC2771ContextUpgradeableLogic.json",
"./dist/abis/ContractMetadataImpl.json": "./dist/abis/ContractMetadataImpl.json"
},
"license": "Apache-2.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
@@ -36,14 +449,17 @@
"entrypoints": [
"index.ts",
"./factories/TW*.ts"
]
],
"exports": {
"extra": {}
}
},
"sideEffects": false,
"scripts": {
"clean": "rm -rf dist/",
"typechain": "rm -rf ./src && cp ./external_abis/*.json ./node_modules/@thirdweb-dev/contracts/abi && typechain --target ethers-v5 --out-dir src './node_modules/@thirdweb-dev/contracts/abi/*.json'",
"extract-abi": "node ./scripts/extract-abis.mjs",
"build": "pnpm typechain && tsc && pnpm preconstruct build && pnpm extract-abi",
"extract-abi": "bun ./scripts/extract-abis.ts && bun ./scripts/write-exports.ts",
"build": "bun ./scripts/revert-exports.ts && pnpm typechain && tsc && pnpm preconstruct build && pnpm extract-abi",
"push": "yalc push"
}
}
}
@@ -0,0 +1,15 @@
import { readFile, writeFile } from "node:fs/promises";
const pkgJson = JSON.parse(await readFile('./package.json', "utf-8"));
// for each path add to the `exports` field in `package.json`
const pkgExports = pkgJson.exports || {};
Object.keys(pkgExports).forEach(key => {
if(key.endsWith(".json") && key !== './package.json'){
delete pkgExports[key];
}
})
pkgJson.exports = pkgExports;
// write the updated `package.json` to disk
await writeFile('./package.json', JSON.stringify(pkgJson, null, 2), "utf-8");
@@ -0,0 +1,28 @@
import {resolve} from "node:path";
import { readdir, readFile, writeFile } from "node:fs/promises";
// enumerate all files in `dist/abis/*.json`
async function getPaths() {
const dir = resolve(__dirname, '../dist/abis');
const paths = await readdir(dir);
const jsonPaths = paths.filter(p => p.endsWith('.json'));
return jsonPaths.map(p => `./dist/abis/${p}`);
}
const [paths, pkg] = await Promise.all([getPaths(), readFile('./package.json', "utf-8")]);
const pkgJson = JSON.parse(pkg);
// for each path add to the `exports` field in `package.json`
const pkgExports = pkgJson.exports || {};
for(const path of paths){
pkgExports[path] = path;
}
pkgJson.exports = pkgExports;
// write the updated `package.json` to disk
await writeFile('./package.json', JSON.stringify(pkgJson, null, 2), "utf-8");
+1 -1
View File
@@ -1,5 +1,5 @@
{
"extends": "@thirdweb-dev/tsconfig/base.json",
"extends": "@thirdweb-dev/tsconfig/sdk.json",
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}
+6
View File
@@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-typescript",
["@babel/preset-env", { "targets": "defaults, not ie 11" }]
]
}
+4
View File
@@ -0,0 +1,4 @@
# folders
dist/
# files
+24
View File
@@ -0,0 +1,24 @@
module.exports = {
root: true,
extends: ["thirdweb"],
rules: {
"no-restricted-globals": [
"error",
{
name: "Buffer",
message: "Use Uint8Array instead.",
},
],
"no-restricted-imports": [
"error",
{
name: "buffer",
message: "Use Uint8Array instead.",
},
{
name: "node:buffer",
message: "Use Uint8Array instead.",
},
],
},
};
+7
View File
@@ -0,0 +1,7 @@
# @thirdweb-dev/crypto
## 0.2.0
### Minor Changes
- [#1861](https://github.com/thirdweb-dev/js/pull/1861) [`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4) Thanks [@jnsdls](https://github.com/jnsdls)! - initial release
+94
View File
@@ -0,0 +1,94 @@
<p align="center">
<br />
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/packages/sdk/logo.svg?raw=true" width="200" alt=""/></a>
<br />
</p>
<h1 align="center">thirdweb crypto</h1>
<p align="center">
<a href="https://www.npmjs.com/package/@thirdweb-dev/crypto"><img src="https://img.shields.io/npm/v/@thirdweb-dev/crypto?color=red&label=npm&logo=npm" alt="npm version"/></a>
<a href="https://github.com/thirdweb-dev/js/actions/workflows/build-test-lint.yml"><img alt="Build Status" src="https://github.com/thirdweb-dev/js/actions/workflows/build-test-lint.yml/badge.svg"/></a>
<a href="https://discord.gg/thirdweb"><img alt="Join our Discord!" src="https://img.shields.io/discord/834227967404146718.svg?color=7289da&label=discord&logo=discord&style=flat"/></a>
</p>
<p align="center"><strong>Secure, performant and cross-platform cryptography methods commonly used across other thirdweb packages.</strong></p>
<br />
## Installation
```bash
npm install @thirdweb-dev/crypto
```
## Available methods
### Encryption
| method | use case | return type | underlying technology |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ----------------- | --------------------- |
| `aesEncrypt(<plaintext>, <password>)` | Encrypts a string/Uint8Array with AES-GCM | `Promise<string>` | `WebCrypto` (AES-GCM) |
| `aesDecrypt(<ciphertext>, <password>)` | Decrypts a value encrypted via `aesEncrypt` | `Promise<string>` | `WebCrypto` (AES-GCM) |
| `aesDecryptCompat(<ciphertext>, <password>)` | Same as `aesDecrypt` but also supports values encrypted via `crypto-js` (for backwards compatibility) | `Promise<string>` | `WebCrypto` (AES-GCM) |
### Hashing
| method | use case | return type | underlying technology |
| ----------------------------------- | --------------------------------------------------------------- | --------------------- | --------------------- |
| `sha256(<value>)` | Hashes a string/Uint8Array with SHA256 | `Promise<Uint8Array>` | `WebCrypto` |
| `sha256Hex(<value>)` | Same as `sha256` but reruns as hex string | `Promise<string>` | `WebCrypto` |
| `sha256Sync(<value>)` | Same as `sha256` but synchronous | `Uint8Array` | `@noble/hashes` |
| `sha256SyncHex(<value>)` | Same as `sha256Sync` but reruns as hex string | `string` | `@noble/hashes` |
| `keccak256Sync(<value>)` | Hashes a string/Uint8Array with Keccak256 | `Uint8Array` | `js-sha3` |
| `keccak256SyncHex(<value>)` | Same as `keccak256Sync` but returns as hex string | `string` | `js-sha3` |
| `keccak256SyncHexPrefixed(<value>)` | Same as `keccak256Sync` but returns as `0x` prefixed hex string | `0x${string}` | `js-sha3` |
## Initial Benchmarks
#### sha256 -> Uint8Array
```bash
┌──────────────────────────────┬─────────┬────────────────────┬────────┬─────────┐
│ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │
├──────────────────────────────┼─────────┼────────────────────┼────────┼─────────┤
│ @thirdweb-dev/crypto (async) │ 112,929 │ 8855.08539999982 │ ±1.37% │ 100000 │
│ @thirdweb-dev/crypto (sync) │ 55,004 │ 18180.482820000594 │ ±1.02% │ 100000 │
│ crypto-js (sync) │ 19,213 │ 52047.35353999857 │ ±0.25% │ 100000 │
└──────────────────────────────┴─────────┴────────────────────┴────────┴─────────┘
```
#### sha256 -> hex
```bash
┌──────────────────────────────┬─────────┬────────────────────┬────────┬─────────┐
│ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │
├──────────────────────────────┼─────────┼────────────────────┼────────┼─────────┤
│ @thirdweb-dev/crypto (async) │ 100,845 │ 9916.125730001713 │ ±1.24% │ 100000 │
│ @thirdweb-dev/crypto (sync) │ 53,751 │ 18604.047449997142 │ ±0.92% │ 100000 │
│ crypto-js (sync) │ 19,077 │ 52419.03465000334 │ ±0.24% │ 100000 │
└──────────────────────────────┴─────────┴────────────────────┴────────┴─────────┘
```
#### keccack256 -> Uint8Array
```bash
┌──────────────────────┬─────────┬────────────────────┬────────┬─────────┐
│ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │
├──────────────────────┼─────────┼────────────────────┼────────┼─────────┤
│ @thirdweb-dev/crypto │ 72,533 │ 13786.719900000808 │ ±0.31% │ 100000 │
│ ethers@v5 │ 71,891 │ 13909.94663000107 │ ±0.33% │ 100000 │
│ @noble/hashes │ 27,244 │ 36704.73226000136 │ ±0.19% │ 100000 │
│ viem │ 27,115 │ 36879.07953999698 │ ±0.26% │ 100000 │
└──────────────────────┴─────────┴────────────────────┴────────┴─────────┘
```
#### keccack256 -> hex
```bash
┌──────────────────────┬─────────┬───────────────────┬────────┬─────────┐
│ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │
├──────────────────────┼─────────┼───────────────────┼────────┼─────────┤
│ @thirdweb-dev/crypto │ 42,947 │ 23284.22780000266 │ ±0.23% │ 100000 │
│ ethers@5 │ 35,568 │ 28114.98005000045 │ ±0.42% │ 100000 │
│ @noble/hashes │ 15,149 │ 66009.80448000187 │ ±0.26% │ 100000 │
│ viem │ 15,217 │ 65714.15843999518 │ ±0.25% │ 100000 │
└──────────────────────┴─────────┴───────────────────┴────────┴─────────┘
```
+44
View File
@@ -0,0 +1,44 @@
import { Bench } from "tinybench";
import { aesDecrypt, aesDecryptCompat, aesEncrypt } from "../src";
import { AES, enc } from "crypto-js";
// @ts-expect-error - this function actually does exist
import { consoleTable } from "js-awe";
const PLAINTEXT = "my secret text";
const PASSWORD = "pw";
const encrypted = await aesEncrypt(PLAINTEXT, PASSWORD);
const encryptedCryptoJs = AES.encrypt(PLAINTEXT, PASSWORD).toString();
const bench = new Bench({ iterations: 100_000 });
bench
.add("aesDecrypt", async () => {
const res = await aesDecrypt(encrypted, PASSWORD);
if (res !== PLAINTEXT) {
throw new Error("aesDecrypt failed");
}
})
.add("aesDecryptCompat", async () => {
const res = await aesDecryptCompat(encrypted, PASSWORD);
if (res !== PLAINTEXT) {
throw new Error("aesDecryptCompat (compat, modern) failed");
}
})
.add("aesDecryptCompat (crypto-js)", async () => {
const res = await aesDecryptCompat(encryptedCryptoJs, PASSWORD);
if (res !== PLAINTEXT) {
throw new Error("aesDecryptCompat (compat, legacy) failed");
}
})
.add("crypto-js", async () => {
const res = AES.decrypt(encryptedCryptoJs, PASSWORD).toString(enc.Utf8);
if (res !== PLAINTEXT) {
throw new Error("crypto-js failed");
}
});
await bench.warmup();
await bench.run();
consoleTable(bench.table());
+23
View File
@@ -0,0 +1,23 @@
import { Bench } from "tinybench";
import { aesEncrypt } from "../src";
import { AES } from "crypto-js";
// @ts-expect-error - this function actually does exist
import { consoleTable } from "js-awe";
const PLAINTEXT = "my secret text";
const PASSWORD = "pw";
const bench = new Bench({ iterations: 100_000 });
bench
.add("aesEncrypt", async () => {
await aesEncrypt(PLAINTEXT, PASSWORD);
})
.add("crypto-js", () => {
AES.encrypt(PLAINTEXT, PASSWORD).toString();
});
await bench.warmup();
await bench.run();
consoleTable(bench.table());
+63
View File
@@ -0,0 +1,63 @@
import { Bench } from "tinybench";
import { keccak256SyncHex } from "../src";
import { utils } from "ethers";
import {
uint8ArrayToString,
uint8ArrayToHex,
} from "../src/utils/uint8array-extras";
import { keccak_256 as noble } from "@noble/hashes/sha3";
import { keccak256 as viem } from "viem";
// @ts-expect-error - this function actually does exist
import { consoleTable } from "js-awe";
const value = crypto.getRandomValues(new Uint8Array(1000));
const stringValue = uint8ArrayToString(value);
const uint8Bench = new Bench({ iterations: 100_000 });
uint8Bench
.add("@thirdweb-dev/crypto", async () => {
keccak256SyncHex(value);
})
.add("ethers@v5", async () => {
utils.keccak256(value);
})
.add("@noble/hashes", async () => {
uint8ArrayToHex(noble(value));
})
.add("viem", async () => {
viem(value);
});
await uint8Bench.warmup();
await uint8Bench.run();
console.log();
console.log("keccak256(<Uint8Array>):");
consoleTable(uint8Bench.table());
console.log();
const stringBench = new Bench({ iterations: 100_000 });
stringBench
.add("@thirdweb-dev/crypto", async () => {
keccak256SyncHex(stringValue);
})
.add("ethers@5", async () => {
// OK because this is benchmark code only
// eslint-disable-next-line no-restricted-globals
utils.keccak256(Buffer.from(stringValue));
})
.add("@noble/hashes", async () => {
uint8ArrayToHex(noble(stringValue));
})
.add("viem", async () => {
viem(stringValue as `0x${string}`);
});
await stringBench.warmup();
await stringBench.run();
console.log("keccak256(<string>):");
consoleTable(stringBench.table());
console.log();
+51
View File
@@ -0,0 +1,51 @@
import Bench from "tinybench";
import { sha256, sha256Hex, sha256Sync, sha256HexSync } from "../src";
import { SHA256, enc } from "crypto-js";
import { uint8ArrayToString } from "../src/utils/uint8array-extras";
// @ts-expect-error - this function actually does exist
import { consoleTable } from "js-awe";
const STRING_TO_HASH = uint8ArrayToString(
crypto.getRandomValues(new Uint8Array(1000)),
);
const sha256Bench = new Bench({ iterations: 100_000 });
sha256Bench
.add("@thirdweb-dev/crypto (async)", async () => {
await sha256(STRING_TO_HASH);
})
.add("@thirdweb-dev/crypto (sync)", async () => {
sha256Sync(STRING_TO_HASH);
})
.add("crypto-js (sync)", async () => {
SHA256(STRING_TO_HASH).toString();
});
await sha256Bench.warmup();
await sha256Bench.run();
console.log();
console.log("sha256:");
consoleTable(sha256Bench.table());
console.log();
const sha256HexBench = new Bench({ iterations: 100_000 });
sha256HexBench
.add("@thirdweb-dev/crypto (async)", async () => {
await sha256Hex(STRING_TO_HASH);
})
.add("@thirdweb-dev/crypto (sync)", async () => {
sha256HexSync(STRING_TO_HASH);
})
.add("crypto-js (sync)", async () => {
SHA256(STRING_TO_HASH).toString(enc.Hex);
});
await sha256HexBench.warmup();
await sha256HexBench.run();
console.log("sha256 -> hex:");
consoleTable(sha256HexBench.table());
console.log();
+62
View File
@@ -0,0 +1,62 @@
{
"name": "@thirdweb-dev/crypto",
"version": "0.2.0",
"main": "dist/thirdweb-dev-crypto.cjs.js",
"module": "dist/thirdweb-dev-crypto.esm.js",
"exports": {
".": {
"module": "./dist/thirdweb-dev-crypto.esm.js",
"default": "./dist/thirdweb-dev-crypto.cjs.js"
},
"./package.json": "./package.json"
},
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/utils",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/thirdweb-dev/js/issues"
},
"author": "thirdweb eng <[email protected]>",
"files": [
"dist/"
],
"preconstruct": {
"entrypoints": [
"index.ts"
],
"exports": true
},
"sideEffects": false,
"dependencies": {
"@noble/hashes": "^1.3.2",
"js-sha3": "^0.9.2"
},
"devDependencies": {
"@preconstruct/cli": "2.7.0",
"@thirdweb-dev/tsconfig": "workspace:*",
"@types/crypto-js": "^4.1.3",
"crypto-js": "^4.2.0",
"eslint-config-thirdweb": "workspace:*",
"ethers": "^5.7.2",
"js-awe": "^1.0.62",
"tinybench": "^2.5.1",
"typescript": "^5.1.6",
"viem": "^1.19.1"
},
"scripts": {
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/ && bunx publint --strict --level warning",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"build": "tsc && preconstruct build",
"push": "yalc push",
"test": "bun test",
"benchmark:aes-encrypt": "bun benchmark/aes-encrypt.ts",
"benchmark:aes-decrypt": "bun benchmark/aes-decrypt.ts",
"benchmark:sha256": "bun benchmark/sha256.ts",
"benchmark:keccak256": "bun benchmark/keccak256.ts",
"benchmark": "bun run benchmark:aes-encrypt && bun run benchmark:aes-decrypt && bun run benchmark:sha256 && bun run benchmark:keccak256"
},
"engines": {
"node": ">=18"
}
}
+80
View File
@@ -0,0 +1,80 @@
import { getCachedTextDecoder, getCachedTextEncoder } from "../utils/cache";
import {
decryptCryptoJSCipherBase64,
parseCryptoJSCipherBase64,
} from "./utils/crypto-js-compat";
import { base64ToUint8Array } from "../utils/uint8array-extras";
import { universalCrypto } from "../utils/universal-crypto";
/**
* Decrypts ciphertext encrypted with aesEncrypt() using supplied password.
*
* @param ciphertext - Ciphertext to be decrypted.
* @param password - Password to use to decrypt ciphertext.
* @returns Decrypted plaintext.
*
* @example
* const plaintext = await aesDecrypt(ciphertext, 'pw');
*/
export async function aesDecrypt(
ciphertext: string,
password: string,
): Promise<string> {
const crypto = await universalCrypto();
// encode password as UTF-8
const pwUtf8 = getCachedTextEncoder().encode(password);
// hash the password
const pwHash = await crypto.subtle.digest("SHA-256", pwUtf8);
const cipherUint8Array = base64ToUint8Array(ciphertext);
// iv
const iv = cipherUint8Array.slice(0, 12);
// specify algorithm to use
const alg = { name: "AES-GCM", iv };
// generate key from pw
const key = await crypto.subtle.importKey("raw", pwHash, alg, false, [
"decrypt",
]);
// ciphertext
const ctUint8 = cipherUint8Array.slice(12);
try {
// decrypt ciphertext using key
const plainBuffer = await crypto.subtle.decrypt(alg, key, ctUint8);
// return the plaintext from ArrayBuffer
return getCachedTextDecoder().decode(plainBuffer);
} catch (e) {
throw new Error("Decrypt failed");
}
}
/**
* Decrypts ciphertext encrypted with aesEncrypt() OR "crypto-js".AES using supplied password.
*
* @param ciphertext - Ciphertext to be decrypted.
* @param password - Password to use to decrypt ciphertext.
* @returns Decrypted plaintext.
*
* @example
* const plaintext = await aesDecryptCompat(ciphertext, 'pw');
*/
export async function aesDecryptCompat(
ciphertext: string,
password: string,
): Promise<string> {
// determine if we're dealing with a legacy (cryptojs) ciphertext
const cryptoJs = parseCryptoJSCipherBase64(ciphertext);
if (cryptoJs.salt && cryptoJs.ciphertext) {
return decryptCryptoJSCipherBase64(
cryptoJs.salt,
cryptoJs.ciphertext,
password,
);
}
// otherwise assume it's a ciphertext generated by aesEncrypt()
return aesDecrypt(ciphertext, password);
}
+47
View File
@@ -0,0 +1,47 @@
import {
concatUint8Arrays,
uint8ArrayToBase64,
} from "../utils/uint8array-extras";
import { getCachedTextEncoder } from "../utils/cache";
import { universalCrypto } from "../utils/universal-crypto";
/**
* Encrypts plaintext using AES-GCM with supplied password, for decryption with aesDecrypt().
*
* @param plaintext - Plaintext to be encrypted.
* @param password - Password to use to encrypt plaintext.
* @returns Encrypted ciphertext.
*
* @example
* const ciphertext = await aesEncrypt('my secret text', 'pw');
*/
export async function aesEncrypt(
plaintext: string,
password: string,
): Promise<string> {
const crypto = await universalCrypto();
const textEncoder = getCachedTextEncoder();
// encode password as UTF-8
const pwUtf8 = textEncoder.encode(password);
// hash the password
const pwHash = await crypto.subtle.digest("SHA-256", pwUtf8);
// get 96-bit random iv
const iv = crypto.getRandomValues(new Uint8Array(12));
// specify algorithm to use
const alg = { name: "AES-GCM", iv };
// generate key from pw
const key = await crypto.subtle.importKey("raw", pwHash, alg, false, [
"encrypt",
]);
// encode plaintext as UTF-8
const ptUint8 = textEncoder.encode(plaintext);
// encrypt plaintext using key
const ctBuffer = await crypto.subtle.encrypt(alg, key, ptUint8);
// iv+ciphertext base64-encoded
return uint8ArrayToBase64(concatUint8Arrays([iv, new Uint8Array(ctBuffer)]));
}
+415
View File
@@ -0,0 +1,415 @@
// stripped down version of `js-md5`
// changes:
// - we know we always have ArrayBuffer available
// - we only care about `arrayBuffer` output
// - we want to behave the same regardless of NODE or non NODE env
// - transformed into class
// - typescript
/**
* [js-md5]{@link https://github.com/emn178/js-md5}
*
* @namespace md5
* @version 0.8.3
* @author Chen, Yi-Cyuan [[email protected]]
* @copyright Chen, Yi-Cyuan 2014-2023
* @license MIT
*/
const INPUT_ERROR = "input is invalid type";
const FINALIZE_ERROR = "finalize already called";
const EXTRA = [128, 32768, 8388608, -2147483648];
// [message: string, isString: bool]
function formatMessage(
message: string | any[] | ArrayBuffer | ArrayLike<number>,
): [string | Uint8Array | Array<any> | ArrayBufferView, boolean] {
const type = typeof message;
if (typeof message === "string") {
return [message, true];
}
if (type !== "object" || message === null) {
throw new Error(INPUT_ERROR);
}
if (message instanceof ArrayBuffer) {
return [new Uint8Array(message), false];
}
if (!Array.isArray(message) && !ArrayBuffer.isView(message)) {
throw new Error(INPUT_ERROR);
}
return [message, false];
}
/**
* Md5 class
* @class Md5
* @description This is internal class.
* @see {@link md5.create}
*/
class Md5 {
buffer8: Uint8Array;
blocks: Uint32Array;
h0: number;
h1: number;
h2: number;
h3: number;
start: number;
bytes: number;
hBytes: number;
finalized: boolean;
hashed: boolean;
first: boolean;
lastByteIndex: number = 0;
constructor() {
// eslint-disable-next-line @typescript-eslint/no-shadow
const buffer = new ArrayBuffer(68);
this.buffer8 = new Uint8Array(buffer);
this.blocks = new Uint32Array(buffer);
this.h0 =
this.h1 =
this.h2 =
this.h3 =
this.start =
this.bytes =
this.hBytes =
0;
this.finalized = this.hashed = false;
this.first = true;
}
/**
* @method update
* @memberof Md5
* @instance
* @description Update hash
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {Md5} Md5 object.
* @see {@link md5.update}
*/
update(inputMessage: string | any[] | ArrayBuffer | ArrayLike<number>): Md5 {
if (this.finalized) {
throw new Error(FINALIZE_ERROR);
}
const [message, isString] = formatMessage(inputMessage);
const blocks = this.blocks;
let length = 0;
if (ArrayBuffer.isView(message)) {
length = message.byteLength;
} else {
length = message.length;
}
let code,
index = 0,
i;
// eslint-disable-next-line @typescript-eslint/no-shadow
const buffer8 = this.buffer8;
while (index < length) {
if (this.hashed) {
this.hashed = false;
blocks[0] = blocks[16];
blocks[16] =
blocks[1] =
blocks[2] =
blocks[3] =
blocks[4] =
blocks[5] =
blocks[6] =
blocks[7] =
blocks[8] =
blocks[9] =
blocks[10] =
blocks[11] =
blocks[12] =
blocks[13] =
blocks[14] =
blocks[15] =
0;
}
if (isString) {
for (i = this.start; index < length && i < 64; ++index) {
code = (message as string).charCodeAt(index);
if (code < 0x80) {
buffer8[i++] = code;
} else if (code < 0x800) {
buffer8[i++] = 0xc0 | (code >>> 6);
buffer8[i++] = 0x80 | (code & 0x3f);
} else if (code < 0xd800 || code >= 0xe000) {
buffer8[i++] = 0xe0 | (code >>> 12);
buffer8[i++] = 0x80 | ((code >>> 6) & 0x3f);
buffer8[i++] = 0x80 | (code & 0x3f);
} else {
code =
0x10000 +
(((code & 0x3ff) << 10) |
((message as string).charCodeAt(++index) & 0x3ff));
buffer8[i++] = 0xf0 | (code >>> 18);
buffer8[i++] = 0x80 | ((code >>> 12) & 0x3f);
buffer8[i++] = 0x80 | ((code >>> 6) & 0x3f);
buffer8[i++] = 0x80 | (code & 0x3f);
}
}
} else {
for (i = this.start; index < length && i < 64; ++index) {
// at this point we know it's not a string
buffer8[i++] = (message as any)[index];
}
}
this.lastByteIndex = i;
this.bytes += i - this.start;
if (i >= 64) {
this.start = i - 64;
this.hash();
this.hashed = true;
} else {
this.start = i;
}
}
if (this.bytes > 4294967295) {
this.hBytes += (this.bytes / 4294967296) << 0;
this.bytes = this.bytes % 4294967296;
}
return this;
}
finalize() {
if (this.finalized) {
return;
}
this.finalized = true;
// eslint-disable-next-line @typescript-eslint/no-shadow
const blocks = this.blocks,
i = this.lastByteIndex;
blocks[i >>> 2] |= EXTRA[i & 3];
if (i >= 56) {
if (!this.hashed) {
this.hash();
}
blocks[0] = blocks[16];
blocks[16] =
blocks[1] =
blocks[2] =
blocks[3] =
blocks[4] =
blocks[5] =
blocks[6] =
blocks[7] =
blocks[8] =
blocks[9] =
blocks[10] =
blocks[11] =
blocks[12] =
blocks[13] =
blocks[14] =
blocks[15] =
0;
}
blocks[14] = this.bytes << 3;
blocks[15] = (this.hBytes << 3) | (this.bytes >>> 29);
this.hash();
}
hash() {
const blocks = this.blocks;
let a, b, c, d, bc, da;
if (this.first) {
a = blocks[0] - 680876937;
a = (((a << 7) | (a >>> 25)) - 271733879) << 0;
d = (-1732584194 ^ (a & 2004318071)) + blocks[1] - 117830708;
d = (((d << 12) | (d >>> 20)) + a) << 0;
c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2] - 1126478375;
c = (((c << 17) | (c >>> 15)) + d) << 0;
b = (a ^ (c & (d ^ a))) + blocks[3] - 1316259209;
b = (((b << 22) | (b >>> 10)) + c) << 0;
} else {
a = this.h0;
b = this.h1;
c = this.h2;
d = this.h3;
a += (d ^ (b & (c ^ d))) + blocks[0] - 680876936;
a = (((a << 7) | (a >>> 25)) + b) << 0;
d += (c ^ (a & (b ^ c))) + blocks[1] - 389564586;
d = (((d << 12) | (d >>> 20)) + a) << 0;
c += (b ^ (d & (a ^ b))) + blocks[2] + 606105819;
c = (((c << 17) | (c >>> 15)) + d) << 0;
b += (a ^ (c & (d ^ a))) + blocks[3] - 1044525330;
b = (((b << 22) | (b >>> 10)) + c) << 0;
}
a += (d ^ (b & (c ^ d))) + blocks[4] - 176418897;
a = (((a << 7) | (a >>> 25)) + b) << 0;
d += (c ^ (a & (b ^ c))) + blocks[5] + 1200080426;
d = (((d << 12) | (d >>> 20)) + a) << 0;
c += (b ^ (d & (a ^ b))) + blocks[6] - 1473231341;
c = (((c << 17) | (c >>> 15)) + d) << 0;
b += (a ^ (c & (d ^ a))) + blocks[7] - 45705983;
b = (((b << 22) | (b >>> 10)) + c) << 0;
a += (d ^ (b & (c ^ d))) + blocks[8] + 1770035416;
a = (((a << 7) | (a >>> 25)) + b) << 0;
d += (c ^ (a & (b ^ c))) + blocks[9] - 1958414417;
d = (((d << 12) | (d >>> 20)) + a) << 0;
c += (b ^ (d & (a ^ b))) + blocks[10] - 42063;
c = (((c << 17) | (c >>> 15)) + d) << 0;
b += (a ^ (c & (d ^ a))) + blocks[11] - 1990404162;
b = (((b << 22) | (b >>> 10)) + c) << 0;
a += (d ^ (b & (c ^ d))) + blocks[12] + 1804603682;
a = (((a << 7) | (a >>> 25)) + b) << 0;
d += (c ^ (a & (b ^ c))) + blocks[13] - 40341101;
d = (((d << 12) | (d >>> 20)) + a) << 0;
c += (b ^ (d & (a ^ b))) + blocks[14] - 1502002290;
c = (((c << 17) | (c >>> 15)) + d) << 0;
b += (a ^ (c & (d ^ a))) + blocks[15] + 1236535329;
b = (((b << 22) | (b >>> 10)) + c) << 0;
a += (c ^ (d & (b ^ c))) + blocks[1] - 165796510;
a = (((a << 5) | (a >>> 27)) + b) << 0;
d += (b ^ (c & (a ^ b))) + blocks[6] - 1069501632;
d = (((d << 9) | (d >>> 23)) + a) << 0;
c += (a ^ (b & (d ^ a))) + blocks[11] + 643717713;
c = (((c << 14) | (c >>> 18)) + d) << 0;
b += (d ^ (a & (c ^ d))) + blocks[0] - 373897302;
b = (((b << 20) | (b >>> 12)) + c) << 0;
a += (c ^ (d & (b ^ c))) + blocks[5] - 701558691;
a = (((a << 5) | (a >>> 27)) + b) << 0;
d += (b ^ (c & (a ^ b))) + blocks[10] + 38016083;
d = (((d << 9) | (d >>> 23)) + a) << 0;
c += (a ^ (b & (d ^ a))) + blocks[15] - 660478335;
c = (((c << 14) | (c >>> 18)) + d) << 0;
b += (d ^ (a & (c ^ d))) + blocks[4] - 405537848;
b = (((b << 20) | (b >>> 12)) + c) << 0;
a += (c ^ (d & (b ^ c))) + blocks[9] + 568446438;
a = (((a << 5) | (a >>> 27)) + b) << 0;
d += (b ^ (c & (a ^ b))) + blocks[14] - 1019803690;
d = (((d << 9) | (d >>> 23)) + a) << 0;
c += (a ^ (b & (d ^ a))) + blocks[3] - 187363961;
c = (((c << 14) | (c >>> 18)) + d) << 0;
b += (d ^ (a & (c ^ d))) + blocks[8] + 1163531501;
b = (((b << 20) | (b >>> 12)) + c) << 0;
a += (c ^ (d & (b ^ c))) + blocks[13] - 1444681467;
a = (((a << 5) | (a >>> 27)) + b) << 0;
d += (b ^ (c & (a ^ b))) + blocks[2] - 51403784;
d = (((d << 9) | (d >>> 23)) + a) << 0;
c += (a ^ (b & (d ^ a))) + blocks[7] + 1735328473;
c = (((c << 14) | (c >>> 18)) + d) << 0;
b += (d ^ (a & (c ^ d))) + blocks[12] - 1926607734;
b = (((b << 20) | (b >>> 12)) + c) << 0;
bc = b ^ c;
a += (bc ^ d) + blocks[5] - 378558;
a = (((a << 4) | (a >>> 28)) + b) << 0;
d += (bc ^ a) + blocks[8] - 2022574463;
d = (((d << 11) | (d >>> 21)) + a) << 0;
da = d ^ a;
c += (da ^ b) + blocks[11] + 1839030562;
c = (((c << 16) | (c >>> 16)) + d) << 0;
b += (da ^ c) + blocks[14] - 35309556;
b = (((b << 23) | (b >>> 9)) + c) << 0;
bc = b ^ c;
a += (bc ^ d) + blocks[1] - 1530992060;
a = (((a << 4) | (a >>> 28)) + b) << 0;
d += (bc ^ a) + blocks[4] + 1272893353;
d = (((d << 11) | (d >>> 21)) + a) << 0;
da = d ^ a;
c += (da ^ b) + blocks[7] - 155497632;
c = (((c << 16) | (c >>> 16)) + d) << 0;
b += (da ^ c) + blocks[10] - 1094730640;
b = (((b << 23) | (b >>> 9)) + c) << 0;
bc = b ^ c;
a += (bc ^ d) + blocks[13] + 681279174;
a = (((a << 4) | (a >>> 28)) + b) << 0;
d += (bc ^ a) + blocks[0] - 358537222;
d = (((d << 11) | (d >>> 21)) + a) << 0;
da = d ^ a;
c += (da ^ b) + blocks[3] - 722521979;
c = (((c << 16) | (c >>> 16)) + d) << 0;
b += (da ^ c) + blocks[6] + 76029189;
b = (((b << 23) | (b >>> 9)) + c) << 0;
bc = b ^ c;
a += (bc ^ d) + blocks[9] - 640364487;
a = (((a << 4) | (a >>> 28)) + b) << 0;
d += (bc ^ a) + blocks[12] - 421815835;
d = (((d << 11) | (d >>> 21)) + a) << 0;
da = d ^ a;
c += (da ^ b) + blocks[15] + 530742520;
c = (((c << 16) | (c >>> 16)) + d) << 0;
b += (da ^ c) + blocks[2] - 995338651;
b = (((b << 23) | (b >>> 9)) + c) << 0;
a += (c ^ (b | ~d)) + blocks[0] - 198630844;
a = (((a << 6) | (a >>> 26)) + b) << 0;
d += (b ^ (a | ~c)) + blocks[7] + 1126891415;
d = (((d << 10) | (d >>> 22)) + a) << 0;
c += (a ^ (d | ~b)) + blocks[14] - 1416354905;
c = (((c << 15) | (c >>> 17)) + d) << 0;
b += (d ^ (c | ~a)) + blocks[5] - 57434055;
b = (((b << 21) | (b >>> 11)) + c) << 0;
a += (c ^ (b | ~d)) + blocks[12] + 1700485571;
a = (((a << 6) | (a >>> 26)) + b) << 0;
d += (b ^ (a | ~c)) + blocks[3] - 1894986606;
d = (((d << 10) | (d >>> 22)) + a) << 0;
c += (a ^ (d | ~b)) + blocks[10] - 1051523;
c = (((c << 15) | (c >>> 17)) + d) << 0;
b += (d ^ (c | ~a)) + blocks[1] - 2054922799;
b = (((b << 21) | (b >>> 11)) + c) << 0;
a += (c ^ (b | ~d)) + blocks[8] + 1873313359;
a = (((a << 6) | (a >>> 26)) + b) << 0;
d += (b ^ (a | ~c)) + blocks[15] - 30611744;
d = (((d << 10) | (d >>> 22)) + a) << 0;
c += (a ^ (d | ~b)) + blocks[6] - 1560198380;
c = (((c << 15) | (c >>> 17)) + d) << 0;
b += (d ^ (c | ~a)) + blocks[13] + 1309151649;
b = (((b << 21) | (b >>> 11)) + c) << 0;
a += (c ^ (b | ~d)) + blocks[4] - 145523070;
a = (((a << 6) | (a >>> 26)) + b) << 0;
d += (b ^ (a | ~c)) + blocks[11] - 1120210379;
d = (((d << 10) | (d >>> 22)) + a) << 0;
c += (a ^ (d | ~b)) + blocks[2] + 718787259;
c = (((c << 15) | (c >>> 17)) + d) << 0;
b += (d ^ (c | ~a)) + blocks[9] - 343485551;
b = (((b << 21) | (b >>> 11)) + c) << 0;
if (this.first) {
this.h0 = (a + 1732584193) << 0;
this.h1 = (b - 271733879) << 0;
this.h2 = (c - 1732584194) << 0;
this.h3 = (d + 271733878) << 0;
this.first = false;
} else {
this.h0 = (this.h0 + a) << 0;
this.h1 = (this.h1 + b) << 0;
this.h2 = (this.h2 + c) << 0;
this.h3 = (this.h3 + d) << 0;
}
}
/**
* @method arrayBuffer
* @memberof Md5
* @instance
* @description Output hash as ArrayBuffer
* @returns {ArrayBuffer} ArrayBuffer
* @see {@link md5.arrayBuffer}
* @example
* hash.arrayBuffer();
*/
arrayBuffer(): ArrayBuffer {
this.finalize();
// eslint-disable-next-line @typescript-eslint/no-shadow
const buffer = new ArrayBuffer(16);
// eslint-disable-next-line @typescript-eslint/no-shadow
const blocks = new Uint32Array(buffer);
blocks[0] = this.h0;
blocks[1] = this.h1;
blocks[2] = this.h2;
blocks[3] = this.h3;
return buffer;
}
}
export function arrayBuffer(uint8Arr: Uint8Array) {
const md5 = new Md5();
md5.update(uint8Arr);
return md5.arrayBuffer();
}
@@ -0,0 +1,116 @@
import { getCachedTextDecoder, getCachedTextEncoder } from "../../utils/cache";
import { arrayBuffer } from "../lib/md5";
import {
base64ToUint8Array,
concatUint8Arrays,
} from "../../utils/uint8array-extras";
import { universalCrypto } from "../../utils/universal-crypto";
/**
* This is an implementation of the CryptoJS AES decryption scheme, without actually relying on crypto-js.
*/
const HEAD_SIZE_DWORD = 2;
const SALT_SIZE_DWORD = 2;
export async function decryptCryptoJSCipherBase64(
salt: Uint8Array,
ciphertext: Uint8Array,
password: string,
{ keySizeDWORD = 256 / 32, ivSizeDWORD = 128 / 32, iterations = 1 } = {},
) {
const crypto = await universalCrypto();
const { key, iv } = await dangerouslyDeriveParameters(
password,
salt,
keySizeDWORD,
ivSizeDWORD,
iterations,
);
try {
// decrypt ciphertext using key
const plainBuffer = await crypto.subtle.decrypt(
{ name: "AES-CBC", iv },
key,
ciphertext,
);
// return the plaintext from ArrayBuffer
return getCachedTextDecoder().decode(plainBuffer);
} catch (e) {
throw new Error("Decrypt failed");
}
}
export function parseCryptoJSCipherBase64(cryptoJSCipherBase64: string) {
let salt: Uint8Array | null = null;
let ciphertext = base64ToUint8Array(cryptoJSCipherBase64);
const [head, body] = splitUint8Array(ciphertext, HEAD_SIZE_DWORD * 4);
// This effectively checks if the ciphertext starts with 'Salted__', which is the crypto-js convention.
const headDataView = new DataView(head.buffer);
if (
headDataView.getInt32(0) === 0x53616c74 &&
headDataView.getInt32(4) === 0x65645f5f
) {
[salt, ciphertext] = splitUint8Array(body, SALT_SIZE_DWORD * 4);
}
return { ciphertext, salt };
}
async function dangerouslyDeriveParameters(
password: string,
salt: Uint8Array,
keySizeDWORD: number,
ivSizeDWORD: number,
iterations: number,
) {
const crypto = await universalCrypto();
const passwordUint8Array = getCachedTextEncoder().encode(password);
const keyPlusIV = dangerousEVPKDF(
passwordUint8Array,
salt,
keySizeDWORD + ivSizeDWORD,
iterations,
);
const [rawKey, iv] = splitUint8Array(keyPlusIV, keySizeDWORD * 4);
const key = await crypto.subtle.importKey("raw", rawKey, "AES-CBC", false, [
"decrypt",
]);
return { key, iv };
}
function dangerousEVPKDF(
passwordUint8Array: Uint8Array,
saltUint8Array: Uint8Array,
keySizeDWORD: number,
iterations: number,
) {
let derivedKey = new Uint8Array();
let block = new Uint8Array();
while (derivedKey.byteLength < keySizeDWORD * 4) {
block = new Uint8Array(
arrayBuffer(
concatUint8Arrays([block, passwordUint8Array, saltUint8Array]),
),
);
for (let i = 1; i < iterations; i++) {
block = new Uint8Array(arrayBuffer(block));
}
derivedKey = concatUint8Arrays([derivedKey, block]);
}
return derivedKey;
}
function splitUint8Array(a: Uint8Array, i: number) {
return [a.subarray(0, i), a.subarray(i, a.length)];
}
+8
View File
@@ -0,0 +1,8 @@
export { aesDecrypt, aesDecryptCompat } from "./aes/decrypt";
export { aesEncrypt } from "./aes/encrypt";
export { sha256, sha256Hex, sha256Sync, sha256HexSync } from "./sha256";
export {
keccak256Sync,
keccak256SyncHex,
keccak256SyncHexPrefixed,
} from "./keccak256";
+31
View File
@@ -0,0 +1,31 @@
import { keccak_256 } from "js-sha3";
/**
* Hashes a string or Uint8Array using keccak256.
* @param value - Value to be hashed.
* @returns Hash of the value as Uint8Array.
*/
export function keccak256Sync(value: string | Uint8Array): Uint8Array {
return new Uint8Array(keccak_256.arrayBuffer(value));
}
/**
* Hashes a string or Uint8Array using keccak256 and returns the result as a hex string.
* @param value - Value to be hashed.
* @returns Hash of the value as a hex string.
*/
export function keccak256SyncHex(value: string | Uint8Array): string {
return keccak_256(value);
}
/**
* Hashes a string or Uint8Array using keccak256 and returns the result as a hex string prefixed with "0x".
* @param value - Value to be hashed.
* @returns Hash of the value as a hex string prefixed with "0x".
*/
export function keccak256SyncHexPrefixed(
value: string | Uint8Array,
): `0x${string}` {
// prefix with 0x (this is what ethers.utils.keccak256 does)
return `0x${keccak256SyncHex(value)}`;
}
+53
View File
@@ -0,0 +1,53 @@
import { sha256 as sha256Noble } from "@noble/hashes/sha256";
import { uint8ArrayToHex } from "./utils/uint8array-extras";
import { getCachedTextEncoder } from "./utils/cache";
import { universalCrypto } from "./utils/universal-crypto";
/**
* Hash a string or Uint8Array using sha256.
* @param value - Value to be hashed.
* @returns A promise that resolves to the hash of the value as Uint8Array.
*/
export async function sha256(
value: string | BufferSource,
): Promise<Uint8Array> {
let encodedValue;
if (typeof value === "string") {
// if we do not have a cahced TextEncoder instance, create one
encodedValue = getCachedTextEncoder().encode(value);
} else {
encodedValue = value;
}
return new Uint8Array(
await (await universalCrypto()).subtle.digest("SHA-256", encodedValue),
);
}
/**
* Hash a string or Uint8Array using sha256 and returns the result as a hex string.
* @param value - Value to be hashed.
* @returns A promise that resolves to the hash of the value as a hex string.
*/
export async function sha256Hex(value: string | BufferSource): Promise<string> {
return uint8ArrayToHex(await sha256(value));
}
/**
* Hash a string or Uint8Array using sha256.
* @param value - Value to be hashed.
* @returns The hash of the value as Uint8Array.
*/
export function sha256Sync(value: string | Uint8Array): Uint8Array {
return sha256Noble(value);
}
/**
* Hash a string or Uint8Array using sha256.
* @param value - Value to be hashed.
* @returns The hash of the value as a hex string.
*/
export function sha256HexSync(value: string | Uint8Array): string {
return uint8ArrayToHex(sha256Sync(value));
}
+29
View File
@@ -0,0 +1,29 @@
class TextProcessorCache {
#encoder: TextEncoder | undefined;
#decoder: TextDecoder | undefined;
get encoder(): TextEncoder {
if (!this.#encoder) {
this.#encoder = new TextEncoder();
}
return this.#encoder;
}
get decoder(): TextDecoder {
if (!this.#decoder) {
this.#decoder = new TextDecoder();
}
return this.#decoder;
}
}
// create a singleton instance of the TextProcessorCache
const textProcessorSingleton = new TextProcessorCache();
export function getCachedTextEncoder(): TextEncoder {
return textProcessorSingleton.encoder;
}
export function getCachedTextDecoder(): TextDecoder {
return textProcessorSingleton.decoder;
}
+251
View File
@@ -0,0 +1,251 @@
export type TypedArray =
| Int8Array
| Uint8Array
| Uint8ClampedArray
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| Float32Array
| Float64Array
| BigInt64Array
| BigUint64Array;
/**
Check if the given value is an instance of `Uint8Array`.
Replacement for [`Buffer.isBuffer()`](https://nodejs.org/api/buffer.html#static-method-bufferisbufferobj).
@example
```
import {isUint8Array} from 'uint8array-extras';
console.log(isUint8Array(new Uint8Array()));
//=> true
console.log(isUint8Array(Buffer.from('x')));
//=> true
console.log(isUint8Array(new ArrayBuffer(10)));
//=> false
```
*/
export function isUint8Array(value: unknown): value is Uint8Array;
/**
Throw a `TypeError` if the given value is not an instance of `Uint8Array`.
@example
```
import {assertUint8Array} from 'uint8array-extras';
try {
assertUint8Array(new ArrayBuffer(10)); // Throws a TypeError
} catch (error) {
console.error(error.message);
}
```
*/
export function assertUint8Array(value: unknown): asserts value is Uint8Array;
/**
Convert a value to a `Uint8Array` without copying its data.
This can be useful for converting a `Buffer` to a pure `Uint8Array`. `Buffer` is already an `Uint8Array` subclass, but [`Buffer` alters some behavior](https://sindresorhus.com/blog/goodbye-nodejs-buffer), so it can be useful to cast it to a pure `Uint8Array` before returning it.
Tip: If you want a copy, just call `.slice()` on the return value.
*/
export function toUint8Array(value: TypedArray | ArrayBuffer | DataView): Uint8Array;
/**
Concatenate the given arrays into a new array.
If `arrays` is empty, it will return a zero-sized `Uint8Array`.
If `totalLength` is not specified, it is calculated from summing the lengths of the given arrays.
Replacement for [`Buffer.concat()`](https://nodejs.org/api/buffer.html#static-method-bufferconcatlist-totallength).
@example
```
import {concatUint8Arrays} from 'uint8array-extras';
const a = new Uint8Array([1, 2, 3]);
const b = new Uint8Array([4, 5, 6]);
console.log(concatUint8Arrays([a, b]));
//=> Uint8Array [1, 2, 3, 4, 5, 6]
```
*/
export function concatUint8Arrays(arrays: Uint8Array[], totalLength?: number): Uint8Array;
/**
Check if two arrays are identical by verifying that they contain the same bytes in the same sequence.
Replacement for [`Buffer#equals()`](https://nodejs.org/api/buffer.html#bufequalsotherbuffer).
@example
```
import {areUint8ArraysEqual} from 'uint8array-extras';
const a = new Uint8Array([1, 2, 3]);
const b = new Uint8Array([1, 2, 3]);
const c = new Uint8Array([4, 5, 6]);
console.log(areUint8ArraysEqual(a, b));
//=> true
console.log(areUint8ArraysEqual(a, c));
//=> false
```
*/
export function areUint8ArraysEqual(a: Uint8Array, b: Uint8Array): boolean;
/**
Compare two arrays and indicate their relative order or equality. Useful for sorting.
Replacement for [`Buffer.compare()`](https://nodejs.org/api/buffer.html#static-method-buffercomparebuf1-buf2).
@example
```
import {compareUint8Arrays} from 'uint8array-extras';
const array1 = new Uint8Array([1, 2, 3]);
const array2 = new Uint8Array([4, 5, 6]);
const array3 = new Uint8Array([7, 8, 9]);
[array3, array1, array2].sort(compareUint8Arrays);
//=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
```
*/
export function compareUint8Arrays(a: Uint8Array, b: Uint8Array): 0 | 1 | -1;
/**
Convert a `Uint8Array` (containing a UTF-8 string) to a string.
Replacement for [`Buffer#toString()`](https://nodejs.org/api/buffer.html#buftostringencoding-start-end).
@example
```
import {uint8ArrayToString} from 'uint8array-extras';
const byteArray = new Uint8Array([72, 101, 108, 108, 111]);
console.log(uint8ArrayToString(byteArray));
//=> 'Hello'
```
*/
export function uint8ArrayToString(array: Uint8Array): string;
/**
Convert a string to a `Uint8Array` (using UTF-8 encoding).
Replacement for [`Buffer.from('Hello')`](https://nodejs.org/api/buffer.html#static-method-bufferfromstring-encoding).
@example
```
import {stringToUint8Array} from 'uint8array-extras';
console.log(stringToUint8Array('Hello'));
//=> Uint8Array [72, 101, 108, 108, 111]
```
*/
export function stringToUint8Array(string: string): Uint8Array;
/**
Convert a `Uint8Array` to a Base64-encoded string.
Specify `{urlSafe: true}` to get a [Base64URL](https://base64.guru/standards/base64url)-encoded string.
Replacement for [`Buffer#toString('base64')`](https://nodejs.org/api/buffer.html#buftostringencoding-start-end).
@example
```
import {uint8ArrayToBase64} from 'uint8array-extras';
const byteArray = new Uint8Array([72, 101, 108, 108, 111]);
console.log(uint8ArrayToBase64(byteArray));
//=> 'SGVsbG8='
```
*/
export function uint8ArrayToBase64(array: Uint8Array, options?: {urlSafe: boolean}): string;
/**
Convert a Base64-encoded or [Base64URL](https://base64.guru/standards/base64url)-encoded string to a `Uint8Array`.
Replacement for [`Buffer.from('SGVsbG8=', 'base64')`](https://nodejs.org/api/buffer.html#static-method-bufferfromstring-encoding).
@example
```
import {base64ToUint8Array} from 'uint8array-extras';
console.log(base64ToUint8Array('SGVsbG8='));
//=> Uint8Array [72, 101, 108, 108, 111]
```
*/
export function base64ToUint8Array(string: string): Uint8Array;
/**
Encode a string to Base64-encoded string.
Specify `{urlSafe: true}` to get a [Base64URL](https://base64.guru/standards/base64url)-encoded string.
Replacement for `Buffer.from('Hello').toString('base64')` and [`btoa()`](https://developer.mozilla.org/en-US/docs/Web/API/btoa).
@example
```
import {stringToBase64} from 'uint8array-extras';
console.log(stringToBase64('Hello'));
//=> 'SGVsbG8='
```
*/
export function stringToBase64(string: string, options?: {urlSafe: boolean}): string;
/**
Decode a Base64-encoded or [Base64URL](https://base64.guru/standards/base64url)-encoded string to a string.
Replacement for `Buffer.from('SGVsbG8=', 'base64').toString()` and [`atob()`](https://developer.mozilla.org/en-US/docs/Web/API/atob).
@example
```
import {base64ToString} from 'uint8array-extras';
console.log(base64ToString('SGVsbG8='));
//=> 'Hello'
```
*/
export function base64ToString(base64String: string): string;
/**
Convert a `Uint8Array` to a Hex string.
Replacement for [`Buffer#toString('hex')`](https://nodejs.org/api/buffer.html#buftostringencoding-start-end).
@example
```
import {uint8ArrayToHex} from 'uint8array-extras';
const byteArray = new Uint8Array([72, 101, 108, 108, 111]);
console.log(uint8ArrayToHex(byteArray));
//=> '48656c6c6f'
```
*/
export function uint8ArrayToHex(array: Uint8Array): string;
/**
Convert a Hex string to a `Uint8Array`.
Replacement for [`Buffer.from('48656c6c6f', 'hex')`](https://nodejs.org/api/buffer.html#static-method-bufferfromstring-encoding).
@example
```
import {hexToUint8Array} from 'uint8array-extras';
console.log(hexToUint8Array('48656c6c6f'));
//=> Uint8Array [72, 101, 108, 108, 111]
```
*/
export function hexToUint8Array(hexString: string): Uint8Array;
@@ -0,0 +1,242 @@
// taken from: https://github.com/sindresorhus/uint8array-extras
// to make it work across CJS and ESM -> once we're fully ESM we can remove this and instead use the package directly
import { getCachedTextDecoder, getCachedTextEncoder } from "./cache";
const objectToString = Object.prototype.toString;
const uint8ArrayStringified = "[object Uint8Array]";
export function isUint8Array(value) {
if (!value) {
return false;
}
if (value.constructor === Uint8Array) {
return true;
}
return objectToString.call(value) === uint8ArrayStringified;
}
export function assertUint8Array(value) {
if (!isUint8Array(value)) {
throw new TypeError(`Expected \`Uint8Array\`, got \`${typeof value}\``);
}
}
export function toUint8Array(value) {
if (value instanceof ArrayBuffer) {
return new Uint8Array(value);
}
if (ArrayBuffer.isView(value)) {
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
}
throw new TypeError(`Unsupported value, got \`${typeof value}\`.`);
}
export function concatUint8Arrays(arrays, totalLength) {
if (arrays.length === 0) {
return new Uint8Array(0);
}
totalLength ??= arrays.reduce(
(accumulator, currentValue) => accumulator + currentValue.length,
0,
);
const returnValue = new Uint8Array(totalLength);
let offset = 0;
for (const array of arrays) {
assertUint8Array(array);
returnValue.set(array, offset);
offset += array.length;
}
return returnValue;
}
export function areUint8ArraysEqual(a, b) {
assertUint8Array(a);
assertUint8Array(b);
if (a === b) {
return true;
}
if (a.length !== b.length) {
return false;
}
for (let index = 0; index < a.length; index++) {
if (a[index] !== b[index]) {
return false;
}
}
return true;
}
export function compareUint8Arrays(a, b) {
assertUint8Array(a);
assertUint8Array(b);
const length = Math.min(a.length, b.length);
for (let index = 0; index < length; index++) {
if (a[index] < b[index]) {
return -1;
}
if (a[index] > b[index]) {
return 1;
}
}
// At this point, all the compared elements are equal.
// The shorter array should come first if the arrays are of different lengths.
if (a.length > b.length) {
return 1;
}
if (a.length < b.length) {
return -1;
}
return 0;
}
export function uint8ArrayToString(array) {
assertUint8Array(array);
return getCachedTextDecoder().decode(array);
}
function assertString(value) {
if (typeof value !== "string") {
throw new TypeError(`Expected \`string\`, got \`${typeof value}\``);
}
}
export function stringToUint8Array(string) {
assertString(string);
return getCachedTextEncoder().encode(string);
}
function base64ToBase64Url(base64) {
return base64.replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
}
function base64UrlToBase64(base64url) {
return base64url.replaceAll("-", "+").replaceAll("_", "/");
}
// Reference: https://phuoc.ng/collection/this-vs-that/concat-vs-push/
const MAX_BLOCK_SIZE = 65_535;
export function uint8ArrayToBase64(array, { urlSafe = false } = {}) {
assertUint8Array(array);
let base64;
if (array.length < MAX_BLOCK_SIZE) {
// Required as `btoa` and `atob` don't properly support Unicode: https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem
base64 = globalThis.btoa(String.fromCodePoint.apply(this, array));
} else {
base64 = "";
for (const value of array) {
base64 += String.fromCodePoint(value);
}
base64 = globalThis.btoa(base64);
}
return urlSafe ? base64ToBase64Url(base64) : base64;
}
export function base64ToUint8Array(base64String) {
assertString(base64String);
return Uint8Array.from(
globalThis.atob(base64UrlToBase64(base64String)),
(x) => x.codePointAt(0),
);
}
export function stringToBase64(string, { urlSafe = false } = {}) {
assertString(string);
return uint8ArrayToBase64(stringToUint8Array(string), { urlSafe });
}
export function base64ToString(base64String) {
assertString(base64String);
return uint8ArrayToString(base64ToUint8Array(base64String));
}
const byteToHexLookupTable = Array.from({ length: 256 }, (_, index) =>
index.toString(16).padStart(2, "0"),
);
export function uint8ArrayToHex(array) {
assertUint8Array(array);
// Concatenating a string is faster than using an array.
let hexString = "";
for (let index = 0; index < array.length; index++) {
hexString += byteToHexLookupTable[array[index]];
}
return hexString;
}
const hexToDecimalLookupTable = {
0: 0,
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
9: 9,
a: 10,
b: 11,
c: 12,
d: 13,
e: 14,
f: 15,
A: 10,
B: 11,
C: 12,
D: 13,
E: 14,
F: 15,
};
export function hexToUint8Array(hexString) {
assertString(hexString);
if (hexString.length % 2 !== 0) {
throw new Error("Invalid Hex string length.");
}
const resultLength = hexString.length / 2;
const bytes = new Uint8Array(resultLength);
for (let index = 0; index < resultLength; index++) {
const highNibble = hexToDecimalLookupTable[hexString[index * 2]];
const lowNibble = hexToDecimalLookupTable[hexString[index * 2 + 1]];
if (highNibble === undefined || lowNibble === undefined) {
throw new Error(
`Invalid Hex character encountered at position ${index * 2}`,
);
}
bytes[index] = (highNibble << 4) | lowNibble; // eslint-disable-line no-bitwise
}
return bytes;
}
@@ -0,0 +1,11 @@
export async function universalCrypto(): Promise<Crypto> {
if ("crypto" in globalThis) {
return globalThis.crypto;
}
// otherwise we are in node 18 so we can use `webcrypto` off of the "node:crypto" package and treat it as native
// trick bundlers so that they leave this alone :)
const pto = "pto";
// this becomes `node:crypto` at runtime
return (await import("node" + ":cry" + pto)).webcrypto as Crypto;
}
+66
View File
@@ -0,0 +1,66 @@
import { aesEncrypt, aesDecrypt, aesDecryptCompat } from "../src";
import { AES } from "crypto-js";
describe("AES", () => {
describe("modern", () => {
it("should properly encrypt and decrypt", async () => {
const plaintext = "my secret text";
const password = "pw";
const ciphertext = await aesEncrypt(plaintext, password);
const decrypted = await aesDecrypt(ciphertext, password);
expect(decrypted).toEqual(plaintext);
});
it("should not decypt if password is wrong", async () => {
const plaintext = "my secret text";
const password = "pw";
const wrongPassword = "wrong";
const ciphertext = await aesEncrypt(plaintext, password);
let decrypted = "";
try {
decrypted = await aesDecrypt(ciphertext, wrongPassword);
} catch (err) {
expect(err.message).toEqual("Decrypt failed");
}
expect(decrypted).not.toEqual(plaintext);
});
it("should not decrypt legacy crypto-js ciphertext using aesDecrypt", async () => {
const plaintext = "my secret text";
const password = "pw";
const ciphertext = AES.encrypt(plaintext, password).toString();
let decrypted = "";
try {
decrypted = await aesDecrypt(ciphertext, password);
} catch (err) {
expect(err.message).toEqual("Decrypt failed");
}
expect(decrypted).not.toEqual(plaintext);
});
});
describe("compat (crypto-js decrypt support)", () => {
it("should properly decrypt legacy crypto-js ciphertext when using aesDecryptCompat", async () => {
const plaintext = "my secret text";
const password = "pw";
const ciphertext = AES.encrypt(plaintext, password).toString();
const decrypted = await aesDecryptCompat(ciphertext, password);
expect(decrypted).toEqual(plaintext);
});
it("should not decypt if password is wrong using decrypt legacy crypto-js ciphertext when using aesDecryptCompat", async () => {
const plaintext = "my secret text";
const password = "pw";
const wrongPassword = "wrong";
const ciphertext = AES.encrypt(plaintext, password).toString();
let decrypted = "";
try {
decrypted = await aesDecryptCompat(ciphertext, wrongPassword);
} catch (err) {
expect(err.message).toEqual("Decrypt failed");
}
expect(decrypted).not.toEqual(plaintext);
});
});
});
+30
View File
@@ -0,0 +1,30 @@
import {
keccak256SyncHex,
keccak256SyncHexPrefixed,
keccak256Sync,
} from "../src";
import { utils } from "ethers";
import { keccak_256 } from "@noble/hashes/sha3";
import { uint8ArrayToHex } from "../src/utils/uint8array-extras";
const STRING_TO_HASH = "hello world";
describe("keccak256", () => {
it("keccak256SyncHexPrefixed === ethers.utils.keccak256", async () => {
const out = keccak256SyncHexPrefixed(STRING_TO_HASH);
const ethersOut = utils.keccak256(Buffer.from(STRING_TO_HASH));
expect(out).toEqual(ethersOut);
});
it("keccak256SyncHex === @noble/hashes: keccak_256 -> hex", async () => {
const out = keccak256SyncHex(STRING_TO_HASH);
const nobleOut = uint8ArrayToHex(keccak_256(STRING_TO_HASH));
expect(out).toEqual(nobleOut);
});
it("keccak256Sync === @noble/hashes: keccak_256", async () => {
const out = keccak256Sync(STRING_TO_HASH);
const nobleOut = keccak_256(STRING_TO_HASH);
expect(out).toEqual(nobleOut);
});
});
+30
View File
@@ -0,0 +1,30 @@
import { sha256, sha256Hex, sha256Sync, sha256HexSync } from "../src";
import { createHash } from "node:crypto";
const STRING_TO_HASH = "hello world";
describe("sha256", () => {
it("sha256", async () => {
const out = await sha256(STRING_TO_HASH);
const hash = createHash("sha256").update(STRING_TO_HASH).digest();
expect(out).toEqual(hash);
});
it("sha256 -> hex", async () => {
const out = await sha256Hex(STRING_TO_HASH);
const hash = createHash("sha256").update(STRING_TO_HASH).digest("hex");
expect(out).toEqual(hash);
});
it("sha256Sync", async () => {
const out = sha256Sync(STRING_TO_HASH);
const hash = createHash("sha256").update(STRING_TO_HASH).digest();
expect(out).toEqual(hash);
});
it("sha256Sync -> hex", async () => {
const out = sha256HexSync(STRING_TO_HASH);
const hash = createHash("sha256").update(STRING_TO_HASH).digest("hex");
expect(out).toEqual(hash);
});
});
+5
View File
@@ -0,0 +1,5 @@
{
"extends": "@thirdweb-dev/tsconfig/sdk.json",
"include": ["src", "types"],
"exclude": ["dist", "build", "node_modules"]
}
+6
View File
@@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-typescript",
["@babel/preset-env", { "targets": "defaults, not ie 11" }]
]
}
+4
View File
@@ -0,0 +1,4 @@
# folders
dist/
# files
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["thirdweb"],
};
+12
View File
@@ -0,0 +1,12 @@
# @thirdweb-dev/merkletree
## 0.2.0
### Minor Changes
- [#1861](https://github.com/thirdweb-dev/js/pull/1861) [`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4) Thanks [@jnsdls](https://github.com/jnsdls)! - initial release
### Patch Changes
- Updated dependencies [[`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4)]:
- @thirdweb-dev/crypto@0.2.0
+16
View File
@@ -0,0 +1,16 @@
<p align="center">
<br />
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/packages/sdk/logo.svg?raw=true" width="200" alt=""/></a>
<br />
</p>
<h1 align="center">thirdweb merkletree</h1>
<p align="center">
<a href="https://www.npmjs.com/package/@thirdweb-dev/merkletree"><img src="https://img.shields.io/npm/v/@thirdweb-dev/merkletree?color=red&label=npm&logo=npm" alt="npm version"/></a>
<a href="https://github.com/thirdweb-dev/js/actions/workflows/build-test-lint.yml"><img alt="Build Status" src="https://github.com/thirdweb-dev/js/actions/workflows/build-test-lint.yml/badge.svg"/></a>
<a href="https://discord.gg/thirdweb"><img alt="Join our Discord!" src="https://img.shields.io/discord/834227967404146718.svg?color=7289da&label=discord&logo=discord&style=flat"/></a>
</p>
<!-- <p align="center"><strong>Best in class Web3 SDK for Browser, Node and Mobile apps</strong></p> -->
<br />
### todo
+60
View File
@@ -0,0 +1,60 @@
{
"name": "@thirdweb-dev/merkletree",
"version": "0.2.0",
"main": "dist/thirdweb-dev-merkletree.cjs.js",
"module": "dist/thirdweb-dev-merkletree.esm.js",
"exports": {
".": {
"module": "./dist/thirdweb-dev-merkletree.esm.js",
"default": "./dist/thirdweb-dev-merkletree.cjs.js"
},
"./package.json": "./package.json"
},
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/utils",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/thirdweb-dev/js/issues"
},
"author": "thirdweb eng <[email protected]>",
"files": [
"dist/"
],
"preconstruct": {
"entrypoints": [
"index.ts"
],
"exports": true
},
"sideEffects": false,
"dependencies": {
"@thirdweb-dev/crypto": "workspace:*",
"buffer": "^6.0.3",
"buffer-reverse": "^1.0.1",
"treeify": "^1.1.0"
},
"devDependencies": {
"@preconstruct/cli": "2.7.0",
"@thirdweb-dev/tsconfig": "workspace:*",
"@types/benchmark": "^2.1.4",
"@types/treeify": "^1.0.2",
"benchmark": "^2.1.4",
"eslint-config-thirdweb": "workspace:*",
"typescript": "^5.1.6"
},
"scripts": {
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/ && bunx publint --strict --level warning",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"build": "tsc && preconstruct build",
"push": "yalc push",
"test": "bun test",
"benchmark:aes-encrypt": "bun benchmark/aes-encrypt.ts",
"benchmark:aes-decrypt": "bun benchmark/aes-decrypt.ts",
"benchmark:sha256": "bun benchmark/sha256.ts",
"benchmark": "bun run benchmark:aes-encrypt && bun run benchmark:aes-decrypt && bun run benchmark:sha256"
},
"engines": {
"node": ">=18"
}
}
+1
View File
@@ -0,0 +1 @@
export { MerkleTree } from "./merkletree/MerkleTree";
+441
View File
@@ -0,0 +1,441 @@
// ADAPTED FROM https://github.com/merkletreejs/merkletreejs
import { Buffer } from "buffer";
export class Base {
/**
* print
* @desc Prints out a visual representation of the merkle tree.
* @example
*```js
*tree.print()
*```
*/
print(): void {
Base.print(this);
}
/**
* bufferIndexOf
* @desc Returns the first index of which given buffer is found in array.
* @param {Buffer[]} haystack - Array of buffers.
* @param {Buffer} needle - Buffer to find.
* @return {Number} - Index number
*
* @example
* ```js
*const index = tree.bufferIndexOf(haystack, needle)
*```
*/
protected bufferIndexOf(
array: Buffer[],
element: Buffer,
isSorted: boolean = false,
): number {
if (isSorted) {
return this.binarySearch(array, element, Buffer.compare);
}
const eqChecker = (buffer1: Buffer, buffer2: Buffer) =>
buffer1.equals(buffer2);
return this.linearSearch(array, element, eqChecker);
}
/**
* binarySearch
* @desc Returns the first index of which given item is found in array using binary search.
* @param {Buffer[]} array - Array of items.
* @param {Buffer} element - Item to find.
* @param {Function} compareFunction
* @return {Number} - Index number
*
* @example
* ```js
*const index = MerkleTree.binarySearch(array, element, Buffer.compare)
*```
*/
static binarySearch(
array: Buffer[],
element: Buffer,
compareFunction: (a: Buffer, b: Buffer) => number,
): number {
let start = 0;
let end = array.length - 1;
// Iterate while start not meets end
while (start <= end) {
// Find the mid index
const mid = Math.floor((start + end) / 2);
// Check if the mid value is greater than, equal to, or less than search element.
const ordering = compareFunction(array[mid], element);
// If element is present at mid, start iterating for searching first appearance.
if (ordering === 0) {
// Linear reverse iteration until the first matching item index is found.
for (let i = mid - 1; i >= 0; i--) {
if (compareFunction(array[i], element) === 0) {
continue;
}
return i + 1;
}
return 0;
} /* Else look in left or right half accordingly */ else if (
ordering < 0
) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return -1;
}
/**
* binarySearch
* @desc Returns the first index of which given item is found in array using binary search.
* @param {Buffer[]} array - Array of items.
* @param {Buffer} element - Item to find.
* @param {Function} compareFunction
* @return {Number} - Index number
*
* @example
* ```js
*const index = tree.binarySearch(array, element, Buffer.compare)
*```
*/
binarySearch(
array: Buffer[],
element: Buffer,
compareFunction: (a: Buffer, b: Buffer) => number,
): number {
return Base.binarySearch(array, element, compareFunction);
}
/**
* linearSearch
* @desc Returns the first index of which given item is found in array using linear search.
* @param {Buffer[]} array - Array of items.
* @param {Buffer} element - Item to find.
* @param {Function} eqChecker
* @return {Number} - Index number
*
* @example
* ```js
*const index = MerkleTree.linearSearch(array, element, (a, b) => a === b)
*```
*/
static linearSearch(
array: Buffer[],
element: Buffer,
eqChecker: (a: Buffer, b: Buffer) => boolean,
): number {
for (let i = 0; i < array.length; i++) {
if (eqChecker(array[i], element)) {
return i;
}
}
return -1;
}
/**
* linearSearch
* @desc Returns the first index of which given item is found in array using linear search.
* @param {Buffer[]} array - Array of items.
* @param {Buffer} element - Item to find.
* @param {Function} eqChecker
* @return {Number} - Index number
*
* @example
* ```js
*const index = tree.linearSearch(array, element, (a, b) => a === b)
*```
*/
linearSearch(
array: Buffer[],
element: Buffer,
eqChecker: (a: Buffer, b: Buffer) => boolean,
): number {
return Base.linearSearch(array, element, eqChecker);
}
/**
* bufferify
* @desc Returns a buffer type for the given value.
* @param {String|Number|Object|Buffer|ArrayBuffer} value
* @return {Buffer}
*
* @example
* ```js
*const buf = MerkleTree.bufferify('0x1234')
*```
*/
static bufferify(value: any): Buffer {
if (!Buffer.isBuffer(value)) {
// crypto-js support
if (typeof value === "object" && value.words) {
return Buffer.from(value.toString(convertWordsToBuffer), "hex");
} else if (Base.isHexString(value)) {
return Buffer.from(value.replace(/^0x/, ""), "hex");
} else if (typeof value === "string") {
return Buffer.from(value);
} else if (typeof value === "bigint") {
return Buffer.from(value.toString(16), "hex");
} else if (value instanceof Uint8Array) {
return Buffer.from(value.buffer);
} else if (typeof value === "number") {
let s = value.toString();
if (s.length % 2) {
s = `0${s}`;
}
return Buffer.from(s, "hex");
} else if (ArrayBuffer.isView(value)) {
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
}
}
return value;
}
bigNumberify(value: any): bigint {
return Base.bigNumberify(value);
}
static bigNumberify(value: any): bigint {
if (typeof value === "bigint") {
return value;
}
if (typeof value === "string") {
if (value.startsWith("0x") && Base.isHexString(value)) {
return BigInt("0x" + value.replace("0x", "").toString());
}
return BigInt(value);
}
if (Buffer.isBuffer(value)) {
return BigInt("0x" + value.toString("hex"));
}
if (value instanceof Uint8Array) {
return uint8ArrayToBigInt(value);
}
if (typeof value === "number") {
return BigInt(value);
}
throw new Error("cannot bigNumberify");
}
/**
* isHexString
* @desc Returns true if value is a hex string.
* @param {String} value
* @return {Boolean}
*
* @example
* ```js
*console.log(MerkleTree.isHexString('0x1234'))
*```
*/
static isHexString(v: string): boolean {
return typeof v === "string" && /^(0x)?[0-9A-Fa-f]*$/.test(v);
}
/**
* print
* @desc Prints out a visual representation of the given merkle tree.
* @param {Object} tree - Merkle tree instance.
* @return {String}
* @example
*```js
*MerkleTree.print(tree)
*```
*/
static print(tree: any): void {
console.log(tree.toString());
}
/**
* bufferToHex
* @desc Returns a hex string with 0x prefix for given buffer.
* @param {Buffer} value
* @return {String}
* @example
*```js
*const hexStr = tree.bufferToHex(Buffer.from('A'))
*```
*/
bufferToHex(value: Buffer, withPrefix: boolean = true): string {
return Base.bufferToHex(value, withPrefix);
}
/**
* bufferToHex
* @desc Returns a hex string with 0x prefix for given buffer.
* @param {Buffer} value
* @return {String}
* @example
*```js
*const hexStr = MerkleTree.bufferToHex(Buffer.from('A'))
*```
*/
static bufferToHex(value: Buffer, withPrefix: boolean = true): string {
return `${withPrefix ? "0x" : ""}${(value || Buffer.alloc(0)).toString(
"hex",
)}`;
}
/**
* bufferify
* @desc Returns a buffer type for the given value.
* @param {String|Number|Object|Buffer} value
* @return {Buffer}
*
* @example
* ```js
*const buf = tree.bufferify('0x1234')
*```
*/
bufferify(value: any): Buffer {
return Base.bufferify(value);
}
/**
* bufferifyFn
* @desc Returns a function that will bufferify the return value.
* @param {Function}
* @return {Function}
*
* @example
* ```js
*const fn = tree.bufferifyFn((value) => sha256(value))
*```
*/
bufferifyFn(f: any): any {
return (value: any): Buffer => {
const v = f(value);
if (Buffer.isBuffer(v)) {
return v;
}
if (this.isHexString(v)) {
return Buffer.from(v.replace("0x", ""), "hex");
}
if (typeof v === "string") {
return Buffer.from(v);
}
if (typeof v === "bigint") {
return Buffer.from(value.toString(16), "hex");
}
if (ArrayBuffer.isView(v)) {
return Buffer.from(v.buffer, v.byteOffset, v.byteLength);
}
// crypto-js support
const arrayBuffer = hexStringToArrayBuffer(value.toString("hex"));
// Assuming f now works with ArrayBuffers
const processedBuffer = f(arrayBuffer);
const hexResult = arrayBufferToHexString(processedBuffer);
return Buffer.from(hexResult, "hex");
};
}
/**
* isHexString
* @desc Returns true if value is a hex string.
* @param {String} value
* @return {Boolean}
*
* @example
* ```js
*console.log(MerkleTree.isHexString('0x1234'))
*```
*/
protected isHexString(value: string): boolean {
return Base.isHexString(value);
}
/**
* log2
* @desc Returns the log2 of number.
* @param {Number} value
* @return {Number}
*/
protected log2(n: number): number {
return n === 1 ? 0 : 1 + this.log2((n / 2) | 0);
}
/**
* zip
* @desc Returns true if value is a hex string.
* @param {String[]|Number[]|Buffer[]} a - first array
* @param {String[]|Number[]|Buffer[]} b - second array
* @return {String[][]|Number[][]|Buffer[][]}
*
* @example
* ```js
*const zipped = tree.zip(['a', 'b'],['A', 'B'])
*console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ]
*```
*/
protected zip(a: any[], b: any[]): any[][] {
return a.map((e, i) => [e, b[i]]);
}
static hexZeroPad(hexStr: string, length: number) {
return "0x" + hexStr.replace("0x", "").padStart(length, "0");
}
}
export default Base;
// UTILS
// replaces CryptoJS.enc.Hex
function convertWordsToBuffer(value: { words: any }) {
const wordArray = value.words;
const arrayBuffer = new ArrayBuffer(wordArray.length * 4); // 4 bytes per word
const uint8View = new Uint8Array(arrayBuffer);
for (let i = 0; i < wordArray.length; i++) {
uint8View[i * 4] = (wordArray[i] >> 24) & 0xff;
uint8View[i * 4 + 1] = (wordArray[i] >> 16) & 0xff;
uint8View[i * 4 + 2] = (wordArray[i] >> 8) & 0xff;
uint8View[i * 4 + 3] = wordArray[i] & 0xff;
}
return arrayBuffer;
}
function hexStringToArrayBuffer(hexString: string) {
const buffer = new Uint8Array(hexString.length / 2);
for (let i = 0; i < hexString.length; i += 2) {
buffer[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
}
return buffer.buffer;
}
function arrayBufferToHexString(arrayBuffer: ArrayBuffer) {
const uint8View = new Uint8Array(arrayBuffer);
return Array.from(uint8View)
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
}
function uint8ArrayToBigInt(u8a: Uint8Array) {
const hex = Array.from(u8a)
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
return BigInt(`0x${hex}`);
}
File diff suppressed because it is too large Load Diff
+199
View File
@@ -0,0 +1,199 @@
import { MerkleTree } from "../src/index";
import { keccak256Sync, sha256Sync } from "@thirdweb-dev/crypto";
describe("MerkleTree", () => {
it("sha256 with keccak256 leaves", () => {
const leaves = ["a", "b", "c"].map(keccak256Sync);
const tree = new MerkleTree(leaves, sha256Sync);
// check equality of the roots
expect(tree.getHexRoot()).toMatchSnapshot();
// check equality of the leaves
expect(tree.getHexLeaves()).toMatchSnapshot();
// check equality of the layers
expect(tree.getHexLayers()).toMatchSnapshot();
});
it("sha256 with keccak256 leaves with duplicate odd option", () => {
const leaves = ["a", "b", "c"].map(keccak256Sync);
const tree = new MerkleTree(leaves, sha256Sync, { duplicateOdd: true });
// check equality of the roots
expect(tree.getHexRoot()).toMatchSnapshot();
// check equality of the leaves
expect(tree.getHexLeaves()).toMatchSnapshot();
// check equality of the layers
expect(tree.getHexLayers()).toMatchSnapshot();
});
it("sha256 with sort pairs option", () => {
const leaves = ["a", "b", "c", "d", "e", "f"].map(sha256Sync);
const tree = new MerkleTree(leaves, sha256Sync, { sortPairs: true });
// check equality of the roots
expect(tree.getHexRoot()).toMatchSnapshot();
});
it("sha256 verify with positional hex proof and no pairSort", () => {
const leaves = ["a", "b", "c", "d", "e", "f"].map(sha256Sync);
const tree = new MerkleTree(leaves, sha256Sync, { sortPairs: false });
expect(
tree.verify(
// TODO: accept Uint8Array as input
tree.getProof(Buffer.from(leaves[1]), 1),
// TODO: accept Uint8Array as input
Buffer.from(leaves[1]),
tree.getHexRoot(),
),
).toBe(true);
});
test("sha256 verify with non-hex proof and no pairSort", () => {
const leaves = ["a", "b", "c", "d", "e", "f"].map(sha256Sync);
const tree = new MerkleTree(leaves, sha256Sync, { sortPairs: false });
expect(
tree.verify(
// TODO: accept Uint8Array as input
tree.getProof(Buffer.from(leaves[1]), 1),
// TODO: accept Uint8Array as input
Buffer.from(leaves[1]),
tree.getHexRoot(),
),
).toBe(true);
});
it("sha256 verify with hex proof and pairSort", () => {
const leaves = ["a", "b", "c", "d", "e", "f"].map(sha256Sync);
const tree = new MerkleTree(leaves, sha256Sync, { sortPairs: true });
expect(
tree.verify(
// TODO: accept Uint8Array as input
tree.getHexProof(Buffer.from(leaves[1]), 1),
// TODO: accept Uint8Array as input
Buffer.from(leaves[1]),
tree.getHexRoot(),
),
).toBe(true);
});
it("keccak256 with sort leaves and sort pairs option", () => {
const leaves = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"].map(
keccak256Sync,
);
const tree = new MerkleTree(leaves, keccak256Sync, {
sortLeaves: true,
sortPairs: true,
});
// check equality of the roots
expect(tree.getHexRoot()).toMatchSnapshot();
});
it("keccak256 with sort option", () => {
const leaves = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"].map(
keccak256Sync,
);
const tree = new MerkleTree(leaves, keccak256Sync, { sort: true });
// check equality of the roots
expect(tree.getHexRoot()).toMatchSnapshot();
});
it("sha256 with sha256 leaves and sort pairs option and duplicate odd option", () => {
const leaves = ["a", "b", "c", "d", "e", "f"].map(sha256Sync);
const tree = new MerkleTree(leaves, sha256Sync, {
sortPairs: true,
duplicateOdd: true,
});
// check equality of the roots
expect(tree.getHexRoot()).toMatchSnapshot();
});
it("sha256 with hash leaves option", () => {
const expectedRoot =
"0x1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2";
const leaves = ["a", "b", "c", "d", "e", "f"];
const tree1 = new MerkleTree(leaves, sha256Sync, { hashLeaves: true });
expect(tree1.getHexRoot()).toEqual(expectedRoot);
const hashedLeaves = ["a", "b", "c", "d", "e", "f"].map(sha256Sync);
const tree2 = new MerkleTree(hashedLeaves, sha256Sync);
expect(tree2.getHexRoot()).toEqual(expectedRoot);
});
it("sha256 with hash leaves option and duplicate odd option", () => {
const leaves = ["a", "b", "c", "d", "e", "f"];
const tree = new MerkleTree(leaves, sha256Sync, {
hashLeaves: true,
duplicateOdd: true,
});
const root =
"44205acec5156114821f1f71d87c72e0de395633cd1589def6d4444cc79f8103";
expect(tree.getRoot().toString("hex")).toEqual(root);
});
it("solidity keccak256", () => {
const leaves = ["a", "b", "c"].map(keccak256Sync);
const a_hash =
"3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb";
const b_hash =
"b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510";
const c_hash =
"0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2";
expect(leaves.map((l) => Buffer.from(l).toString("hex"))).toEqual([
a_hash,
b_hash,
c_hash,
]);
const tree = new MerkleTree(leaves, keccak256Sync);
const layers = tree.getLayers().slice(1); // no leaves
const layer_1 = Buffer.from(
keccak256Sync(Buffer.concat([leaves[0], leaves[1]])),
).toString("hex");
expect(layers[0][0].toString("hex")).toEqual(layer_1);
expect(layers[0][1].toString("hex")).toEqual(c_hash);
const root = Buffer.from(
"aff1208e69c9e8be9b584b07ebac4e48a1ee9d15ce3afe20b77a4d29e4175aa3",
"hex",
);
expect(tree.getRoot().toString("hex")).toEqual(root.toString("hex"));
const proof_0 = tree.getProof(Buffer.from(leaves[0]));
expect(proof_0.length).toEqual(2);
expect(proof_0[0].position).toEqual("right");
expect(proof_0[0].data.toString("hex")).toEqual(b_hash);
expect(proof_0[1].position).toEqual("right");
expect(proof_0[1].data.toString("hex")).toEqual(c_hash);
expect(tree.verify(proof_0, Buffer.from(leaves[0]), root)).toBe(true);
const proof_1 = tree.getProof(Buffer.from(leaves[1]));
expect(proof_1.length).toEqual(2);
expect(proof_1[0].position).toEqual("left");
expect(proof_1[0].data.toString("hex")).toEqual(a_hash);
expect(proof_1[1].position).toEqual("right");
expect(proof_1[1].data.toString("hex")).toEqual(c_hash);
expect(tree.verify(proof_1, Buffer.from(leaves[1]), root)).toBe(true);
const proof_2 = tree.getProof(Buffer.from(leaves[2]));
expect(proof_2.length).toEqual(1);
expect(proof_2[0].position).toEqual("left");
expect(proof_2[0].data.toString("hex")).toEqual(layer_1);
expect(tree.verify(proof_2, Buffer.from(leaves[2]), root)).toBe(true);
});
});
@@ -0,0 +1,63 @@
// Bun Snapshot v1, https://goo.gl/fbAQLP
exports[`MerkleTree sha256 with keccak256 leaves 1`] = `"0x311d2e46f49b15fff8b746b74ad57f2cc9e0d9939fda94387141a2d3fdf187ae"`;
exports[`MerkleTree sha256 with keccak256 leaves 2`] = `
[
"0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb",
"0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510",
"0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2",
]
`;
exports[`MerkleTree sha256 with keccak256 leaves 3`] = `
[
[
"0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb",
"0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510",
"0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2",
],
[
"0x176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1",
"0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2",
],
[
"0x311d2e46f49b15fff8b746b74ad57f2cc9e0d9939fda94387141a2d3fdf187ae",
],
]
`;
exports[`MerkleTree sha256 with keccak256 leaves with duplicate odd option 1`] = `"0xbcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1"`;
exports[`MerkleTree sha256 with keccak256 leaves with duplicate odd option 2`] = `
[
"0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb",
"0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510",
"0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2",
]
`;
exports[`MerkleTree sha256 with keccak256 leaves with duplicate odd option 3`] = `
[
[
"0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb",
"0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510",
"0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2",
],
[
"0x176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1",
"0x43e061172b1177f25d0f156b2d2ed11728006fade8e167ff3d1b9dbc979a3358",
],
[
"0xbcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1",
],
]
`;
exports[`MerkleTree sha256 with sort pairs option 1`] = `"0xa30ba95a1a5dc397fe45ea20105363b08d682b864a28f4940419a29349a28325"`;
exports[`MerkleTree keccak256 with sort option 1`] = `"0x60219f87561939610b484575e45c6e81156a53b86d7cd16640d930d14f21758e"`;
exports[`MerkleTree sha256 with sha256 leaves and sort pairs option and duplicate odd option 1`] = `"0xa5260b2a7ec31584e5d5689a5628c2b3d949e2397334fd71c107478e5f887eaf"`;
exports[`MerkleTree keccak256 with sort leaves and sort pairs option 1`] = `"0x60219f87561939610b484575e45c6e81156a53b86d7cd16640d930d14f21758e"`;
+5
View File
@@ -0,0 +1,5 @@
{
"extends": "@thirdweb-dev/tsconfig/sdk.json",
"include": ["src", "types"],
"exclude": ["dist", "build", "node_modules"]
}
+1 -1
View File
@@ -43,7 +43,7 @@
},
"scripts": {
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/",
"lint": "eslint src/ && bunx publint --strict --level warning",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"build": "tsc && preconstruct build",
+66
View File
@@ -1,5 +1,71 @@
# @thirdweb-dev/react-core
## 4.1.9
### Patch Changes
- [#1952](https://github.com/thirdweb-dev/js/pull/1952) [`fb0cb676`](https://github.com/thirdweb-dev/js/commit/fb0cb676b996fe386dcc53b5a6dab03f88e76175) Thanks [@MananTank](https://github.com/MananTank)! - - Add `ConnectEmbed`` component to embed the ConnectWallet's Modal UI directly in page
- Fix `ConnectWallet` component setting personal wallet as "connected" wallet when connecting a Safe / Smart Wallet
- [#1921](https://github.com/thirdweb-dev/js/pull/1921) [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Add Coin98 wallet
- [#1971](https://github.com/thirdweb-dev/js/pull/1971) [`12d85dec`](https://github.com/thirdweb-dev/js/commit/12d85dec10f47d6520b737a23576e4f832abb47f) Thanks [@iketw](https://github.com/iketw)! - Adds usePersonalWalletAddress hook to React/RN
```typescript
// React
import { usePersonalWalletAddress } from "@thirdweb-dev/react";
const personalWalletAddress = usePersonalWalletAddress();
```
```typescript
// React-Native
import { usePersonalWalletAddress } from "@thirdweb-dev/react-native";
const personalWalletAddress = usePersonalWalletAddress();
```
- Updated dependencies [[`36600dec`](https://github.com/thirdweb-dev/js/commit/36600decefca7b9ebb1924757b4186b559e439f6), [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1), [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.1.8
### Patch Changes
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96), [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.1.7
### Patch Changes
- Updated dependencies [[`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4), [`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.1.6
### Patch Changes
- Updated dependencies [[`69d68f5a`](https://github.com/thirdweb-dev/js/commit/69d68f5a25877bf5ea694719d2cf4ca8388f3091)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.1.5
### Patch Changes
- Updated dependencies [[`9f993dcc`](https://github.com/thirdweb-dev/js/commit/9f993dcc1e60cc087850b8fb9ce09524073ce0b5)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.1.4
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "4.1.4",
"version": "4.1.9",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -45,7 +45,7 @@
"private": false,
"scripts": {
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/",
"lint": "eslint src/ && bunx publint --strict --level warning",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"generate-docs": "pnpm generate-docs:evm && pnpm generate-snippets",
@@ -2,6 +2,7 @@ import { useWalletContext } from "../providers/thirdweb-wallet-provider";
import invariant from "tiny-invariant";
import type {
BloctoWallet,
Coin98Wallet,
CoinbaseWallet,
CoreWallet,
EmbeddedWallet,
@@ -26,6 +27,7 @@ export type WalletId = (typeof walletIds)[keyof typeof walletIds];
type WalletIdToWalletTypeMap = {
metamask: MetaMaskWallet;
coin98: Coin98Wallet;
coinbase: CoinbaseWallet;
coreWallet: CoreWallet;
rainbowWallet: RainbowWallet;
@@ -65,6 +65,16 @@ const walletInstanceToConfig: Map<
WalletConfig<any>
> = new Map();
type PersonalWalletInfo = {
signer?: Signer;
wallet?: WalletInstance;
walletConfig?: WalletConfig;
connectionStatus: ConnectionStatus;
chainId?: number;
disconnect: () => Promise<void>;
switchChain: (chainId: number) => Promise<void>;
};
type ThirdwebWalletContextData = {
wallets: WalletConfig[];
signer?: Signer;
@@ -92,6 +102,20 @@ type ThirdwebWalletContextData = {
getWalletConfig: (walletInstance: WalletInstance) => WalletConfig | undefined;
activeChainSetExplicitly: boolean;
clientId?: string;
/**
* flag indicating whether to treat any wallet that gets connected after setting this flag as a "personal" wallet
* If a wallet is treated as a "personal" wallet, It's details like signer, connectionStatus, wallet, walletConfig will be saved in `personalWalletInfo` object instead of the main context
*/
isConnectingToPersonalWallet: boolean;
/**
* Set value of `isConnectingToPersonalWallet` flag
*/
setIsConnectingToPersonalWallet: (value: boolean) => void;
/**
* object containing info about the connected personal wallet
* When `isConnectingToPersonalWallet` is set to true, and a wallet is connected after that, all the info about the connected wallet will be stored in this object
*/
personalWalletInfo: PersonalWalletInfo;
};
const ThirdwebWalletContext = /* @__PURE__ */ createContext<
@@ -113,12 +137,32 @@ export function ThirdwebWalletProvider(
signerWallet?: WalletConfig<SignerWallet>;
}>,
) {
const [signer, setSigner] = useState<Signer | undefined>(undefined);
const [connectionStatus, setConnectionStatus] =
useState<ConnectionStatus>("unknown");
const autoConnectTimeout = props.autoConnectTimeout || 15000;
if (!lastConnectedWalletStorage) {
lastConnectedWalletStorage =
props.createWalletStorage("coordinatorStorage");
}
// if autoSwitch is enabled - enforce connection to activeChain
const chainToConnect = props.autoSwitch ? props.activeChain : undefined;
const [isConnectingToPersonalWallet, setIsConnectingToPersonalWallet] =
useState(false);
const personalWalletConnection = usePersonalWalletConnection();
const {
wallet: personalWallet,
setWallet: setPersonalWallet,
setSigner: setPersonalWalletSigner,
setStatus: setPersonalWalletStatus,
setWalletConfig: setPersonalWalletConfig,
} = personalWalletConnection;
const [signer, setSigner] = useState<Signer | undefined>(undefined);
const [status, setStatus] = useState<ConnectionStatus>("unknown");
const [activeWallet, setActiveWallet] = useState<
WalletInstance | undefined
>();
@@ -131,14 +175,6 @@ export function ThirdwebWalletProvider(
WalletConfig | undefined
>();
if (!lastConnectedWalletStorage) {
lastConnectedWalletStorage =
props.createWalletStorage("coordinatorStorage");
}
// if autoSwitch is enabled - enforce connection to activeChain
const chainToConnect = props.autoSwitch ? props.activeChain : undefined;
const walletParams: WalletOptions = useMemo(() => {
return {
chains: props.chains,
@@ -165,12 +201,25 @@ export function ThirdwebWalletProvider(
[walletParams],
);
// if props.chains is updated, update the active wallet's chains
// if props.chains is updated, update the wallet's chains
useEffect(() => {
if (activeWallet) {
activeWallet.updateChains(props.chains);
const _personalWallet = activeWallet.getPersonalWallet();
if (_personalWallet instanceof AbstractClientWallet) {
_personalWallet.updateChains(props.chains);
}
}
}, [activeWallet, props.chains]);
if (isConnectingToPersonalWallet && personalWallet) {
personalWallet.updateChains(props.chains);
}
}, [
activeWallet,
props.chains,
personalWallet,
isConnectingToPersonalWallet,
]);
const setConnectedWallet = useCallback(
async (
@@ -178,23 +227,45 @@ export function ThirdwebWalletProvider(
connectParams?: ConnectParams<Record<string, any>>,
isAutoConnect = false,
) => {
setActiveWallet(wallet);
if (isConnectingToPersonalWallet) {
setPersonalWallet(wallet);
} else {
setActiveWallet(wallet);
}
const walletConfig = walletInstanceToConfig.get(wallet);
if (!walletConfig) {
throw new Error(
"Wallet config not found for given wallet instance. Do not create a wallet instance manually - use the useCreateWalletInstance() hook instead",
);
}
setActiveWalletConfig(walletConfig);
setConnectionStatus("connected");
const _signer = await wallet.getSigner();
setSigner(_signer);
// it autoconnected, then the details is already saved in storage, no need to store again
if (isConnectingToPersonalWallet) {
setPersonalWalletConfig(walletConfig);
setPersonalWalletStatus("connected");
} else {
setActiveWalletConfig(walletConfig);
setStatus("connected");
}
const _signer = await wallet.getSigner();
if (isConnectingToPersonalWallet) {
setPersonalWalletSigner(_signer);
} else {
setSigner(_signer);
}
// it auto-connected, then the details is already saved in storage, no need to store again
if (isAutoConnect) {
return;
}
// do not save connection details for personal wallet
if (isConnectingToPersonalWallet) {
return;
}
// save to storage
const walletInfo: LastConnectedWalletInfo = {
@@ -202,16 +273,18 @@ export function ThirdwebWalletProvider(
connectParams: connectParams || wallet.getConnectParams(),
};
// if personal wallet exists, we need to replace the connectParams.personalWallet to a stringifiable version
const personalWallet = wallet.getPersonalWallet() as AbstractClientWallet;
const personalWalletConfig = walletInstanceToConfig.get(personalWallet);
// if personal wallet exists, we need to replace the connectParams.personalWallet to a serializable version
const _personalWallet =
wallet.getPersonalWallet() as AbstractClientWallet;
if (personalWallet && personalWalletConfig) {
const _personalWalletConfig = walletInstanceToConfig.get(_personalWallet);
if (_personalWallet && _personalWalletConfig) {
walletInfo.connectParams = {
...walletInfo.connectParams,
personalWallet: {
walletId: personalWalletConfig.id,
connectParams: personalWallet.getConnectParams(),
walletId: _personalWalletConfig.id,
connectParams: _personalWallet.getConnectParams(),
},
};
@@ -220,7 +293,13 @@ export function ThirdwebWalletProvider(
saveLastConnectedWalletInfo(walletInfo);
}
},
[],
[
isConnectingToPersonalWallet,
setPersonalWallet,
setPersonalWalletConfig,
setPersonalWalletStatus,
setPersonalWalletSigner,
],
);
const storeLastActiveChainId = useCallback(async (chainId: number) => {
@@ -250,22 +329,35 @@ export function ThirdwebWalletProvider(
const switchChain = useCallback(
async (chainId: number) => {
if (!activeWallet) {
const wallet = isConnectingToPersonalWallet
? personalWallet
: activeWallet;
if (!wallet) {
throw new Error("No active wallet");
}
await activeWallet.switchChain(chainId);
const _signer = await activeWallet.getSigner();
await storeLastActiveChainId(chainId);
await wallet.switchChain(chainId);
const _signer = await wallet.getSigner();
setSigner(_signer);
if (!isConnectingToPersonalWallet) {
await storeLastActiveChainId(chainId);
setSigner(_signer);
} else {
setPersonalWalletSigner(_signer);
}
},
[activeWallet, storeLastActiveChainId],
[
isConnectingToPersonalWallet,
personalWallet,
activeWallet,
storeLastActiveChainId,
setPersonalWalletSigner,
],
);
const autoConnectTriggered = useRef(false);
// Auto Connect
const autoConnectTriggered = useRef(false);
useEffect(() => {
// do not auto connect if signerWallet is given
if (props.signerWallet) {
@@ -278,7 +370,7 @@ export function ThirdwebWalletProvider(
// if explicitly set to false, don't auto connect
// by default, auto connect
if (props.shouldAutoConnect === false) {
setConnectionStatus("disconnected");
setStatus("disconnected");
return;
}
@@ -287,18 +379,18 @@ export function ThirdwebWalletProvider(
return;
}
if (connectionStatus !== "unknown") {
if (status !== "unknown") {
// only try to auto connect if we're in the unknown state
return;
}
autoConnectTriggered.current = true;
async function autoconnect() {
async function autoConnect() {
const walletInfo = await getLastConnectedWalletInfo();
if (!walletInfo) {
setConnectionStatus("disconnected");
setStatus("disconnected");
return;
}
@@ -308,7 +400,7 @@ export function ThirdwebWalletProvider(
if (!walletObj) {
// last connected wallet is no longer present in the supported wallets
setConnectionStatus("disconnected");
setStatus("disconnected");
return;
}
@@ -317,12 +409,13 @@ export function ThirdwebWalletProvider(
if (personalWalletInfo) {
const personalWallets = walletObj.personalWallets || [];
const personalWalleObj = personalWallets.find(
const personalWalletObj = personalWallets.find(
(W) => W.id === personalWalletInfo.walletId,
);
if (personalWalleObj) {
if (personalWalletObj) {
// create a personal wallet instance and auto connect it
const personalWalletInstance = createWalletInstance(personalWalleObj);
const personalWalletInstance =
createWalletInstance(personalWalletObj);
try {
await timeoutPromise(
@@ -337,7 +430,7 @@ export function ThirdwebWalletProvider(
} catch (e) {
console.error("Failed to auto connect personal wallet");
console.error(e);
setConnectionStatus("disconnected");
setStatus("disconnected");
return;
}
@@ -348,7 +441,7 @@ export function ThirdwebWalletProvider(
};
} else {
// last used personal wallet is no longer present in the supported wallets
setConnectionStatus("disconnected");
setStatus("disconnected");
return;
}
}
@@ -357,7 +450,7 @@ export function ThirdwebWalletProvider(
const wallet = createWalletInstance(walletObj);
try {
setConnectionStatus("connecting");
setStatus("connecting");
await timeoutPromise(wallet.autoConnect(walletInfo.connectParams), {
ms: autoConnectTimeout,
message: autoConnectTimeoutErrorMessage,
@@ -374,20 +467,21 @@ export function ThirdwebWalletProvider(
LAST_CONNECTED_WALLET_STORAGE_KEY,
);
}
setConnectionStatus("disconnected");
setStatus("disconnected");
}
}
autoconnect();
autoConnect();
}, [
createWalletInstance,
props.supportedWallets,
setConnectedWallet,
props.shouldAutoConnect,
activeWallet,
connectionStatus,
status,
autoConnectTimeout,
props.signerWallet,
setStatus,
]);
const connectWallet = useCallback(
@@ -400,7 +494,12 @@ export function ThirdwebWalletProvider(
};
const wallet = createWalletInstance(WalletObj);
setConnectionStatus("connecting");
if (isConnectingToPersonalWallet) {
setPersonalWalletStatus("connecting");
} else {
setStatus("connecting");
}
try {
// if magic is using social login - it will redirect the page - so need to save walletInfo before connecting
// TODO: find a better way to handle this
@@ -414,41 +513,65 @@ export function ThirdwebWalletProvider(
setConnectedWallet(wallet, _connectedParams);
} catch (e: any) {
console.error(`Error connecting to wallet: ${e}`);
setConnectionStatus("disconnected");
if (isConnectingToPersonalWallet) {
setPersonalWalletStatus("disconnected");
} else {
setStatus("disconnected");
}
throw e;
}
return wallet;
},
[createWalletInstance, setConnectedWallet, chainToConnect],
[
chainToConnect?.chainId,
createWalletInstance,
isConnectingToPersonalWallet,
setPersonalWalletStatus,
setConnectedWallet,
],
);
const onWalletDisconnect = useCallback(async () => {
await lastConnectedWalletStorage.removeItem(
LAST_CONNECTED_WALLET_STORAGE_KEY,
);
setConnectionStatus("disconnected");
setSigner(undefined);
setActiveWallet(undefined);
setActiveWalletConfig(undefined);
}, []);
if (isConnectingToPersonalWallet) {
setPersonalWalletStatus("disconnected");
setPersonalWalletSigner(undefined);
setPersonalWallet(undefined);
setPersonalWalletConfig(undefined);
} else {
setStatus("disconnected");
setSigner(undefined);
setActiveWallet(undefined);
setActiveWalletConfig(undefined);
}
}, [
isConnectingToPersonalWallet,
setPersonalWallet,
setPersonalWalletStatus,
setPersonalWalletSigner,
setPersonalWalletConfig,
]);
const disconnectWallet = useCallback(async () => {
// if disconnect is called before the wallet is connected
if (!activeWallet) {
onWalletDisconnect();
return;
const wallet = isConnectingToPersonalWallet ? personalWallet : activeWallet;
if (wallet) {
const _personalWallet = wallet.getPersonalWallet();
await wallet.disconnect();
if (_personalWallet instanceof AbstractClientWallet) {
await _personalWallet.disconnect();
}
}
const personalWallet = activeWallet.getPersonalWallet();
await activeWallet.disconnect();
if (personalWallet) {
await (personalWallet as AbstractClientWallet)?.disconnect();
}
onWalletDisconnect();
}, [activeWallet, onWalletDisconnect]);
}, [
activeWallet,
isConnectingToPersonalWallet,
onWalletDisconnect,
personalWallet,
]);
// when wallet's network or account is changed using the extension, update UI
useEffect(() => {
@@ -461,19 +584,20 @@ export function ThirdwebWalletProvider(
setSigner(_signer);
};
activeWallet.addListener("change", () => {
update();
});
activeWallet.addListener("disconnect", () => {
onWalletDisconnect();
});
activeWallet.addListener("change", update);
activeWallet.addListener("disconnect", onWalletDisconnect);
return () => {
activeWallet.removeListener("change");
activeWallet.removeListener("disconnect");
activeWallet.removeListener("change", update);
activeWallet.removeListener("disconnect", onWalletDisconnect);
};
}, [activeWallet, onWalletDisconnect]);
}, [
activeWallet,
isConnectingToPersonalWallet,
onWalletDisconnect,
personalWallet,
setPersonalWalletSigner,
setSigner,
]);
// connect signerWallet immediately if it's passed
// and disconnect it if it's not passed
@@ -502,6 +626,17 @@ export function ThirdwebWalletProvider(
disconnectWallet,
]);
const setConnectionStatus = useCallback(
(value: ConnectionStatus) => {
if (isConnectingToPersonalWallet) {
setPersonalWalletStatus(value);
} else {
setStatus(value);
}
},
[isConnectingToPersonalWallet, setPersonalWalletStatus],
);
return (
<ThirdwebWalletContext.Provider
value={{
@@ -511,8 +646,8 @@ export function ThirdwebWalletProvider(
signer,
activeWallet,
activeWalletConfig,
connectionStatus,
setConnectionStatus,
connectionStatus: status,
setConnectionStatus: setConnectionStatus,
createWalletInstance: createWalletInstance,
createdWalletInstance: createdWalletInstance,
createWalletStorage: props.createWalletStorage,
@@ -525,6 +660,17 @@ export function ThirdwebWalletProvider(
},
activeChainSetExplicitly: props.activeChainSetExplicitly,
clientId: props.clientId,
isConnectingToPersonalWallet,
setIsConnectingToPersonalWallet,
personalWalletInfo: {
wallet: personalWalletConnection.wallet,
signer: personalWalletConnection.signer,
walletConfig: personalWalletConnection.walletConfig,
connectionStatus: personalWalletConnection.status,
chainId: personalWalletConnection.chainId,
disconnect: personalWalletConnection.disconnect,
switchChain: personalWalletConnection.switchChain,
},
}}
>
{props.children}
@@ -603,3 +749,79 @@ function timeoutPromise<T>(
}
const autoConnectTimeoutErrorMessage = `Failed to Auto connect. Auto connect timed out. You can increase the timeout duration using the autoConnectTimeout prop on <ThirdwebProvider />`;
function usePersonalWalletConnection() {
const [signer, setSigner] = useState<Signer | undefined>(undefined);
const [status, setStatus] = useState<ConnectionStatus>("unknown");
const [wallet, setWallet] = useState<WalletInstance | undefined>();
const [walletConfig, setWalletConfig] = useState<WalletConfig | undefined>();
const [chainId, setChainId] = useState<number | undefined>(undefined);
const handleDisconnect = useCallback(async () => {
setStatus("disconnected");
setSigner(undefined);
setWallet(undefined);
setWalletConfig(undefined);
}, []);
const disconnect = useCallback(async () => {
if (wallet) {
await wallet.disconnect();
}
handleDisconnect();
}, [wallet, handleDisconnect]);
useEffect(() => {
if (wallet) {
const updateChainId = () => {
wallet?.getChainId().then((_chainId) => {
setChainId(_chainId);
});
};
const update = async () => {
updateChainId();
const _signer = await wallet.getSigner();
setSigner(_signer);
};
updateChainId();
wallet.addListener("change", update);
wallet.addListener("disconnect", handleDisconnect);
return () => {
wallet.removeListener("change", update);
wallet.removeListener("disconnect", handleDisconnect);
};
} else {
setChainId(undefined);
}
}, [disconnect, handleDisconnect, wallet]);
const switchChain = useCallback(
async (_chainId: number) => {
if (!wallet) {
throw new Error("No active wallet");
}
await wallet.switchChain(_chainId);
const _signer = await wallet.getSigner();
setSigner(_signer);
},
[wallet],
);
return {
signer: signer,
setSigner: setSigner,
wallet: wallet,
setWallet: setWallet,
walletConfig: walletConfig,
setWalletConfig: setWalletConfig,
status: status,
setStatus: setStatus,
chainId: chainId,
disconnect,
switchChain,
};
}
@@ -0,0 +1,27 @@
import { useEffect, useState } from "react";
import { useWallet } from "../../core/hooks/wallet-hooks";
export const usePersonalWalletAddress = () => {
const [personalWalletAddress, setPersonalWalletAddress] = useState<
string | undefined
>();
const activeWallet = useWallet();
useEffect(() => {
(async () => {
const possiblePersonalWallet = activeWallet?.getPersonalWallet();
if (!possiblePersonalWallet) {
setPersonalWalletAddress(undefined);
return;
}
const address = await (
await possiblePersonalWallet?.getSigner()
)?.getAddress();
setPersonalWalletAddress(address);
})();
}, [activeWallet]);
return personalWalletAddress;
};
+1
View File
@@ -22,6 +22,7 @@ export {
} from "../core/hooks/wallet-hooks";
export { useNetwork } from "../core/hooks/useNetwork";
export { useWalletContext } from "../core/providers/thirdweb-wallet-provider";
export { usePersonalWalletAddress } from "./hooks/usePersonalWalletAddress";
// connected wallet hooks
export {
+10
View File
@@ -1,5 +1,15 @@
# @thirdweb-dev/react-native-compat
## 0.4.9
## 0.4.8
## 0.4.7
## 0.4.6
## 0.4.5
## 0.4.4
## 0.4.3
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@thirdweb-dev/react-native-compat",
"description": "Shims for Thirdweb in React Native Projects",
"version": "0.4.4",
"version": "0.4.9",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+64
View File
@@ -1,5 +1,69 @@
# @thirdweb-dev/react-native
## 0.4.9
### Patch Changes
- [#1971](https://github.com/thirdweb-dev/js/pull/1971) [`12d85dec`](https://github.com/thirdweb-dev/js/commit/12d85dec10f47d6520b737a23576e4f832abb47f) Thanks [@iketw](https://github.com/iketw)! - Adds usePersonalWalletAddress hook to React/RN
```typescript
// React
import { usePersonalWalletAddress } from "@thirdweb-dev/react";
const personalWalletAddress = usePersonalWalletAddress();
```
```typescript
// React-Native
import { usePersonalWalletAddress } from "@thirdweb-dev/react-native";
const personalWalletAddress = usePersonalWalletAddress();
```
- [#1989](https://github.com/thirdweb-dev/js/pull/1989) [`bf27b887`](https://github.com/thirdweb-dev/js/commit/bf27b8872870c08bb19934d87e574e0477c9550f) Thanks [@iketw](https://github.com/iketw)! - Improves EmbeddedWallet error messages in React Native
- Updated dependencies [[`fb0cb676`](https://github.com/thirdweb-dev/js/commit/fb0cb676b996fe386dcc53b5a6dab03f88e76175), [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1), [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4), [`12d85dec`](https://github.com/thirdweb-dev/js/commit/12d85dec10f47d6520b737a23576e4f832abb47f)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.4.8
### Patch Changes
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96), [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.4.7
### Patch Changes
- Updated dependencies [[`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4), [`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.4.6
### Patch Changes
- [#1945](https://github.com/thirdweb-dev/js/pull/1945) [`69d68f5a`](https://github.com/thirdweb-dev/js/commit/69d68f5a25877bf5ea694719d2cf4ca8388f3091) Thanks [@iketw](https://github.com/iketw)! - Enable Facebook Sign In for embedded wallet
- Updated dependencies [[`69d68f5a`](https://github.com/thirdweb-dev/js/commit/69d68f5a25877bf5ea694719d2cf4ca8388f3091)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.4.5
### Patch Changes
- Updated dependencies [[`9f993dcc`](https://github.com/thirdweb-dev/js/commit/9f993dcc1e60cc087850b8fb9ce09524073ce0b5)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.4.4
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.4.4",
"version": "0.4.9",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -13,12 +13,12 @@ import { ModalHeaderTextClose } from "../base/modal/ModalHeaderTextClose";
import {
shortenAddress,
useAddress,
usePersonalWalletAddress,
useWallet,
} from "@thirdweb-dev/react-core";
import { PasswordInput } from "../PasswordInput";
import * as FileSystem from "expo-file-system";
import { SmartWallet } from "@thirdweb-dev/wallets";
import { usePersonalWalletAddress } from "../../wallets/hooks/usePersonalWalletAddress";
import { LocalWallet } from "../../wallets/wallets/LocalWallet";
import { TWModal } from "../base/modal/TWModal";
import { useLocale } from "../../providers/ui-context-provider";
@@ -176,16 +176,17 @@ export const ExportLocalWalletModal = ({
<Text variant="bodySmall" color="red" mt="xs" textAlign="left">
{error}
</Text>
<Box flexDirection="row" justifyContent="flex-end" mt="lg">
<Box flexDirection="row" justifyContent="flex-end" mt="xs">
<BaseButton
backgroundColor="white"
backgroundColor="accentButtonColor"
borderColor="accentButtonColor"
style={styles.modalButton}
onPress={onContinuePress}
>
{isExporting ? (
<ActivityIndicator size="small" color="black" />
) : (
<Text variant="bodySmall" color="black">
<Text variant="bodySmall" color="accentButtonTextColor">
{l.connect_wallet_details.backup}
</Text>
)}
@@ -1,7 +1,7 @@
import { walletIds } from "@thirdweb-dev/wallets";
import { ModalHeaderTextClose } from "../../base/modal/ModalHeaderTextClose";
import { WalletConfig } from "@thirdweb-dev/react-core";
import { ReactNode, useState } from "react";
import { ReactNode, useMemo, useState } from "react";
import Box from "../../base/Box";
import Text from "../../base/Text";
import ThirdwebLogo from "../../../assets/thirdweb-logo";
@@ -33,7 +33,6 @@ export function ChooseWallet({
onChooseWallet,
modalTitleIconUrl,
onClose,
excludeWalletIds = [],
termsOfServiceUrl,
privacyPolicyUrl,
}: ChooseWalletProps) {
@@ -49,14 +48,25 @@ export function ChooseWallet({
w.id === walletIds.magicLink ||
(w.id === walletIds.embeddedWallet && w.selectUI),
);
const connectionWallets = wallets
.filter(
const walletsToDisplay = useMemo(() => {
const filteredWallets = wallets.filter(
(wallet) =>
wallet.id !== walletIds.magicLink &&
wallet.id !== walletIds.embeddedWallet &&
wallet.id !== walletIds.localWallet,
)
.slice(0, 2);
);
const trueItems = filteredWallets.filter(
(item) => item.recommended === true,
);
const falseItems = filteredWallets.filter(
(item) => item.recommended !== true,
);
const sortedWallets = [...trueItems, ...falseItems];
return sortedWallets;
}, [wallets]);
const showToSPrivacyPolicy = termsOfServiceUrl || privacyPolicyUrl;
@@ -74,7 +84,7 @@ export function ChooseWallet({
};
const onSingleWalletPress = () => {
onChooseWallet(connectionWallets[0]);
onChooseWallet(walletsToDisplay[0]);
};
const onBackPress = () => {
@@ -135,13 +145,7 @@ export function ChooseWallet({
{!emailWallet || isConnectAWalletEnabled ? (
<>
<ChooseWalletContent
wallets={wallets}
excludeWalletIds={[
...excludeWalletIds,
walletIds.localWallet,
walletIds.magicLink,
walletIds.embeddedWallet,
]}
wallets={walletsToDisplay}
onChooseWallet={onChooseWallet}
/>
@@ -176,7 +180,7 @@ export function ChooseWallet({
) : null}
{emailWallet &&
!isConnectAWalletEnabled &&
(guestWallet || connectionWallets.length > 0) ? (
(guestWallet || walletsToDisplay.length > 0) ? (
<Box
mt="md"
marginHorizontal="xl"
@@ -193,15 +197,15 @@ export function ChooseWallet({
) : null}
{emailWallet &&
!isConnectAWalletEnabled &&
connectionWallets.length > 0 ? (
connectionWallets.length === 1 ? (
walletsToDisplay.length > 0 ? (
walletsToDisplay.length === 1 ? (
<WalletButton
marginHorizontal="xl"
paddingHorizontal="none"
paddingVertical="none"
mt="md"
walletIconUrl={connectionWallets[0].meta.iconURL}
name={connectionWallets[0].meta.name}
walletIconUrl={walletsToDisplay[0].meta.iconURL}
name={walletsToDisplay[0].meta.name}
onPress={onSingleWalletPress}
/>
) : (
@@ -217,7 +221,10 @@ export function ChooseWallet({
borderWidth={1}
onPress={onConnectAWalletPress}
>
{connectionWallets.map((wallet) => {
{walletsToDisplay.map((wallet, index) => {
if (index > 1) {
return null;
}
return (
<Box key={wallet.meta.name} mr="xxs">
<ImageSvgUri
@@ -1,4 +1,3 @@
import { useMemo } from "react";
import type { WalletConfig } from "@thirdweb-dev/react-core";
import { Dimensions, ScrollView, View } from "react-native";
import { WalletButton } from "../../base/WalletButton";
@@ -7,7 +6,6 @@ import { useTheme } from "@shopify/restyle";
interface ChooseWalletContentProps {
wallets: WalletConfig[];
excludeWalletIds?: string[];
onChooseWallet: (wallet: WalletConfig, data?: any) => void;
}
@@ -15,27 +13,10 @@ const MAX_HEIGHT = Dimensions.get("window").height * 0.3;
export const ChooseWalletContent = ({
wallets,
excludeWalletIds,
onChooseWallet,
}: ChooseWalletContentProps) => {
const theme = useTheme();
const walletsToDisplay = useMemo(() => {
const filteredWallets = wallets.filter(
(w) => !!!excludeWalletIds?.find((ewId) => ewId === w.id),
);
const trueItems = filteredWallets.filter(
(item) => item.recommended === true,
);
const falseItems = filteredWallets.filter(
(item) => item.recommended !== true,
);
const sortedWallets = [...trueItems, ...falseItems];
return sortedWallets;
}, [wallets, excludeWalletIds]);
return (
<View style={{ flexDirection: "column", maxHeight: MAX_HEIGHT }}>
<ScrollView
@@ -45,9 +26,8 @@ export const ChooseWalletContent = ({
paddingHorizontal: 16,
}}
>
{walletsToDisplay.map((item, index) => {
const marginBottom =
index === walletsToDisplay.length - 1 ? "none" : "xxs";
{wallets.map((item, index) => {
const marginBottom = index === wallets.length - 1 ? "none" : "xxs";
return (
<Box key={item.id}>
{item.selectUI ? (
@@ -188,14 +188,21 @@ export const LocalWalletImportModal = ({
{l.common.password}
</Text>
<PasswordInput onChangeText={onChangeText} />
<Text variant="bodySmall" color="red" mt="xs" textAlign="left">
{error}
</Text>
{error ? (
<Text
variant="bodySmall"
color="red"
marginVertical="xxs"
textAlign="left"
>
{error}
</Text>
) : null}
<Box
mb="xs"
flexDirection="row"
justifyContent="center"
alignItems="center"
mt="sm"
>
<Box height={1} flex={1} backgroundColor="border" />
<Text variant="bodySmall" textAlign="center" marginHorizontal="xxs">
@@ -214,23 +221,19 @@ export const LocalWalletImportModal = ({
fontFamily: theme.textVariants.defaults.fontFamily,
},
}}
containerProps={{ pl: "xxs" }}
containerProps={{ pl: "xxs", mt: "sm" }}
/>
<Box
flexDirection="row"
justifyContent="center"
paddingHorizontal="md"
mt="lg"
>
<Box flexDirection="row" justifyContent="flex-end" mt="lg">
<BaseButton
backgroundColor="white"
backgroundColor="accentButtonColor"
borderColor="accentButtonColor"
style={styles.modalButton}
onPress={onImportPress}
>
{isImporting ? (
<ActivityIndicator size="small" color="black" />
<ActivityIndicator size="small" color="accentButtonTextColor" />
) : (
<Text variant="bodySmall" color="black">
<Text variant="bodySmall" color="accentButtonTextColor">
{l.common.import}
</Text>
)}
@@ -338,6 +338,7 @@ const SendFundsForm = ({
<BaseButton
mt="md"
backgroundColor="accentButtonColor"
borderColor="accentButtonColor"
flexDirection="row"
alignItems="center"
justifyContent="center"
@@ -85,7 +85,7 @@ export const NetworkButton = ({
>
<Box flexDirection="row" alignItems="center">
<ChainIcon chainIconUrl={chain?.icon?.url} size={28} />
<Box ml="md" alignItems="flex-start" justifyContent="center">
<Box ml="sm" alignItems="flex-start" justifyContent="center">
<Text variant="bodyLarge">
{chain?.name || l.common.unknown_network}
</Text>
@@ -161,8 +161,10 @@ export const SwitchChainModal = ({
return (
<NetworkButton
mt="xxs"
padding="none"
key={chain.chainId}
backgroundColor="backgroundHighlight"
backgroundColor="transparent"
borderColor="transparent"
chain={chain}
enableSwitchModal={false}
switchChainOnPress={true}
@@ -1,5 +1,4 @@
import BaseButton from "./BaseButton";
import Box from "./Box";
import ImageSvgUri from "./ImageSvgUri";
import Text from "./Text";
@@ -22,16 +21,14 @@ export const SquareButton = ({
flexDirection="column"
alignItems="center"
justifyContent="center"
borderWidth={0}
borderWidth={1}
borderRadius="sm"
width={size}
height={size}
borderColor="border"
padding="sm"
onPress={onPress}
{...props}
>
<Box borderWidth={0} borderRadius="lg">
<ImageSvgUri imageUrl={iconUrl} width={size} height={size} />
</Box>
<ImageSvgUri imageUrl={iconUrl} width={size} height={size} />
{name ? (
<Text
variant="bodySmallSecondary"
@@ -26,6 +26,7 @@ export type Palette = {
black: string;
white: string;
red: string;
transparent: string;
warning: string;
@@ -38,6 +39,7 @@ export const paletteBase = {
red: "red",
warning: "#F46565",
error: "#D85252",
transparent: "transparent",
};
export const paletteLight: Palette = {
@@ -19,8 +19,11 @@ import {
import { getEthersSigner } from "./embedded/signer";
import { logoutUser } from "./embedded/helpers/auth/logout";
import {
clearConnectedAuthStrategy,
clearConnectedEmail,
getConnectedAuthStrategy,
getConnectedEmail,
saveConnectedAuthStrategy,
saveConnectedEmail,
} from "./embedded/helpers/storage/local";
import {
@@ -39,11 +42,15 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
email?: string;
connectedAuthStrategy?: AuthParams["strategy"];
constructor(options: EmbeddedWalletConnectorOptions) {
super();
this.options = options;
this.email = getConnectedEmail();
this.connectedAuthStrategy = getConnectedAuthStrategy();
}
async connect(options?: EmbeddedWalletConnectionArgs) {
@@ -59,14 +66,12 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
await this.switchChain(options.chainId);
}
if (this.email) {
saveConnectedEmail(this.email);
}
return this.getAddress();
}
async authenticate(params: AuthParams): Promise<AuthResult> {
const strategy = params.strategy;
this.connectedAuthStrategy = strategy;
switch (strategy) {
case "email_verification": {
return await this.validateEmailOTP({
@@ -172,7 +177,6 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
this.options.clientId,
);
this.email = email;
saveConnectedEmail(email);
return {
user: {
@@ -228,6 +232,7 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
async disconnect(): Promise<void> {
clearConnectedEmail();
clearConnectedAuthStrategy();
await logoutUser(this.options.clientId);
await this.onDisconnect();
this.signer = undefined;
@@ -274,6 +279,13 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
);
}
if (this.email) {
saveConnectedEmail(this.email);
}
if (this.connectedAuthStrategy) {
saveConnectedAuthStrategy(this.connectedAuthStrategy);
}
return signer;
}
@@ -347,6 +359,10 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
getEmail() {
return this.email;
}
getConnectedAuthStrategy() {
return this.connectedAuthStrategy;
}
}
function assertUnreachable(x: never): never {
@@ -25,11 +25,13 @@ import { getCognitoUser, setCognitoUser } from "./helpers/storage/state";
import { isDeviceSharePresentForUser } from "./helpers/storage/local";
import { Auth } from "aws-amplify";
import {
DOMAIN_URL_2023,
ROUTE_AUTH_JWT_CALLBACK,
ROUTE_HEADLESS_OAUTH_LOGIN,
} from "./helpers/constants";
import { AuthOptions, OauthOption, VerifiedTokenResponse } from "../types";
import { InAppBrowser } from "react-native-inappbrowser-reborn";
import { createErrorMessage } from "./helpers/errors";
export async function sendVerificationEmail(options: {
email: string;
@@ -50,7 +52,7 @@ export async function sendVerificationEmail(options: {
});
} catch (e) {
throw new Error(
`Malformed response from the send email OTP API: ${JSON.stringify(e)}`,
createErrorMessage("Malformed response from the send email OTP API", e),
);
}
@@ -102,7 +104,7 @@ export async function validateEmailOTP(options: {
});
} catch (e) {
throw new Error(
`Malformed response from the send email OTP API: ${JSON.stringify(e)}`,
createErrorMessage("Malformed response validating the OTP", e),
);
}
let verifiedTokenResponse: VerifiedTokenResponse;
@@ -163,9 +165,10 @@ export async function validateEmailOTP(options: {
return { storedToken };
} catch (e) {
throw new Error(
`Malformed response from the verify one time password: ${
(e as Error).message
}}`,
createErrorMessage(
"Malformed response from the verify one time password",
e,
),
);
}
}
@@ -173,7 +176,7 @@ export async function validateEmailOTP(options: {
export async function socialLogin(oauthOptions: OauthOption, clientId: string) {
const encodedProvider = encodeURIComponent(oauthOptions.provider);
const headlessLoginLinkWithParams = `${ROUTE_HEADLESS_OAUTH_LOGIN}?authProvider=${encodedProvider}&baseUrl=${encodeURIComponent(
`https://embedded-wallet.thirdweb.com`,
DOMAIN_URL_2023,
)}&platform=${encodeURIComponent("mobile")}`;
const resp = await fetch(headlessLoginLinkWithParams);
@@ -188,7 +191,7 @@ export async function socialLogin(oauthOptions: OauthOption, clientId: string) {
const { platformLoginLink } = json;
const completeLoginUrl = `${platformLoginLink}?developerClientId=${encodeURIComponent(
clientId || "",
clientId,
)}&platform=${encodeURIComponent("mobile")}&redirectUrl=${encodeURIComponent(
oauthOptions.redirectUrl,
)}&authOption=${encodedProvider}`;
@@ -220,7 +223,7 @@ export async function socialLogin(oauthOptions: OauthOption, clientId: string) {
if (parts.length < 2) {
// assume error
const error = decodedUrl.split("?error=")?.[1];
throw new Error(`Something went wrong. Please, try again later: ${error}`);
throw new Error(`Something went wrong: ${error}`);
}
const authResult = parts[1];
@@ -243,7 +246,7 @@ export async function socialLogin(oauthOptions: OauthOption, clientId: string) {
return { storedToken, email: storedToken.authDetails.email };
} catch (e) {
throw new Error(
`Malformed response from post authentication: ${JSON.stringify(e)}`,
createErrorMessage("Malformed response from post authentication", e),
);
}
}
@@ -285,7 +288,7 @@ export async function customJwt(authOptions: AuthOptions, clientId: string) {
return { verifiedToken, email: verifiedToken.authDetails.email };
} catch (e) {
throw new Error(
`Malformed response from post authentication: ${JSON.stringify(e)}`,
createErrorMessage("Malformed response from post authentication", e),
);
}
}
@@ -18,6 +18,7 @@ import {
IsValidUserManagedEmailOTPResponse,
VerifiedTokenResponse,
} from "../../../types";
import { createErrorMessage } from "../errors";
const EMBEDDED_WALLET_TOKEN_HEADER = "embedded-wallet-token";
const PAPER_CLIENT_ID_HEADER = "x-thirdweb-client-id";
@@ -281,7 +282,7 @@ export async function getUserShares(clientId: string, getShareUrl: URL) {
};
} catch (e) {
throw new Error(
`Malformed response from the ews user wallet API: ${JSON.stringify(e)}`,
createErrorMessage("Malformed response from the ews user wallet API", e),
);
}
}
@@ -19,7 +19,8 @@ export const COGNITO_IDENTITY_POOL_ID =
export const GENERATE_RECOVERY_PASSWORD_LAMBDA_FUNCTION =
"arn:aws:lambda:us-west-2:324457261097:function:recovery-share-password-GenerateRecoverySharePassw-bbE5ZbVAToil";
const BASE_URL_2023 = "https://embedded-wallet.thirdweb.com/";
export const DOMAIN_URL_2023 = "https://embedded-wallet.thirdweb.com";
const BASE_URL_2023 = `${DOMAIN_URL_2023}/`;
const ROUTE_2023_10_20_API_BASE_PATH = `${BASE_URL_2023}api/2023-10-20`;
export const ROUTE_GET_EMBEDDED_WALLET_DETAILS = `${ROUTE_2023_10_20_API_BASE_PATH}/embedded-wallet/embedded-wallet-user-details`;
@@ -3,3 +3,11 @@ export const ErrorMessages = {
"Your OTP code is invalid or expired. Please request a new code or try again.",
missingRecoveryCode: "Missing encryption key for user",
};
export const createErrorMessage = (message: string, error: any): string => {
if (error instanceof Error) {
return `${message}: ${error.message}`;
} else {
return `${message}: ${JSON.stringify(error)}`;
}
};
@@ -6,8 +6,11 @@ import {
} from "@paperxyz/embedded-wallet-service-sdk";
import { MMKV } from "react-native-mmkv";
import { DEVICE_SHARE_MISSING_MESSAGE } from "../constants";
import { AuthParams } from "../../../types";
const CONNECTED_EMAIL_LOCAL_STORAGE_NAME = "embedded-wallet-connected-email";
const CONNECTED_AUTH_STRATEGY_LOCAL_STORAGE_NAME =
"embedded-wallet-connected-auth-params";
const storage = new MMKV();
@@ -34,6 +37,23 @@ export function clearConnectedEmail() {
removeItemInAsyncStorage(CONNECTED_EMAIL_LOCAL_STORAGE_NAME);
}
export function getConnectedAuthStrategy(): AuthParams["strategy"] | undefined {
return getItemFromAsyncStorage(CONNECTED_AUTH_STRATEGY_LOCAL_STORAGE_NAME) as
| AuthParams["strategy"]
| undefined;
}
export function saveConnectedAuthStrategy(authStrategy: string) {
setItemInAsyncStorage(
CONNECTED_AUTH_STRATEGY_LOCAL_STORAGE_NAME,
authStrategy,
);
}
export function clearConnectedAuthStrategy() {
removeItemInAsyncStorage(CONNECTED_AUTH_STRATEGY_LOCAL_STORAGE_NAME);
}
export async function isDeviceSharePresentForUser(
clientId: string,
walletUserId?: string,
@@ -12,6 +12,7 @@ import {
} from "../constants";
import { setDeviceShare } from "../storage/local";
import { encryptShareWeb } from "./encryption";
import { createErrorMessage } from "../errors";
export async function setUpNewUserWallet(
recoveryCode: string,
@@ -145,9 +146,10 @@ export async function storeShares<R extends string | undefined>({
}
} catch (e) {
throw new Error(
`Malformed response from the ews store user share API: ${JSON.stringify(
createErrorMessage(
"Malformed response from the ews store user share API",
e,
)}`,
),
);
}
}
@@ -1,23 +0,0 @@
import { useWallet } from "@thirdweb-dev/react-core";
import { AbstractClientWallet, SmartWallet } from "@thirdweb-dev/wallets";
import { useEffect, useState } from "react";
export const usePersonalWalletAddress = () => {
const [personalWalletAddress, setPersonalWalletAddress] = useState<
string | undefined
>();
const activeWallet = useWallet();
useEffect(() => {
(async () => {
if (activeWallet?.walletId === SmartWallet.id) {
const address = await (
activeWallet.getPersonalWallet() as AbstractClientWallet
).getAddress();
setPersonalWalletAddress(address);
}
})();
}, [activeWallet]);
return personalWalletAddress;
};
@@ -1,9 +1,14 @@
import { APPLE_ICON, EMAIL_WALLET_ICON, GOOGLE_ICON } from "../../assets";
import {
APPLE_ICON,
EMAIL_WALLET_ICON,
FACEBOOK_ICON,
GOOGLE_ICON,
} from "../../assets";
import { LocaleType } from "../../i18n/types";
export type AuthOption = "email" | SocialLogin;
export type SocialLogin = "google" | "apple";
export type SocialLogin = "google" | "apple" | "facebook";
export type AuthOptionDictionary = Record<AuthOption, string>;
@@ -11,6 +16,7 @@ export const AUTH_OPTIONS_ICONS: AuthOptionDictionary = {
google: GOOGLE_ICON,
email: EMAIL_WALLET_ICON,
apple: APPLE_ICON,
facebook: FACEBOOK_ICON,
};
export const AUTH_OPTIONS_TEXT: Record<
@@ -20,4 +26,5 @@ export const AUTH_OPTIONS_TEXT: Record<
google: "sign_in_google",
email: "sign_in_email",
apple: "sign_in_apple",
facebook: "sign_in_facebook",
};
@@ -108,9 +108,9 @@ export const EmailSelectionUI: React.FC<
iconWidth={28}
borderRadius="lg"
borderWidth={1}
borderColor="buttonBackgroundColor"
backgroundColor="buttonBackgroundColor"
nameColor="buttonTextColor"
borderColor="backgroundHighlight"
backgroundColor="backgroundHighlight"
nameColor="textPrimary"
flex={1}
justifyContent="center"
name={l.embedded_wallet[AUTH_OPTIONS_TEXT[socialLogins[0]]]}
@@ -118,15 +118,18 @@ export const EmailSelectionUI: React.FC<
onPress={() => onProviderPress(socialLogins[0])}
/>
) : (
socialLogins.map((provider, index) => (
<SquareButton
key={provider}
ml={index === 0 ? 0 : "md"}
onPress={() => onProviderPress(provider)}
iconUrl={AUTH_OPTIONS_ICONS[provider]}
size={40}
/>
))
<Box flexDirection="row" width="100%">
{socialLogins.map((provider, index) => (
<SquareButton
key={provider}
ml={index === 0 ? 0 : "md"}
flex={1}
onPress={() => onProviderPress(provider)}
iconUrl={AUTH_OPTIONS_ICONS[provider]}
size={32}
/>
))}
</Box>
)}
</Box>
{isEmailEnabled && supportedWallets.length === 1 ? (
@@ -148,7 +151,7 @@ export const EmailSelectionUI: React.FC<
<Box height={1} flex={1} backgroundColor="border" />
</Box>
) : (
<Box mt="md" />
<Box mt={isEmailEnabled ? "md" : "none"} />
)}
</Box>
) : null}
@@ -187,8 +190,8 @@ export const EmailSelectionUI: React.FC<
paddingVertical="md"
borderRadius="lg"
borderWidth={1}
borderColor="border"
backgroundColor="accentButtonColor"
borderColor="accentButtonColor"
onPress={handleNetworkCall}
>
{isFetching ? (
@@ -11,6 +11,8 @@ import {
EmbeddedWalletConnectionArgs,
} from "../../connectors/embedded-wallet/types";
import { EMAIL_WALLET_ICON } from "../../../assets/svgs";
import { WalletMeta } from "@thirdweb-dev/wallets/dist/declarations/src/evm/wallets/base";
import { AUTH_OPTIONS_ICONS } from "../../types/embedded-wallet";
export type EmbeddedWalletOptions = WalletOptions<
EmbeddedWalletAdditionalOptions & WalletConnectReceiverConfig
@@ -79,6 +81,22 @@ export class EmbeddedWallet extends AbstractClientWallet<
return connector.authenticate(params);
}
getMeta(): WalletMeta {
const strategy = this.connector?.getConnectedAuthStrategy();
const meta = (this.constructor as typeof AbstractClientWallet).meta;
switch (strategy) {
case "facebook":
case "apple":
case "google":
return {
...meta,
iconURL: AUTH_OPTIONS_ICONS[strategy],
};
default:
return meta;
}
}
onConnected = () => {
this.emit("message", { type: "connected" });
};
+67
View File
@@ -1,5 +1,72 @@
# @thirdweb-dev/react
## 4.1.9
### Patch Changes
- [#1952](https://github.com/thirdweb-dev/js/pull/1952) [`fb0cb676`](https://github.com/thirdweb-dev/js/commit/fb0cb676b996fe386dcc53b5a6dab03f88e76175) Thanks [@MananTank](https://github.com/MananTank)! - - Add `ConnectEmbed`` component to embed the ConnectWallet's Modal UI directly in page
- Fix `ConnectWallet` component setting personal wallet as "connected" wallet when connecting a Safe / Smart Wallet
- [#1921](https://github.com/thirdweb-dev/js/pull/1921) [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Add Coin98 wallet
- Updated dependencies [[`fb0cb676`](https://github.com/thirdweb-dev/js/commit/fb0cb676b996fe386dcc53b5a6dab03f88e76175), [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1), [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4), [`12d85dec`](https://github.com/thirdweb-dev/js/commit/12d85dec10f47d6520b737a23576e4f832abb47f)]:
- @thirdweb-dev/react-core@4.1.9
- @thirdweb-dev/wallets@2.1.8
- @thirdweb-dev/sdk@4.0.16
## 4.1.8
### Patch Changes
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96), [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
- @thirdweb-dev/wallets@2.1.7
- @thirdweb-dev/sdk@4.0.15
- @thirdweb-dev/react-core@4.1.8
## 4.1.7
### Patch Changes
- [#1969](https://github.com/thirdweb-dev/js/pull/1969) [`4c5e40ae`](https://github.com/thirdweb-dev/js/commit/4c5e40ae4eccdca9158a52ec0d677dec4c72ed00) Thanks [@MananTank](https://github.com/MananTank)! - Fix text alignment issue in embedded-wallet/paper
- [#1965](https://github.com/thirdweb-dev/js/pull/1965) [`13029e32`](https://github.com/thirdweb-dev/js/commit/13029e328609eecfd3f0a180d6003c03c67caac1) Thanks [@MananTank](https://github.com/MananTank)! - - Fix Minor Safari / iOS UI issues
- Adjust Magic Link's social icons UI to match EmbeddedWallet's social icons UI
- expose hook `useEmbeddedWalletUserEmail` for fetching email from connected embeddedWallet. The hook returns the `react-query` query object. See example below:
```tsx
const emailQuery = useEmbeddedWalletUserEmail();
const email = emailQuery.data;
const isFetchingEmail = emailQuery.isFetching;
```
- Updated dependencies [[`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4), [`ab2dfd4f`](https://github.com/thirdweb-dev/js/commit/ab2dfd4f5cf4256fa62d76f84ca804992afab1e4)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.1.6
### Patch Changes
- [#1954](https://github.com/thirdweb-dev/js/pull/1954) [`7912c263`](https://github.com/thirdweb-dev/js/commit/7912c263237708e9d2e9471873db7679e783b6f8) Thanks [@MananTank](https://github.com/MananTank)! - use Custom Theme context in styled components to prevent theme conflicts
- [#1945](https://github.com/thirdweb-dev/js/pull/1945) [`69d68f5a`](https://github.com/thirdweb-dev/js/commit/69d68f5a25877bf5ea694719d2cf4ca8388f3091) Thanks [@iketw](https://github.com/iketw)! - Enable Facebook Sign In for embedded wallet
- Updated dependencies [[`69d68f5a`](https://github.com/thirdweb-dev/js/commit/69d68f5a25877bf5ea694719d2cf4ca8388f3091)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.1.5
### Patch Changes
- Updated dependencies [[`9f993dcc`](https://github.com/thirdweb-dev/js/commit/9f993dcc1e60cc087850b8fb9ce09524073ce0b5)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.1.4
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "4.1.4",
"version": "4.1.9",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -60,7 +60,7 @@
"private": false,
"scripts": {
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/",
"lint": "eslint src/ && bunx publint --strict --level warning",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"generate-docs": "pnpm generate-docs:evm && pnpm generate-snippets",
+11 -12
View File
@@ -1,7 +1,6 @@
import type { Theme } from "../design-system";
import styled from "@emotion/styled";
import { Chain } from "@thirdweb-dev/chains";
import { useStorage } from "@thirdweb-dev/react-core";
import { StyledDiv } from "../design-system/elements";
const defaultChainIcon =
"ipfs://QmcxZHpyJa8T4i63xqjPYrZ6tKrt55tZJpbXcjSDKuKaf9/ethereum/512.png";
@@ -52,13 +51,13 @@ export const ChainIcon: React.FC<{
);
};
const ActiveDot = styled.div<{ theme?: Theme }>`
width: 28%;
height: 28%;
border-radius: 50%;
position: absolute;
top: 60%;
right: 0px;
background-color: #00d395;
box-shadow: 0 0 0 2px var(--bg);
`;
const ActiveDot = /* @__PURE__ */ StyledDiv({
width: "28%",
height: "28%",
borderRadius: "50%",
position: "absolute",
top: "60%",
right: 0,
backgroundColor: "#00d395",
boxShadow: `0 0 0 2px var(--bg)`,
});
+7 -4
View File
@@ -1,4 +1,4 @@
import type { Theme } from "../design-system";
import { useCustomTheme } from "../design-system/CustomThemeProvider";
import { useClipboard } from "../evm/components/hooks/useCopyClipboard";
import { ToolTip } from "./Tooltip";
import styled from "@emotion/styled";
@@ -29,6 +29,9 @@ export const CopyIcon: React.FC<{
);
};
const CheckIconStyled = /* @__PURE__ */ styled(CheckIcon)<{ theme?: Theme }>`
color: ${(p) => p.theme.colors.success};
`;
const CheckIconStyled = /* @__PURE__ */ styled(CheckIcon)(() => {
const theme = useCustomTheme();
return {
color: theme.colors.success,
};
});

Some files were not shown because too many files have changed in this diff Show More