chore: Update devDependencies versions (#2577)
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
---
|
||||
"@thirdweb-dev/react-native-compat": patch
|
||||
"@thirdweb-dev/contracts-js": patch
|
||||
"@thirdweb-dev/react-native": patch
|
||||
"@thirdweb-dev/merkletree": patch
|
||||
"@thirdweb-dev/react-core": patch
|
||||
"@thirdweb-dev/payments": patch
|
||||
"@thirdweb-dev/unity-js-bridge": patch
|
||||
"@thirdweb-dev/storage": patch
|
||||
"@thirdweb-dev/wallets": patch
|
||||
"@thirdweb-dev/chains": patch
|
||||
"@thirdweb-dev/crypto": patch
|
||||
"@thirdweb-dev/service-utils": patch
|
||||
"@thirdweb-dev/react": patch
|
||||
"@thirdweb-dev/auth": patch
|
||||
"typedoc-gen": patch
|
||||
"@thirdweb-dev/cli": patch
|
||||
"@thirdweb-dev/pay": patch
|
||||
"@thirdweb-dev/sdk": patch
|
||||
"thirdweb": patch
|
||||
---
|
||||
|
||||
update dependencies
|
||||
@@ -4,11 +4,11 @@ description: "Sets up Node.js and runs install"
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
@@ -28,14 +28,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.21
|
||||
bun-version: 1.0.35
|
||||
|
||||
- name: Install
|
||||
uses: ./.github/composite-actions/install
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
- id: get-week
|
||||
run: echo "WEEK=$(date +%U)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
timeout-minutes: 5
|
||||
id: cache-build
|
||||
with:
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
needs: build
|
||||
steps:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
check-latest: true
|
||||
@@ -68,13 +68,13 @@ jobs:
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.21
|
||||
bun-version: 1.0.35
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
timeout-minutes: 5
|
||||
id: restore-build
|
||||
with:
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
needs: build
|
||||
steps:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
check-latest: true
|
||||
@@ -99,13 +99,13 @@ jobs:
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.21
|
||||
bun-version: 1.0.35
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
timeout-minutes: 5
|
||||
id: restore-build
|
||||
with:
|
||||
@@ -120,16 +120,16 @@ jobs:
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./legacy_packages/sdk/coverage/evm/lcov.info,./packages/thirdweb/coverage/lcov.info
|
||||
files: ./packages/thirdweb/coverage/lcov.info
|
||||
|
||||
e2e:
|
||||
test_legacy:
|
||||
timeout-minutes: 15
|
||||
name: E2E Tests
|
||||
name: Unit Tests (Legacy)
|
||||
runs-on: ubuntu-latest-16
|
||||
needs: build
|
||||
steps:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
check-latest: true
|
||||
@@ -138,13 +138,49 @@ jobs:
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.21
|
||||
bun-version: 1.0.35
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
timeout-minutes: 5
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- run: pnpm test:legacy
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./legacy_packages/sdk/coverage/evm/lcov.info
|
||||
|
||||
e2e:
|
||||
timeout-minutes: 15
|
||||
name: E2E Tests
|
||||
runs-on: ubuntu-latest-16
|
||||
needs: build
|
||||
steps:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
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: 1.0.35
|
||||
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- uses: actions/cache@v4
|
||||
timeout-minutes: 5
|
||||
id: restore-build
|
||||
with:
|
||||
@@ -169,16 +205,16 @@ jobs:
|
||||
needs: build
|
||||
steps:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
check-latest: true
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
timeout-minutes: 5
|
||||
id: restore-build
|
||||
with:
|
||||
|
||||
@@ -13,12 +13,12 @@ name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main" ]
|
||||
branches: ["main"]
|
||||
schedule:
|
||||
- cron: '37 9 * * 3'
|
||||
- cron: "37 9 * * 3"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -32,41 +32,40 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
language: ["javascript"]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
@@ -24,13 +24,13 @@ jobs:
|
||||
runs-on: ubuntu-latest-16
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# bun setup is required, because pnpm typdoc will run pnpm build and some packages uses bun for build
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.21
|
||||
bun-version: 1.0.35
|
||||
|
||||
- name: Install
|
||||
uses: ./.github/composite-actions/install
|
||||
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: pull the repo.
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: true
|
||||
repository: ${{ fromJson(steps.get_pull_request_ref.outputs.data).head.repo.full_name }}
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.21
|
||||
bun-version: 1.0.35
|
||||
|
||||
- name: Install
|
||||
uses: ./.github/composite-actions/install
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
runs-on: ubuntu-latest-16
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Do not use the GITHUB_TOKEN by default
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.21
|
||||
bun-version: 1.0.35
|
||||
|
||||
- name: Install
|
||||
uses: ./.github/composite-actions/install
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.21
|
||||
bun-version: 1.0.35
|
||||
|
||||
- name: Run chains sync
|
||||
run: bun run db:sync
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
"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 240000 --parallel './test/**/*.test.ts'",
|
||||
"test": "vitest",
|
||||
"push": "yalc push"
|
||||
},
|
||||
"preconstruct": {
|
||||
@@ -80,44 +80,37 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@microsoft/api-documenter": "^7.22.30",
|
||||
"@microsoft/api-extractor": "^7.36.3",
|
||||
"@microsoft/tsdoc": "^0.14.1",
|
||||
"@microsoft/api-documenter": "^7.24.1",
|
||||
"@microsoft/api-extractor": "^7.43.0",
|
||||
"@microsoft/tsdoc": "^0.14.2",
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@swc-node/register": "^1.6.8",
|
||||
"@swc/core": "^1.3.71",
|
||||
"@swc-node/register": "^1.9.0",
|
||||
"@swc/core": "^1.4.11",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/chai": "^4.3.5",
|
||||
"@types/cookie": "^0.5.1",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/uuid": "^9.0.5",
|
||||
"chai": "^4.3.6",
|
||||
"@types/cookie": "^0.6.0",
|
||||
"@types/cookie-parser": "^1.4.7",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-inclusive-language": "^2.2.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"ethers": "^5.7.2",
|
||||
"express": "^4.18.1",
|
||||
"fastify": "^4.25.2",
|
||||
"mocha": "10.4.0",
|
||||
"next": "^13.4",
|
||||
"next-auth": "^4.22.3",
|
||||
"typescript": "^5.3.3"
|
||||
"express": "^4.19.2",
|
||||
"fastify": "^4.26.2",
|
||||
"next": "^13.5.6",
|
||||
"next-auth": "^4.24.7",
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"cookie-parser": "^1.4.6",
|
||||
"ethers": "^5",
|
||||
"express": "^4",
|
||||
"fastify": "^4.25.2",
|
||||
"next": "^12 || ^13 || ^14",
|
||||
"next": "^13.4 || ^14",
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
@@ -141,12 +134,12 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cookie": "^9.1.0",
|
||||
"@fastify/cookie": "^9.3.1",
|
||||
"@thirdweb-dev/wallets": "workspace:*",
|
||||
"cookie": "^0.5.0",
|
||||
"cookie": "^0.6.0",
|
||||
"fastify-type-provider-zod": "^1.1.9",
|
||||
"uuid": "^9.0.1",
|
||||
"zod": "^3.22.3"
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"extension": [
|
||||
"ts"
|
||||
],
|
||||
"require": [
|
||||
"@swc-node/register"
|
||||
]
|
||||
}
|
||||
@@ -1,377 +1,384 @@
|
||||
import { ThirdwebAuth } from "../src/core";
|
||||
import { EthersWallet } from "@thirdweb-dev/wallets/evm/wallets/ethers";
|
||||
import { expect } from "chai";
|
||||
import { Wallet } from "ethers";
|
||||
import { describe, it, beforeAll, beforeEach, expect } from "vitest";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("dotenv-mono").load();
|
||||
|
||||
describe("Wallet Authentication - EVM", async () => {
|
||||
let adminWallet: any, signerWallet: any, attackerWallet: any;
|
||||
let auth: ThirdwebAuth;
|
||||
describe(
|
||||
"Wallet Authentication - EVM",
|
||||
{
|
||||
timeout: 60000,
|
||||
},
|
||||
async () => {
|
||||
let adminWallet: any, signerWallet: any, attackerWallet: any;
|
||||
let auth: ThirdwebAuth;
|
||||
|
||||
before(async () => {
|
||||
const [adminSigner, signerSigner, attackerSigner] = [
|
||||
Wallet.createRandom(),
|
||||
Wallet.createRandom(),
|
||||
Wallet.createRandom(),
|
||||
];
|
||||
beforeAll(async () => {
|
||||
const [adminSigner, signerSigner, attackerSigner] = [
|
||||
Wallet.createRandom(),
|
||||
Wallet.createRandom(),
|
||||
Wallet.createRandom(),
|
||||
];
|
||||
|
||||
adminWallet = new EthersWallet(adminSigner);
|
||||
signerWallet = new EthersWallet(signerSigner);
|
||||
attackerWallet = new EthersWallet(attackerSigner);
|
||||
adminWallet = new EthersWallet(adminSigner);
|
||||
signerWallet = new EthersWallet(signerSigner);
|
||||
attackerWallet = new EthersWallet(attackerSigner);
|
||||
|
||||
auth = new ThirdwebAuth(signerWallet, "thirdweb.com", {
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
auth.updateWallet(signerWallet);
|
||||
});
|
||||
|
||||
it("Should verify logged in wallet", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload);
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should verify logged in wallet with chain ID and expiration", async () => {
|
||||
const payload = await auth.login({
|
||||
expirationTime: new Date(Date.now() + 1000 * 60 * 5),
|
||||
chainId: "137",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
chainId: "137",
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should verify payload with resources", async () => {
|
||||
const payload = await auth.login({
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should reject payload without necessary resources", async () => {
|
||||
const payload = await auth.login({
|
||||
resources: ["https://example.com"],
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
auth = new ThirdwebAuth(signerWallet, "thirdweb.com", {
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Login request is missing required resources: https://test.com",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should verify payload with customized statement", async () => {
|
||||
const payload = await auth.login({
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
statement: "Please sign!",
|
||||
beforeEach(async () => {
|
||||
auth.updateWallet(signerWallet);
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
it("Should verify logged in wallet", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
it("Should reject payload with incorrect statement", async () => {
|
||||
const payload = await auth.login({
|
||||
statement: "Please sign!",
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload);
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
statement: "Please sign again!",
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.include(
|
||||
"Expected statement 'Please sign again!' does not match statement on payload",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject invalid nonce", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
validateNonce: (nonce: string) => {
|
||||
if (nonce === payload.payload.nonce) {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal("Login request nonce is invalid");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should accept valid nonce", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
validateNonce: (nonce: string) => {
|
||||
if (nonce !== payload.payload.nonce) {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should reject payload with incorrect domain", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, { domain: "test.thirdweb.com" });
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Expected domain 'test.thirdweb.com' does not match domain on payload 'thirdweb.com'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject expired login payload", async () => {
|
||||
const payload = await auth.login({
|
||||
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal("Login request has expired");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject payload with incorrect chain ID", async () => {
|
||||
const payload = await auth.login({
|
||||
chainId: "1",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
it("Should verify logged in wallet with chain ID and expiration", async () => {
|
||||
const payload = await auth.login({
|
||||
expirationTime: new Date(Date.now() + 1000 * 60 * 5),
|
||||
chainId: "137",
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Expected chain ID '137' does not match chain ID on payload '1'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject payload with incorrect signer", async () => {
|
||||
const payload = await auth.login();
|
||||
payload.payload.address = await attackerWallet.getAddress();
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
chainId: "137",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("does not match payload address");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should generate valid authentication token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
const user = await auth.authenticate(token);
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should reject token with incorrect domain", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
try {
|
||||
await auth.authenticate(token, { domain: "test.thirdweb.com" });
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain(
|
||||
"Expected token to be for the domain 'test.thirdweb.com', but found token with domain 'thirdweb.com'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject token before invalid before", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
invalidBefore: new Date(Date.now() + 1000 * 60 * 5),
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("This token is invalid before");
|
||||
}
|
||||
});
|
||||
it("Should verify payload with resources", async () => {
|
||||
const payload = await auth.login({
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
});
|
||||
|
||||
it("Should reject expired authentication token", async () => {
|
||||
const payload = await auth.login();
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("This token expired");
|
||||
}
|
||||
});
|
||||
it("Should reject payload without necessary resources", async () => {
|
||||
const payload = await auth.login({
|
||||
resources: ["https://example.com"],
|
||||
});
|
||||
|
||||
it("Should reject if admin address is not connected wallet address", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
auth.updateWallet(signerWallet);
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain(
|
||||
`The expected issuer address '${await signerWallet.getAddress()}' did not match the token issuer address '${await adminWallet.getAddress()}'`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should accept token with valid token ID", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
tokenId: "test",
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Login request is missing required resources: https://test.com",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token, {
|
||||
validateTokenId: (tokenId: string) => {
|
||||
if (tokenId !== "test") {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
it("Should verify payload with customized statement", async () => {
|
||||
const payload = await auth.login({
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
it("Should reject payload with incorrect statement", async () => {
|
||||
const payload = await auth.login({
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
it("Should reject token with invalid token ID", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
tokenId: "test",
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
statement: "Please sign again!",
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.include(
|
||||
"Expected statement 'Please sign again!' does not match statement on payload",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token, {
|
||||
validateTokenId: (tokenId: string) => {
|
||||
if (tokenId !== "invalid") {
|
||||
it("Should reject invalid nonce", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
validateNonce: (nonce: string) => {
|
||||
if (nonce === payload.payload.nonce) {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal("Login request nonce is invalid");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should accept valid nonce", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
validateNonce: (nonce: string) => {
|
||||
if (nonce !== payload.payload.nonce) {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("Token ID is invalid");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should propagate session on token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
session: { role: "admin" },
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token);
|
||||
it("Should reject payload with incorrect domain", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
expect(user.session).to.deep.equal({ role: "admin" });
|
||||
});
|
||||
|
||||
it("Should call session callback function", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
session: (address: string) => {
|
||||
return { address, role: "admin" };
|
||||
},
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, { domain: "test.thirdweb.com" });
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Expected domain 'test.thirdweb.com' does not match domain on payload 'thirdweb.com'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token);
|
||||
it("Should reject expired login payload", async () => {
|
||||
const payload = await auth.login({
|
||||
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
|
||||
});
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
expect(user.session).to.deep.equal({
|
||||
address: await signerWallet.getAddress(),
|
||||
role: "admin",
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal("Login request has expired");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("Should authenticate with issuer address", async () => {
|
||||
const payload = await auth.login();
|
||||
it("Should reject payload with incorrect chain ID", async () => {
|
||||
const payload = await auth.login({
|
||||
chainId: "1",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
auth.updateWallet(attackerWallet);
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("The expected issuer address");
|
||||
}
|
||||
|
||||
const user = await auth.authenticate(token, {
|
||||
issuerAddress: await adminWallet.getAddress(),
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
chainId: "137",
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Expected chain ID '137' does not match chain ID on payload '1'",
|
||||
);
|
||||
}
|
||||
});
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
});
|
||||
|
||||
it("Should reject payload with incorrect signer", async () => {
|
||||
const payload = await auth.login();
|
||||
payload.payload.address = await attackerWallet.getAddress();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("does not match payload address");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should generate valid authentication token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
const user = await auth.authenticate(token);
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should reject token with incorrect domain", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
try {
|
||||
await auth.authenticate(token, { domain: "test.thirdweb.com" });
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain(
|
||||
"Expected token to be for the domain 'test.thirdweb.com', but found token with domain 'thirdweb.com'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject token before invalid before", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
invalidBefore: new Date(Date.now() + 1000 * 60 * 5),
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("This token is invalid before");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject expired authentication token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("This token expired");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject if admin address is not connected wallet address", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
auth.updateWallet(signerWallet);
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain(
|
||||
`The expected issuer address '${await signerWallet.getAddress()}' did not match the token issuer address '${await adminWallet.getAddress()}'`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should accept token with valid token ID", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
tokenId: "test",
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token, {
|
||||
validateTokenId: (tokenId: string) => {
|
||||
if (tokenId !== "test") {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should reject token with invalid token ID", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
tokenId: "test",
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token, {
|
||||
validateTokenId: (tokenId: string) => {
|
||||
if (tokenId !== "invalid") {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("Token ID is invalid");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should propagate session on token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
session: { role: "admin" },
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token);
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
expect(user.session).to.deep.equal({ role: "admin" });
|
||||
});
|
||||
|
||||
it("Should call session callback function", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
session: (address: string) => {
|
||||
return { address, role: "admin" };
|
||||
},
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token);
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
expect(user.session).to.deep.equal({
|
||||
address: await signerWallet.getAddress(),
|
||||
role: "admin",
|
||||
});
|
||||
});
|
||||
|
||||
it("Should authenticate with issuer address", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
auth.updateWallet(attackerWallet);
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("The expected issuer address");
|
||||
}
|
||||
|
||||
const user = await auth.authenticate(token, {
|
||||
issuerAddress: await adminWallet.getAddress(),
|
||||
});
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,384 +1,391 @@
|
||||
import { LocalWallet, SmartWallet } from "@thirdweb-dev/wallets";
|
||||
import { ThirdwebAuth } from "../src/core";
|
||||
import { EthersWallet } from "@thirdweb-dev/wallets/evm/wallets/ethers";
|
||||
import { expect } from "chai";
|
||||
import { Wallet } from "ethers";
|
||||
import { describe, it, beforeEach, beforeAll, expect } from "vitest";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("dotenv-mono").load();
|
||||
|
||||
describe("Wallet Authentication - EVM - Smart Wallet", async () => {
|
||||
let adminWallet: any, signerWallet: any, attackerWallet: any;
|
||||
let auth: ThirdwebAuth;
|
||||
describe(
|
||||
"Wallet Authentication - EVM - Smart Wallet",
|
||||
{
|
||||
timeout: 60000,
|
||||
},
|
||||
async () => {
|
||||
let adminWallet: any, signerWallet: any, attackerWallet: any;
|
||||
let auth: ThirdwebAuth;
|
||||
|
||||
before(async () => {
|
||||
const factoryAddress = "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052";
|
||||
const chain = 421614;
|
||||
const localWallet = new LocalWallet();
|
||||
await localWallet.generate();
|
||||
const smartWallet = new SmartWallet({
|
||||
chain: chain,
|
||||
factoryAddress: factoryAddress,
|
||||
gasless: true,
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
});
|
||||
await smartWallet.connect({ personalWallet: localWallet });
|
||||
beforeAll(async () => {
|
||||
const factoryAddress = "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052";
|
||||
const chain = 421614;
|
||||
const localWallet = new LocalWallet();
|
||||
await localWallet.generate();
|
||||
const smartWallet = new SmartWallet({
|
||||
chain: chain,
|
||||
factoryAddress: factoryAddress,
|
||||
gasless: true,
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
});
|
||||
await smartWallet.connect({ personalWallet: localWallet });
|
||||
|
||||
adminWallet = new EthersWallet(Wallet.createRandom());
|
||||
signerWallet = smartWallet;
|
||||
attackerWallet = new EthersWallet(Wallet.createRandom());
|
||||
adminWallet = new EthersWallet(Wallet.createRandom());
|
||||
signerWallet = smartWallet;
|
||||
attackerWallet = new EthersWallet(Wallet.createRandom());
|
||||
|
||||
auth = new ThirdwebAuth(signerWallet, "thirdweb.com", {
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
auth.updateWallet(signerWallet);
|
||||
});
|
||||
|
||||
it("Should verify logged in wallet", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload);
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should verify logged in wallet with chain ID and expiration", async () => {
|
||||
const payload = await auth.login({
|
||||
expirationTime: new Date(Date.now() + 1000 * 60 * 5),
|
||||
chainId: "421614",
|
||||
auth = new ThirdwebAuth(signerWallet, "thirdweb.com", {
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
});
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
chainId: "421614",
|
||||
beforeEach(async () => {
|
||||
auth.updateWallet(signerWallet);
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
it("Should verify logged in wallet", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
it("Should verify payload with resources", async () => {
|
||||
const payload = await auth.login({
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload);
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
it("Should verify logged in wallet with chain ID and expiration", async () => {
|
||||
const payload = await auth.login({
|
||||
expirationTime: new Date(Date.now() + 1000 * 60 * 5),
|
||||
chainId: "421614",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
chainId: "421614",
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should reject payload without necessary resources", async () => {
|
||||
const payload = await auth.login({
|
||||
resources: ["https://example.com"],
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
it("Should verify payload with resources", async () => {
|
||||
const payload = await auth.login({
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Login request is missing required resources: https://test.com",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should verify payload with customized statement", async () => {
|
||||
const payload = await auth.login({
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should reject payload with incorrect statement", async () => {
|
||||
const payload = await auth.login({
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
statement: "Please sign again!",
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.include(
|
||||
"Expected statement 'Please sign again!' does not match statement on payload",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject invalid nonce", async () => {
|
||||
const payload = await auth.login();
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
it("Should reject payload without necessary resources", async () => {
|
||||
const payload = await auth.login({
|
||||
resources: ["https://example.com"],
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
resources: ["https://example.com", "https://test.com"],
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Login request is missing required resources: https://test.com",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should verify payload with customized statement", async () => {
|
||||
const payload = await auth.login({
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should reject payload with incorrect statement", async () => {
|
||||
const payload = await auth.login({
|
||||
statement: "Please sign!",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
statement: "Please sign again!",
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.include(
|
||||
"Expected statement 'Please sign again!' does not match statement on payload",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject invalid nonce", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
validateNonce: (nonce: string) => {
|
||||
if (nonce === payload.payload.nonce) {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal("Login request nonce is invalid");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should accept valid nonce", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
validateNonce: (nonce: string) => {
|
||||
if (nonce === payload.payload.nonce) {
|
||||
if (nonce !== payload.payload.nonce) {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal("Login request nonce is invalid");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should accept valid nonce", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const address = await auth.verify(payload, {
|
||||
validateNonce: (nonce: string) => {
|
||||
if (nonce !== payload.payload.nonce) {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
expect(address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
it("Should reject payload with incorrect domain", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
it("Should reject payload with incorrect domain", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, { domain: "test.thirdweb.com" });
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Expected domain 'test.thirdweb.com' does not match domain on payload 'thirdweb.com'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject expired login payload", async () => {
|
||||
const payload = await auth.login({
|
||||
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, { domain: "test.thirdweb.com" });
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Expected domain 'test.thirdweb.com' does not match domain on payload 'thirdweb.com'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal("Login request has expired");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject payload with incorrect chain ID", async () => {
|
||||
const payload = await auth.login({
|
||||
chainId: "1",
|
||||
});
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
chainId: "137",
|
||||
it("Should reject expired login payload", async () => {
|
||||
const payload = await auth.login({
|
||||
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Expected chain ID '137' does not match chain ID on payload '1'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject payload with incorrect signer", async () => {
|
||||
const payload = await auth.login();
|
||||
payload.payload.address = await attackerWallet.getAddress();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("does not match payload address");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should generate valid authentication token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
const user = await auth.authenticate(token);
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
it("Should reject token with incorrect domain", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
try {
|
||||
await auth.authenticate(token, { domain: "test.thirdweb.com" });
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain(
|
||||
"Expected token to be for the domain 'test.thirdweb.com', but found token with domain 'thirdweb.com'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject token before invalid before", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
invalidBefore: new Date(Date.now() + 1000 * 60 * 5),
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal("Login request has expired");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("This token is invalid before");
|
||||
}
|
||||
});
|
||||
it("Should reject payload with incorrect chain ID", async () => {
|
||||
const payload = await auth.login({
|
||||
chainId: "1",
|
||||
});
|
||||
|
||||
it("Should reject expired authentication token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload, {
|
||||
chainId: "137",
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.equal(
|
||||
"Expected chain ID '137' does not match chain ID on payload '1'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("This token expired");
|
||||
}
|
||||
});
|
||||
it("Should reject payload with incorrect signer", async () => {
|
||||
const payload = await auth.login();
|
||||
payload.payload.address = await attackerWallet.getAddress();
|
||||
|
||||
it("Should reject if admin address is not connected wallet address", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
auth.updateWallet(signerWallet);
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain(
|
||||
`The expected issuer address '${await signerWallet.getAddress()}' did not match the token issuer address '${await adminWallet.getAddress()}'`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should accept token with valid token ID", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
tokenId: "test",
|
||||
auth.updateWallet(adminWallet);
|
||||
try {
|
||||
await auth.verify(payload);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("does not match payload address");
|
||||
}
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token, {
|
||||
validateTokenId: (tokenId: string) => {
|
||||
if (tokenId !== "test") {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
it("Should generate valid authentication token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
const user = await auth.authenticate(token);
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
it("Should reject token with incorrect domain", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
it("Should reject token with invalid token ID", async () => {
|
||||
const payload = await auth.login();
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
tokenId: "test",
|
||||
try {
|
||||
await auth.authenticate(token, { domain: "test.thirdweb.com" });
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain(
|
||||
"Expected token to be for the domain 'test.thirdweb.com', but found token with domain 'thirdweb.com'",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token, {
|
||||
it("Should reject token before invalid before", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
invalidBefore: new Date(Date.now() + 1000 * 60 * 5),
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("This token is invalid before");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject expired authentication token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
|
||||
});
|
||||
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("This token expired");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should reject if admin address is not connected wallet address", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
auth.updateWallet(signerWallet);
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain(
|
||||
`The expected issuer address '${await signerWallet.getAddress()}' did not match the token issuer address '${await adminWallet.getAddress()}'`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should accept token with valid token ID", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
tokenId: "test",
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token, {
|
||||
validateTokenId: (tokenId: string) => {
|
||||
if (tokenId !== "invalid") {
|
||||
if (tokenId !== "test") {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("Token ID is invalid");
|
||||
}
|
||||
});
|
||||
|
||||
it("Should propagate session on token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
session: { role: "admin" },
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token);
|
||||
it("Should reject token with invalid token ID", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
expect(user.session).to.deep.equal({ role: "admin" });
|
||||
});
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
tokenId: "test",
|
||||
});
|
||||
|
||||
it("Should call session callback function", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
session: (address: string) => {
|
||||
return { address, role: "admin" };
|
||||
},
|
||||
try {
|
||||
await auth.authenticate(token, {
|
||||
validateTokenId: (tokenId: string) => {
|
||||
if (tokenId !== "invalid") {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("Token ID is invalid");
|
||||
}
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token);
|
||||
it("Should propagate session on token", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
expect(user.session).to.deep.equal({
|
||||
address: await signerWallet.getAddress(),
|
||||
role: "admin",
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
session: { role: "admin" },
|
||||
});
|
||||
|
||||
const user = await auth.authenticate(token);
|
||||
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
expect(user.session).to.deep.equal({ role: "admin" });
|
||||
});
|
||||
});
|
||||
|
||||
it("Should authenticate with issuer address", async () => {
|
||||
const payload = await auth.login();
|
||||
it("Should call session callback function", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload, {
|
||||
session: (address: string) => {
|
||||
return { address, role: "admin" };
|
||||
},
|
||||
});
|
||||
|
||||
auth.updateWallet(attackerWallet);
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("The expected issuer address");
|
||||
}
|
||||
const user = await auth.authenticate(token);
|
||||
|
||||
const user = await auth.authenticate(token, {
|
||||
issuerAddress: await adminWallet.getAddress(),
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
expect(user.session).to.deep.equal({
|
||||
address: await signerWallet.getAddress(),
|
||||
role: "admin",
|
||||
});
|
||||
});
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
});
|
||||
|
||||
it("Should authenticate with issuer address", async () => {
|
||||
const payload = await auth.login();
|
||||
|
||||
auth.updateWallet(adminWallet);
|
||||
const token = await auth.generate(payload);
|
||||
|
||||
auth.updateWallet(attackerWallet);
|
||||
try {
|
||||
await auth.authenticate(token);
|
||||
expect.fail();
|
||||
} catch (err: any) {
|
||||
expect(err.message).to.contain("The expected issuer address");
|
||||
}
|
||||
|
||||
const user = await auth.authenticate(token, {
|
||||
issuerAddress: await adminWallet.getAddress(),
|
||||
});
|
||||
expect(user.address).to.equal(await signerWallet.getAddress());
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -12,15 +12,11 @@
|
||||
},
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@babel/runtime": "^7.23.8",
|
||||
"@babel/runtime": "^7.24.1",
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"plop": "^3.1.2",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"preconstruct": {
|
||||
"entrypoints": [
|
||||
@@ -34,9 +30,6 @@
|
||||
"db:sync": "bun scripts/sync-db.ts",
|
||||
"build": "tsc && preconstruct build",
|
||||
"push": "yalc push",
|
||||
"plop": "plop",
|
||||
"add-chain": "plop add-chain",
|
||||
"override-chain": "plop override-chain",
|
||||
"test": "bun test"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
const chainIdPrompt = {
|
||||
type: "number",
|
||||
name: "chainId",
|
||||
message: "Chain ID",
|
||||
validate: (value) => {
|
||||
if (value.length === 0) {
|
||||
return "ChainId is required";
|
||||
}
|
||||
if (!/^\d+$/.test(value)) {
|
||||
return "ChainId must be a number";
|
||||
}
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
const stringPrompt = (id, title, isRequired) => ({
|
||||
type: "input",
|
||||
name: id,
|
||||
message: title,
|
||||
validate: (value) => {
|
||||
if (isRequired && value.length === 0) {
|
||||
return `${title} is required`;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const testnetPrompt = {
|
||||
type: "confirm",
|
||||
name: "isTestnet",
|
||||
message: "Is this a testnet?",
|
||||
};
|
||||
|
||||
export default function (
|
||||
/** @type {import('plop').NodePlopAPI} */
|
||||
plop,
|
||||
) {
|
||||
// add partials
|
||||
plop.setPartial("chainIdPartial", "chainId: {{chainId}},\n");
|
||||
plop.setPartial("namePartial", `name: "{{name}}",\n`);
|
||||
plop.setPartial("chainPartial", `chain: "{{chain}}",\n`);
|
||||
plop.setPartial("shortNamePartial", `shortName: "{{shortName}}",\n`);
|
||||
plop.setPartial("isTestnetPartial", `testnet: {{isTestnet}},\n`);
|
||||
|
||||
plop.setGenerator("add-chain", {
|
||||
description: "add a new chain",
|
||||
prompts: [
|
||||
chainIdPrompt,
|
||||
stringPrompt("name", "Name", true),
|
||||
stringPrompt("chain", "Chain", true),
|
||||
stringPrompt("shortName", "Short Name", true),
|
||||
testnetPrompt,
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: "add",
|
||||
path: "data/additional/{{chainId}}.mjs",
|
||||
templateFile: "plop-templates/add-chain.hbs",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
plop.setGenerator("override-chain", {
|
||||
description: "add an override for a chain",
|
||||
prompts: [
|
||||
chainIdPrompt,
|
||||
stringPrompt("name", "Name"),
|
||||
stringPrompt("chain", "Chain"),
|
||||
stringPrompt("shortName", "Short Name"),
|
||||
testnetPrompt,
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: "add",
|
||||
path: "data/overrides/{{chainId}}.mjs",
|
||||
templateFile: "plop-templates/chain-override.hbs",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { prepareEnvironment } from "@gmrchk/cli-testing-library";
|
||||
import { copyFile } from "fs/promises";
|
||||
import { resolve } from "path";
|
||||
import { ERROR_MESSAGES } from "../constants/constants";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
// load env variables
|
||||
require("dotenv-mono").load();
|
||||
@@ -12,38 +13,43 @@ const apiSecretKey = process.env.TW_SECRET_KEY as string;
|
||||
const itif = (condition: boolean) => (condition ? it : it.skip);
|
||||
|
||||
// this creates an app, can take some time that's fine
|
||||
jest.setTimeout(120_000);
|
||||
|
||||
describe("npx thirdweb deploy", () => {
|
||||
itif(!!apiSecretKey)("should return deploy page url", async () => {
|
||||
const { spawn, cleanup, exists, path } = await prepareEnvironment();
|
||||
describe(
|
||||
"npx thirdweb deploy",
|
||||
{
|
||||
timeout: 120_000,
|
||||
},
|
||||
() => {
|
||||
itif(!!apiSecretKey)("should return deploy page url", async () => {
|
||||
const { spawn, cleanup, exists, path } = await prepareEnvironment();
|
||||
|
||||
await copyFile(
|
||||
resolve("./e2e/files/BasicContract.sol"),
|
||||
`${path}/BasicContract.sol`,
|
||||
);
|
||||
|
||||
const { waitForText, waitForFinish, getExitCode, writeText, getStderr } =
|
||||
await spawn(
|
||||
"node",
|
||||
`./dist/cli/index.js deploy --contract -k ${apiSecretKey}`,
|
||||
await copyFile(
|
||||
resolve("./e2e/files/BasicContract.sol"),
|
||||
`${path}/BasicContract.sol`,
|
||||
);
|
||||
|
||||
expect(await exists("BasicContract.sol")).toEqual(true);
|
||||
const { waitForText, waitForFinish, getExitCode, writeText, getStderr } =
|
||||
await spawn(
|
||||
"node",
|
||||
`./dist/cli/index.js deploy --contract -k ${apiSecretKey}`,
|
||||
);
|
||||
|
||||
await waitForText(ERROR_MESSAGES.noConfiguration);
|
||||
await writeText("y");
|
||||
expect(await exists("BasicContract.sol")).toEqual(true);
|
||||
|
||||
// wait for program to finish
|
||||
await waitForFinish();
|
||||
await waitForText(ERROR_MESSAGES.noConfiguration);
|
||||
await writeText("y");
|
||||
|
||||
expect(getStderr().at(-1)).toContain(
|
||||
"https://thirdweb.com/contracts/deploy/",
|
||||
);
|
||||
// wait for program to finish
|
||||
await waitForFinish();
|
||||
|
||||
// the process should exit with code 0
|
||||
expect(getExitCode()).toEqual(0);
|
||||
expect(getStderr().at(-1)).toContain(
|
||||
"https://thirdweb.com/contracts/deploy/",
|
||||
);
|
||||
|
||||
await cleanup(); // cleanup after test
|
||||
});
|
||||
});
|
||||
// the process should exit with code 0
|
||||
expect(getExitCode()).toEqual(0);
|
||||
|
||||
await cleanup(); // cleanup after test
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,92 +1,98 @@
|
||||
import { CREATE_MESSAGES } from "../constants/constants";
|
||||
import { prepareEnvironment } from "@gmrchk/cli-testing-library";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
// load env variables
|
||||
require("dotenv-mono").load();
|
||||
|
||||
// this creates an app, can take some time that's fine
|
||||
jest.setTimeout(240_000);
|
||||
describe(
|
||||
"npx thirdweb detect",
|
||||
{
|
||||
timeout: 240_000,
|
||||
},
|
||||
() => {
|
||||
it("should detect ERC721Base extensions", async () => {
|
||||
const { spawn, cleanup, exists } = await prepareEnvironment();
|
||||
const create = await spawn("node", "./dist/cli/index.js create");
|
||||
|
||||
describe("npx thirdweb detect", () => {
|
||||
it("should detect ERC721Base extensions", async () => {
|
||||
const { spawn, cleanup, exists } = await prepareEnvironment();
|
||||
const create = await spawn("node", "./dist/cli/index.js create");
|
||||
await create.waitForText(CREATE_MESSAGES.typeOfProject);
|
||||
// select contract
|
||||
await create.pressKey("arrowDown");
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.projectName);
|
||||
// use default name
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.framework);
|
||||
// select hardhat
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.contractName);
|
||||
// set contract name
|
||||
await create.writeText("Contract");
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.contract);
|
||||
// select ERC721
|
||||
await create.pressKey("arrowDown");
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.extensions);
|
||||
// select no extra extensions
|
||||
await create.pressKey("enter");
|
||||
|
||||
await create.waitForText(CREATE_MESSAGES.typeOfProject);
|
||||
// select contract
|
||||
await create.pressKey("arrowDown");
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.projectName);
|
||||
// use default name
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.framework);
|
||||
// select hardhat
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.contractName);
|
||||
// set contract name
|
||||
await create.writeText("Contract");
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.contract);
|
||||
// select ERC721
|
||||
await create.pressKey("arrowDown");
|
||||
await create.pressKey("enter");
|
||||
await create.waitForText(CREATE_MESSAGES.extensions);
|
||||
// select no extra extensions
|
||||
await create.pressKey("enter");
|
||||
// wait for program to finish
|
||||
await create.waitForFinish();
|
||||
|
||||
// wait for program to finish
|
||||
await create.waitForFinish();
|
||||
// check if the contract was created
|
||||
expect(await exists("thirdweb-contracts/contracts/Contract.sol")).toEqual(
|
||||
true,
|
||||
);
|
||||
|
||||
// check if the contract was created
|
||||
expect(await exists("thirdweb-contracts/contracts/Contract.sol")).toEqual(
|
||||
true,
|
||||
);
|
||||
// check that hardhat config exists
|
||||
expect(await exists("thirdweb-contracts/hardhat.config.js")).toEqual(
|
||||
true,
|
||||
);
|
||||
|
||||
// check that hardhat config exists
|
||||
expect(await exists("thirdweb-contracts/hardhat.config.js")).toEqual(true);
|
||||
await create.writeText("cd thirdweb-contracts");
|
||||
await create.pressKey("enter");
|
||||
await create.writeText("npm install");
|
||||
await create.pressKey("enter");
|
||||
|
||||
await create.writeText("cd thirdweb-contracts");
|
||||
await create.pressKey("enter");
|
||||
await create.writeText("npm install");
|
||||
await create.pressKey("enter");
|
||||
const detect = await spawn(
|
||||
"node",
|
||||
"./dist/cli/index.js detect",
|
||||
"./thirdweb-contracts",
|
||||
);
|
||||
|
||||
const detect = await spawn(
|
||||
"node",
|
||||
"./dist/cli/index.js detect",
|
||||
"./thirdweb-contracts",
|
||||
);
|
||||
// Select pnpm as package manager.
|
||||
await detect.pressKey("arrowDown");
|
||||
await detect.pressKey("enter");
|
||||
|
||||
// Select pnpm as package manager.
|
||||
await detect.pressKey("arrowDown");
|
||||
await detect.pressKey("enter");
|
||||
// wait for program to finish
|
||||
await detect.waitForFinish();
|
||||
|
||||
// wait for program to finish
|
||||
await detect.waitForFinish();
|
||||
const lines = detect.getStdout();
|
||||
|
||||
const lines = detect.getStdout();
|
||||
// Detected extensions
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("ERC721")),
|
||||
).toBeGreaterThan(-1);
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("ERC721Burnable")),
|
||||
).toBeGreaterThan(-1);
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("ERC721Mintable")),
|
||||
).toBeGreaterThan(-1);
|
||||
|
||||
// Detected extensions
|
||||
expect(lines.findIndex((line) => line.includes("ERC721"))).toBeGreaterThan(
|
||||
-1,
|
||||
);
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("ERC721Burnable")),
|
||||
).toBeGreaterThan(-1);
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("ERC721Mintable")),
|
||||
).toBeGreaterThan(-1);
|
||||
// Suggested extensions
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("- ERC721Enumerable")),
|
||||
).toBeGreaterThan(-1);
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("- ERC721LazyMintable")),
|
||||
).toBeGreaterThan(-1);
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("- ERC721SignatureMint")),
|
||||
).toBeGreaterThan(-1);
|
||||
|
||||
// Suggested extensions
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("- ERC721Enumerable")),
|
||||
).toBeGreaterThan(-1);
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("- ERC721LazyMintable")),
|
||||
).toBeGreaterThan(-1);
|
||||
expect(
|
||||
lines.findIndex((line) => line.includes("- ERC721SignatureMint")),
|
||||
).toBeGreaterThan(-1);
|
||||
|
||||
await cleanup(); // cleanup after test
|
||||
});
|
||||
});
|
||||
await cleanup(); // cleanup after test
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { prepareEnvironment } from "@gmrchk/cli-testing-library";
|
||||
import { copyFile } from "fs/promises";
|
||||
import { resolve } from "path";
|
||||
import { ERROR_MESSAGES } from "../constants/constants";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
// load env variables
|
||||
require("dotenv-mono").load();
|
||||
@@ -12,37 +13,40 @@ const apiSecretKey = process.env.TW_SECRET_KEY as string;
|
||||
// function to skip tests based on condition
|
||||
const itif = (condition: boolean) => (condition ? it : it.skip);
|
||||
|
||||
// this creates an app, can take some time that's fine
|
||||
jest.setTimeout(120_000);
|
||||
describe(
|
||||
"npx thirdweb publish",
|
||||
{
|
||||
timeout: 120_000,
|
||||
},
|
||||
() => {
|
||||
// conditionally skip test if there's no api key
|
||||
itif(!!apiSecretKey)("should return publish page url", async () => {
|
||||
const { spawn, cleanup, exists, path } = await prepareEnvironment();
|
||||
|
||||
describe("npx thirdweb publish", () => {
|
||||
// conditionally skip test if there's no api key
|
||||
itif(!!apiSecretKey)("should return publish page url", async () => {
|
||||
const { spawn, cleanup, exists, path } = await prepareEnvironment();
|
||||
await copyFile(
|
||||
resolve("./e2e/files/BasicContract.sol"),
|
||||
`${path}/BasicContract.sol`,
|
||||
);
|
||||
|
||||
await copyFile(
|
||||
resolve("./e2e/files/BasicContract.sol"),
|
||||
`${path}/BasicContract.sol`,
|
||||
);
|
||||
const { waitForText, waitForFinish, getExitCode, writeText, getStderr } =
|
||||
await spawn("node", `./dist/cli/index.js publish -k ${apiSecretKey}`);
|
||||
|
||||
const { waitForText, waitForFinish, getExitCode, writeText, getStderr } =
|
||||
await spawn("node", `./dist/cli/index.js publish -k ${apiSecretKey}`);
|
||||
expect(await exists("BasicContract.sol")).toEqual(true);
|
||||
|
||||
expect(await exists("BasicContract.sol")).toEqual(true);
|
||||
await waitForText(ERROR_MESSAGES.noConfiguration);
|
||||
await writeText("y");
|
||||
|
||||
await waitForText(ERROR_MESSAGES.noConfiguration);
|
||||
await writeText("y");
|
||||
// wait for program to finish
|
||||
await waitForFinish();
|
||||
|
||||
// wait for program to finish
|
||||
await waitForFinish();
|
||||
expect(getStderr().at(-1)).toContain(
|
||||
"https://thirdweb.com/contracts/publish/",
|
||||
);
|
||||
|
||||
expect(getStderr().at(-1)).toContain(
|
||||
"https://thirdweb.com/contracts/publish/",
|
||||
);
|
||||
// the process should exit with code 0
|
||||
expect(getExitCode()).toEqual(0);
|
||||
|
||||
// the process should exit with code 0
|
||||
expect(getExitCode()).toEqual(0);
|
||||
|
||||
await cleanup(); // cleanup after test
|
||||
});
|
||||
});
|
||||
await cleanup(); // cleanup after test
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||
module.exports = {
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
};
|
||||
@@ -11,28 +11,23 @@
|
||||
"devDependencies": {
|
||||
"@gmrchk/cli-testing-library": "^0.1.2",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/async-retry": "^1.3.0",
|
||||
"@types/cross-spawn": "^6.0.2",
|
||||
"@types/inquirer": "^8.2.1",
|
||||
"@types/jest": "^29.5.3",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/node": "^18.17.1",
|
||||
"@types/prompts": "^2.4.4",
|
||||
"@types/rimraf": "^3.0.0",
|
||||
"@types/tar": "^6.1.5",
|
||||
"@types/update-notifier": "^6.0.4",
|
||||
"@types/validate-npm-package-name": "^4.0.0",
|
||||
"@types/async-retry": "^1.4.8",
|
||||
"@types/cross-spawn": "^6.0.6",
|
||||
"@types/inquirer": "^8.2.10",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "20.11.30",
|
||||
"@types/prompts": "^2.4.9",
|
||||
"@types/tar": "^6.1.11",
|
||||
"@types/update-notifier": "^6.0.8",
|
||||
"@types/validate-npm-package-name": "^4.0.2",
|
||||
"chalk": "^5.3.0",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"dotenv-mono": "^1.3.10",
|
||||
"dotenv-mono": "^1.3.14",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"hardhat": "^2.17.0",
|
||||
"jest": "^29.6.2",
|
||||
"hardhat": "^2.22.2",
|
||||
"ora": "^6.3.1",
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-node": "^10.7.0",
|
||||
"tsup": "^6.7.0",
|
||||
"typescript": "^5.3.3"
|
||||
"tsup": "8.0.2",
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^2.2.5",
|
||||
@@ -42,22 +37,22 @@
|
||||
"@thirdweb-dev/storage": "workspace:*",
|
||||
"@thirdweb-dev/wallets": "workspace:*",
|
||||
"async-retry": "^1.3.3",
|
||||
"commander": "^9.1.0",
|
||||
"commander": "^9.5.0",
|
||||
"detect-package-manager": "^2.0.1",
|
||||
"enquirer": "^2.3.6",
|
||||
"enquirer": "^2.4.1",
|
||||
"ethers": "^5.7.2",
|
||||
"got": "11.8.5",
|
||||
"inquirer": "^8.2.3",
|
||||
"inquirer": "^8.2.6",
|
||||
"js-yaml": "^4.1.0",
|
||||
"open": "^8.4.0",
|
||||
"open": "^8.4.2",
|
||||
"prompts": "^2.4.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"solc": "0.8.17",
|
||||
"tar": "^6.1.15",
|
||||
"rimraf": "5.0.5",
|
||||
"solc": "0.8.25",
|
||||
"tar": "^6.2.1",
|
||||
"update-notifier": "^6.0.2",
|
||||
"validate-npm-package-name": "^4.0.0",
|
||||
"xdg-app-paths": "^8.3.0",
|
||||
"yaml": "^2.3.4"
|
||||
"yaml": "^2.4.1"
|
||||
},
|
||||
"bin": {
|
||||
"thirdweb": "./dist/cli/index.js"
|
||||
@@ -67,11 +62,10 @@
|
||||
"lint": "eslint src/",
|
||||
"fix": "eslint src/ --fix",
|
||||
"clean": "rm -rf dist/",
|
||||
"cli": "node -r ts-node/register src/cli/index.ts",
|
||||
"build": "pnpm clean && tsup --env.NODE_ENV production --minify --format cjs --external hardhat --dts-resolve",
|
||||
"build:watch": "pnpm run build --watch",
|
||||
"generate-thirdweb-package": "node scripts/generate-thirdweb-package.js",
|
||||
"e2e": "jest",
|
||||
"e2e": "vitest",
|
||||
"push": "yalc push"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -428,17 +428,15 @@
|
||||
"@thirdweb-dev/contracts": "3.12.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@ethersproject/abi": "^5.7.0",
|
||||
"@ethersproject/providers": "^5.7.2",
|
||||
"@preconstruct/cli": "^2.8.1",
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@typechain/ethers-v5": "10.0.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"ethers": "^5.7.2",
|
||||
"typechain": "^8.3.1",
|
||||
"typescript": "^5.3.3"
|
||||
"typechain": "^8.3.2",
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ethers": "^5"
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^1.3.2",
|
||||
"js-sha3": "^0.9.2"
|
||||
"@noble/hashes": "^1.4.0",
|
||||
"js-sha3": "^0.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
@@ -37,10 +37,10 @@
|
||||
"crypto-js": "^4.2.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"ethers": "^5.7.2",
|
||||
"js-awe": "^1.0.62",
|
||||
"tinybench": "^2.5.1",
|
||||
"typescript": "^5.3.3",
|
||||
"viem": "^1.19.1"
|
||||
"js-awe": "^1.0.64",
|
||||
"tinybench": "^2.6.0",
|
||||
"typescript": "5.4.3",
|
||||
"viem": "2.9.5"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write 'src/**/*'",
|
||||
|
||||
@@ -35,17 +35,17 @@
|
||||
"devDependencies": {
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/benchmark": "^2.1.4",
|
||||
"@types/treeify": "^1.0.2",
|
||||
"@types/benchmark": "^2.1.5",
|
||||
"@types/treeify": "^1.0.3",
|
||||
"benchmark": "^2.1.4",
|
||||
"circomlibjs": "^0.1.7",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"ethereum-cryptography": "^1.0.3",
|
||||
"ethereumjs-util": "^7.0.9",
|
||||
"ethereum-cryptography": "^1.2.0",
|
||||
"ethereumjs-util": "^7.1.5",
|
||||
"keccak256": "^1.0.6",
|
||||
"sha1": "^1.1.1",
|
||||
"tape": "^4.9.2",
|
||||
"typescript": "^5.3.3"
|
||||
"tape": "^4.17.0",
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write 'src/**/*'",
|
||||
|
||||
@@ -33,13 +33,13 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@coinbase/cbpay-js": "^1.7.0"
|
||||
"@coinbase/cbpay-js": "^1.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write 'src/**/*'",
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write 'src/**/*'",
|
||||
|
||||
@@ -57,34 +57,31 @@
|
||||
},
|
||||
"sideEffects:": false,
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@babel/runtime": "^7.23.8",
|
||||
"@microsoft/api-documenter": "^7.22.30",
|
||||
"@microsoft/api-extractor": "^7.36.3",
|
||||
"@microsoft/tsdoc": "^0.14.1",
|
||||
"@babel/preset-react": "^7.24.1",
|
||||
"@babel/runtime": "^7.24.1",
|
||||
"@microsoft/api-documenter": "^7.24.1",
|
||||
"@microsoft/api-extractor": "^7.43.0",
|
||||
"@microsoft/tsdoc": "^0.14.2",
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@thirdweb-dev/auth": "workspace:*",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/mime": "3.0.0",
|
||||
"@types/react": "^18.2.17",
|
||||
"eslint": "^8.56.0",
|
||||
"@types/react": "18.2.17",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"eslint-plugin-better-tree-shaking": "0.0.4",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-inclusive-language": "^2.2.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"ethers": "^5.7.2",
|
||||
"react": "^18.2.0",
|
||||
"react": "18.2.0",
|
||||
"typedoc-gen": "workspace:*",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^4.33.0",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@thirdweb-dev/auth": "workspace:*",
|
||||
"@thirdweb-dev/chains": "workspace:*",
|
||||
"@thirdweb-dev/generated-abis": "workspace:^",
|
||||
@@ -92,7 +89,7 @@
|
||||
"@thirdweb-dev/storage": "workspace:*",
|
||||
"@thirdweb-dev/wallets": "workspace:*",
|
||||
"mime": "3.0.0",
|
||||
"tiny-invariant": "^1.2.0"
|
||||
"tiny-invariant": "^1.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ethers": ">=5.5.1",
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
DAppMetaData,
|
||||
SignerWallet,
|
||||
} from "@thirdweb-dev/wallets";
|
||||
import React, { useMemo } from "react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
/**
|
||||
* The possible props for the ThirdwebProvider.
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
"@ethersproject/shims": "^5.7.0",
|
||||
"@walletconnect/react-native-compat": "2.9.2",
|
||||
"base-64": "^1.0.0",
|
||||
"big-integer": "^1.6.51",
|
||||
"react-native-get-random-values": "^1.10.0",
|
||||
"big-integer": "^1.6.52",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"react-native-url-polyfill": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -34,14 +34,13 @@
|
||||
"react-native": ">=0.70.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/node": "^18.17.1",
|
||||
"@types/node": "20.11.30",
|
||||
"babel-plugin-module-resolver": "^5.0.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"ethers": "^5.7.2",
|
||||
"react": "^18.2.0",
|
||||
"react-native": "^0.71.11",
|
||||
"typescript": "^5.3.3"
|
||||
"react": "18.2.0",
|
||||
"react-native": "^0.71.17",
|
||||
"typescript": "5.4.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,59 +28,54 @@
|
||||
"@aws-sdk/client-lambda": "3.350.0",
|
||||
"@aws-sdk/credential-providers": "3.350.0",
|
||||
"@coinbase/wallet-mobile-sdk": "1.0.13",
|
||||
"@paperxyz/embedded-wallet-service-sdk": "^1.2.4",
|
||||
"@paperxyz/embedded-wallet-service-sdk": "^1.2.5",
|
||||
"@shopify/restyle": "^2.4.2",
|
||||
"@tanstack/react-query": "^4.33.0",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@thirdweb-dev/chains": "workspace:*",
|
||||
"@thirdweb-dev/react-core": "workspace:*",
|
||||
"@thirdweb-dev/sdk": "workspace:*",
|
||||
"@thirdweb-dev/storage": "workspace:*",
|
||||
"@thirdweb-dev/wallets": "workspace:*",
|
||||
"abitype": "^0.2.5",
|
||||
"aws-amplify": "^5.3.8",
|
||||
"abitype": "1.0.0",
|
||||
"aws-amplify": "^5.3.18",
|
||||
"expo-application": "5.7.0",
|
||||
"expo-clipboard": "^4.5.0",
|
||||
"expo-document-picker": "^11.2.2",
|
||||
"expo-file-system": "^15.2.2",
|
||||
"expo-secure-store": "^12.1.1",
|
||||
"expo-clipboard": "^4.8.0",
|
||||
"expo-document-picker": "^11.10.1",
|
||||
"expo-file-system": "^15.9.0",
|
||||
"expo-secure-store": "^12.8.1",
|
||||
"react-native-modal": "^13.0.1",
|
||||
"react-native-qrcode-svg": "6.2.0",
|
||||
"react-native-svg": "^13.9.0",
|
||||
"react-native-svg": "^13.14.0",
|
||||
"secrets.js-34r7h": "^2.0.2",
|
||||
"tiny-invariant": "^1.2.0"
|
||||
"tiny-invariant": "^1.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@react-native-async-storage/async-storage": "^1.18.2",
|
||||
"@react-native-community/eslint-config": "^3.0.2",
|
||||
"@react-native-async-storage/async-storage": "^1.23.1",
|
||||
"@react-native-community/eslint-config": "^3.2.0",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/jest": "^29.5.3",
|
||||
"@types/react": "^18.2.17",
|
||||
"@types/react-native": "^0.71.8",
|
||||
"babel-jest": "^29.6.2",
|
||||
"eslint": "^8.56.0",
|
||||
"@types/react": "18.2.17",
|
||||
"@types/react-native": "^0.71.13",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"eslint-plugin-i18next": "^6.0.3",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-inclusive-language": "^2.2.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"ethers": "^5.7.2",
|
||||
"jest": "^29.6.2",
|
||||
"react": "^18.2.0",
|
||||
"react-native": "^0.71.11",
|
||||
"react": "18.2.0",
|
||||
"react-native": "^0.71.17",
|
||||
"react-native-camera": "^4.2.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rimraf": "5.0.5",
|
||||
"typedoc-gen": "workspace:*",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"amazon-cognito-identity-js": "^6.3.3",
|
||||
"amazon-cognito-identity-js": "^6.3.12",
|
||||
"react-native-aes-gcm-crypto": "^0.2.2",
|
||||
"react-native-camera": "^4.2.1",
|
||||
"react-native-inappbrowser-reborn": "^3.7.0",
|
||||
"react-native-quick-base64": "^2.0.7",
|
||||
"react-native-quick-base64": "^2.0.8",
|
||||
"react-native-quick-crypto": "^0.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
+5
-5
@@ -24,11 +24,11 @@ export type LocalWalletImportModalProps = {
|
||||
onWalletImported: (wallet: LocalWalletInstance) => void;
|
||||
};
|
||||
|
||||
export const LocalWalletImportModal = ({
|
||||
export const LocalWalletImportModal: React.FC<LocalWalletImportModalProps> = ({
|
||||
isVisible,
|
||||
onClose,
|
||||
onWalletImported,
|
||||
}: LocalWalletImportModalProps) => {
|
||||
}) => {
|
||||
const l = useLocale();
|
||||
const [password, setPassword] = useState<string | undefined>();
|
||||
const [privateKeyOrMnemonic, setPrivateKeyOrMnemonic] = useState<
|
||||
@@ -140,12 +140,12 @@ export const LocalWalletImportModal = ({
|
||||
|
||||
const onPickFilePress = async () => {
|
||||
const result = await DocumentPicker.getDocumentAsync({
|
||||
copyToCacheDirectory: true,
|
||||
type: "application/json",
|
||||
multiple: false,
|
||||
});
|
||||
|
||||
if (result.type === "success") {
|
||||
setJsonFile(result.uri);
|
||||
if (!result.canceled && result.assets.length > 0) {
|
||||
setJsonFile(result.assets[0].uri);
|
||||
setError(undefined);
|
||||
} else {
|
||||
setError(l.local_wallet.error_accessing_file);
|
||||
|
||||
@@ -72,40 +72,36 @@
|
||||
},
|
||||
"sideEffects:": false,
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@babel/runtime": "^7.23.8",
|
||||
"@microsoft/api-documenter": "^7.22.30",
|
||||
"@microsoft/api-extractor": "^7.36.3",
|
||||
"@microsoft/tsdoc": "^0.14.1",
|
||||
"@babel/runtime": "^7.24.1",
|
||||
"@microsoft/api-documenter": "^7.24.1",
|
||||
"@microsoft/api-extractor": "^7.43.0",
|
||||
"@microsoft/tsdoc": "^0.14.2",
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@thirdweb-dev/auth": "workspace:*",
|
||||
"@thirdweb-dev/sdk": "workspace:*",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/qrcode": "^1.5.2",
|
||||
"@types/react": "^18.2.17",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/uuid": "^9.0.5",
|
||||
"eslint": "^8.56.0",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react": "18.2.17",
|
||||
"@types/react-dom": "^18.2.23",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"eslint-plugin-better-tree-shaking": "0.0.4",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-svg-jsx": "^1.2.2",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-inclusive-language": "^2.2.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-svg-jsx": "^1.2.4",
|
||||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"ethers": "^5.7.2",
|
||||
"react": "^18.2.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"stream": "npm:stream-browserify@^3.0.0",
|
||||
"typedoc-gen": "workspace:*",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.10.5",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@google/model-viewer": "^2.1.1",
|
||||
"@headlessui/react": "1.7.6",
|
||||
@@ -113,21 +109,21 @@
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-focus-scope": "^1.0.4",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-popover": "^1.0.6",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@tanstack/react-query": "^4.33.0",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@thirdweb-dev/chains": "workspace:*",
|
||||
"@thirdweb-dev/payments": "workspace:*",
|
||||
"@thirdweb-dev/react-core": "workspace:*",
|
||||
"@thirdweb-dev/sdk": "workspace:*",
|
||||
"@thirdweb-dev/wallets": "workspace:*",
|
||||
"buffer": "^6.0.3",
|
||||
"copy-to-clipboard": "^3.3.2",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"detect-browser": "^5.3.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"tiny-invariant": "^1.2.0"
|
||||
"tiny-invariant": "^1.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@thirdweb-dev/sdk": "workspace:*",
|
||||
|
||||
@@ -8,7 +8,4 @@ test-evm: FORCE
|
||||
./scripts/waitForHardhatNode.sh
|
||||
pnpm run test:all
|
||||
|
||||
test-sol: FORCE
|
||||
./scripts/run_tests.sh
|
||||
|
||||
FORCE: ;
|
||||
|
||||
@@ -75,42 +75,41 @@
|
||||
"typedoc": "node scripts/typedoc.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.378.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.543.0",
|
||||
"@ethersproject/abstract-provider": "^5.7.0",
|
||||
"@microsoft/api-documenter": "^7.22.30",
|
||||
"@microsoft/api-extractor": "^7.36.3",
|
||||
"@microsoft/tsdoc": "^0.14.1",
|
||||
"@microsoft/api-documenter": "^7.24.1",
|
||||
"@microsoft/api-extractor": "^7.43.0",
|
||||
"@microsoft/tsdoc": "^0.14.2",
|
||||
"@nomiclabs/hardhat-ethers": "^2.2.3",
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@project-serum/anchor": "^0.25.0",
|
||||
"@swc-node/register": "^1.6.8",
|
||||
"@swc/core": "^1.3.71",
|
||||
"@swc-node/register": "^1.9.0",
|
||||
"@swc/core": "^1.4.11",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/bn.js": "^5.1.1",
|
||||
"@types/chai": "^4.3.5",
|
||||
"@types/deep-equal-in-any-order": "^1.0.1",
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/node": "^18.17.1",
|
||||
"@types/uuid": "^9.0.5",
|
||||
"chai": "^4.3.6",
|
||||
"deep-equal-in-any-order": "^1.1.18",
|
||||
"dotenv-mono": "^1.3.10",
|
||||
"eslint": "^8.56.0",
|
||||
"@types/bn.js": "^5.1.5",
|
||||
"@types/chai": "^4.3.14",
|
||||
"@types/deep-equal-in-any-order": "^1.0.3",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/node": "20.11.30",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"chai": "^4.4.1",
|
||||
"deep-equal-in-any-order": "^1.1.20",
|
||||
"dotenv-mono": "^1.3.14",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"eslint-plugin-better-tree-shaking": "0.0.4",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-inclusive-language": "^2.2.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"ethers": "^5.7.2",
|
||||
"ethers-aws-kms-signer": "^1.3.2",
|
||||
"hardhat": "^2.17.0",
|
||||
"hardhat": "^2.22.2",
|
||||
"mocha": "10.4.0",
|
||||
"nyc": "^15.1.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rimraf": "5.0.5",
|
||||
"typedoc-gen": "workspace:*",
|
||||
"typescript": "^5.3.3",
|
||||
"zksync-web3": "^0.14.3"
|
||||
"typescript": "5.4.3",
|
||||
"zksync-web3": "^0.14.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.215.0",
|
||||
@@ -137,17 +136,17 @@
|
||||
"@thirdweb-dev/generated-abis": "workspace:*",
|
||||
"@thirdweb-dev/merkletree": "workspace:*",
|
||||
"@thirdweb-dev/storage": "workspace:*",
|
||||
"abitype": "^0.2.5",
|
||||
"bn.js": "^5.2.1",
|
||||
"abitype": "1.0.0",
|
||||
"bn.js": "5.2.1",
|
||||
"bs58": "^5.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"uuid": "^9.0.1",
|
||||
"yaml": "^2.3.4",
|
||||
"zod": "^3.22.3"
|
||||
"yaml": "^2.4.1",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/thirdweb-dev/js/issues"
|
||||
|
||||
@@ -65,7 +65,6 @@ export type ExtractFunctionType<
|
||||
TAbi extends Abi,
|
||||
TFunctionName extends TAbiFunctionNames<TAbi>,
|
||||
> = (
|
||||
// @ts-expect-error - TODO: fix this
|
||||
...args: ExtractFunctionInputsType<TAbi, TFunctionName>
|
||||
) => ExtractFunctionOutputsType<TAbi, TFunctionName>;
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB |
@@ -20,7 +20,7 @@ describe("Error Handling", async () => {
|
||||
});
|
||||
expect.fail();
|
||||
} catch (err) {
|
||||
expectError(err, "sender doesn't have enough funds to send tx");
|
||||
expectError(err, "Sender doesn't have enough funds to send tx");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Binary file not shown.
@@ -20,9 +20,7 @@
|
||||
"fix": "eslint src/ --fix",
|
||||
"clean": "rm -rf dist/",
|
||||
"build": "tsc && preconstruct build",
|
||||
"test:all": "NODE_ENV=test SWC_NODE_PROJECT=./tsconfig.test.json mocha --timeout 30000 --parallel './test/**/*.test.ts'",
|
||||
"test": "pnpm test:all",
|
||||
"test:single": "NODE_ENV=test SWC_NODE_PROJECT=./tsconfig.test.json mocha --timeout 30000",
|
||||
"test": "vitest",
|
||||
"push": "yalc push",
|
||||
"typedoc": "node scripts/typedoc.mjs"
|
||||
},
|
||||
@@ -33,26 +31,20 @@
|
||||
"exports": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.23.8",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@microsoft/api-documenter": "^7.22.30",
|
||||
"@microsoft/api-extractor": "^7.36.3",
|
||||
"@microsoft/tsdoc": "^0.14.1",
|
||||
"@microsoft/api-documenter": "^7.24.1",
|
||||
"@microsoft/api-extractor": "^7.43.0",
|
||||
"@microsoft/tsdoc": "^0.14.2",
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@swc-node/register": "^1.6.8",
|
||||
"@swc-node/register": "^1.9.0",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/chai": "^4.3.5",
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/uuid": "^9.0.5",
|
||||
"chai": "^4.3.6",
|
||||
"eslint": "^8.56.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"esm": "^3.2.25",
|
||||
"mocha": "10.4.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rimraf": "5.0.5",
|
||||
"typedoc-gen": "workspace:*",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@thirdweb-dev/crypto": "workspace:*",
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
import { expect } from "chai";
|
||||
import { readFileSync } from "fs";
|
||||
import { getGatewayUrlForCid, IpfsUploader, ThirdwebStorage } from "../src";
|
||||
import { DEFAULT_GATEWAY_URLS, prepareGatewayUrls } from "../src/common/urls";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
// load env variables
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("dotenv-mono").load();
|
||||
|
||||
const secretKey = process.env.TW_SECRET_KEY as string;
|
||||
|
||||
const itIfCI = process.env.CI ? it : it.skip;
|
||||
|
||||
describe("IPFS", async () => {
|
||||
describe("IPFS", { timeout: 30000 }, async () => {
|
||||
if (!secretKey) {
|
||||
throw new Error("TW_SECRET_KEY is not set in the environment variables");
|
||||
}
|
||||
@@ -413,7 +412,7 @@ describe("IPFS", async () => {
|
||||
});
|
||||
|
||||
// only run this in CI because on local it will always be different (different api key)
|
||||
itIfCI(
|
||||
it.runIf(process.env.CI)(
|
||||
"Should return URIs with gateway URLs if specified on function",
|
||||
async () => {
|
||||
const uri = await storage.upload(
|
||||
|
||||
@@ -520,34 +520,34 @@
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@account-abstraction/contracts": "^0.5.0",
|
||||
"@blocto/sdk": "^0.5.4",
|
||||
"@coinbase/wallet-sdk": "^3.7.1",
|
||||
"@blocto/sdk": "0.5.5",
|
||||
"@coinbase/wallet-sdk": "^3.7.2",
|
||||
"@google-cloud/kms": "3.0.1",
|
||||
"@magic-ext/connect": "^6.7.2",
|
||||
"@magic-ext/oauth": "^7.6.2",
|
||||
"@magic-sdk/provider": "^13.6.2",
|
||||
"@metamask/eth-sig-util": "^4.0.0",
|
||||
"@metamask/eth-sig-util": "^4.0.1",
|
||||
"@paperxyz/embedded-wallet-service-sdk": "^1.2.5",
|
||||
"@paperxyz/sdk-common-utilities": "^0.1.0",
|
||||
"@safe-global/safe-core-sdk": "^3.3.4",
|
||||
"@paperxyz/sdk-common-utilities": "^0.1.1",
|
||||
"@safe-global/safe-core-sdk": "^3.3.5",
|
||||
"@safe-global/safe-ethers-adapters": "0.1.0-alpha.17",
|
||||
"@safe-global/safe-ethers-lib": "^1.9.4",
|
||||
"@thirdweb-dev/chains": "workspace:*",
|
||||
"@thirdweb-dev/contracts-js": "workspace:*",
|
||||
"@thirdweb-dev/crypto": "workspace:*",
|
||||
"@thirdweb-dev/sdk": "workspace:*",
|
||||
"@walletconnect/core": "^2.9.1",
|
||||
"@walletconnect/ethereum-provider": "^2.9.1",
|
||||
"@walletconnect/core": "^2.11.3",
|
||||
"@walletconnect/ethereum-provider": "^2.11.3",
|
||||
"@walletconnect/jsonrpc-utils": "^1.0.8",
|
||||
"@walletconnect/modal": "^2.6.1",
|
||||
"@walletconnect/types": "^2.9.1",
|
||||
"@walletconnect/utils": "^2.10.2",
|
||||
"@walletconnect/web3wallet": "^1.8.7",
|
||||
"@walletconnect/modal": "^2.6.2",
|
||||
"@walletconnect/types": "^2.11.3",
|
||||
"@walletconnect/utils": "^2.11.3",
|
||||
"@walletconnect/web3wallet": "^1.10.3",
|
||||
"asn1.js": "5.4.1",
|
||||
"bn.js": "5.2.0",
|
||||
"bn.js": "5.2.1",
|
||||
"buffer": "^6.0.3",
|
||||
"eth-provider": "^0.13.6",
|
||||
"ethereumjs-util": "^7.1.3",
|
||||
"ethereumjs-util": "^7.1.5",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"key-encoder": "2.0.3",
|
||||
"magic-sdk": "^13.6.2",
|
||||
@@ -582,34 +582,31 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.378.0",
|
||||
"@babel/plugin-transform-class-properties": "^7.23.3",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.23.3",
|
||||
"@babel/plugin-transform-private-methods": "^7.23.3",
|
||||
"@microsoft/api-extractor": "^7.36.3",
|
||||
"@noble/ed25519": "^1.7.1",
|
||||
"@aws-sdk/client-secrets-manager": "^3.543.0",
|
||||
"@babel/plugin-transform-class-properties": "^7.24.1",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.24.1",
|
||||
"@babel/plugin-transform-private-methods": "^7.24.1",
|
||||
"@microsoft/api-extractor": "^7.43.0",
|
||||
"@noble/ed25519": "^1.7.3",
|
||||
"@nomiclabs/hardhat-ethers": "^2.2.3",
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/bn.js": "^5.1.1",
|
||||
"@types/jest": "^29.5.3",
|
||||
"abitype": "^0.2.5",
|
||||
"@types/bn.js": "^5.1.5",
|
||||
"abitype": "1.0.0",
|
||||
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
|
||||
"bs58": "^5.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"eslint-plugin-better-tree-shaking": "0.0.4",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"ethereum-provider": "^0.7.7",
|
||||
"ethers": "^5.7.2",
|
||||
"ethers-aws-kms-signer": "^1.3.2",
|
||||
"hardhat": "^2.17.0",
|
||||
"jest": "^29.6.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-jest": "^29.1.1",
|
||||
"hardhat": "^2.22.2",
|
||||
"rimraf": "5.0.5",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"typedoc-gen": "workspace:*",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write 'src/**/*'",
|
||||
@@ -620,12 +617,9 @@
|
||||
"preconstruct:fix": "preconstruct fix",
|
||||
"push": "yalc push",
|
||||
"typedoc": "node scripts/typedoc.mjs",
|
||||
"test": "jest"
|
||||
"test": "vitest"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"jest": {
|
||||
"testTimeout": 30000
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ import { LocalWallet } from "../src/evm/wallets/local-wallet";
|
||||
import { BaseSepoliaTestnet } from "@thirdweb-dev/chains";
|
||||
import { ThirdwebSDK, SmartContract } from "@thirdweb-dev/sdk";
|
||||
import { BigNumber, ethers } from "ethers";
|
||||
import { describe, it, expect, beforeAll } from "vitest";
|
||||
|
||||
require("dotenv-mono").load();
|
||||
|
||||
jest.setTimeout(240_000);
|
||||
|
||||
let smartWallet: SmartWallet;
|
||||
let smartWalletAddress: string;
|
||||
let personalWallet: LocalWallet;
|
||||
@@ -42,66 +41,75 @@ beforeAll(async () => {
|
||||
);
|
||||
});
|
||||
|
||||
describeIf(!!process.env.TW_SECRET_KEY)("ERC20Paymaster core tests", () => {
|
||||
it("can connect", async () => {
|
||||
expect(smartWalletAddress).toHaveLength(42);
|
||||
});
|
||||
|
||||
it("should deploy+approve (gasless) and execute a tx", async () => {
|
||||
const tokenContract = await sdk.getContract(erc20TokenAddress);
|
||||
|
||||
// initial checks
|
||||
let approvedAmount = (
|
||||
await tokenContract.erc20.allowanceOf(
|
||||
smartWalletAddress,
|
||||
erc20PaymasterAddress,
|
||||
)
|
||||
).value;
|
||||
expect(approvedAmount.toNumber()).toEqual(0);
|
||||
|
||||
let isDeployed = await smartWallet.isDeployed();
|
||||
expect(isDeployed).toEqual(false);
|
||||
|
||||
let balance = (await contract.erc20.balance()).value;
|
||||
expect(balance.toNumber()).toEqual(0);
|
||||
|
||||
// airdrop tokens for payment
|
||||
const tempLocalWallet = new LocalWallet();
|
||||
await tempLocalWallet.generate();
|
||||
const tempSmartWallet = new SmartWallet({
|
||||
chain,
|
||||
factoryAddress,
|
||||
gasless: true,
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
describeIf(!!process.env.TW_SECRET_KEY)(
|
||||
"ERC20Paymaster core tests",
|
||||
{
|
||||
timeout: 240_000,
|
||||
},
|
||||
() => {
|
||||
it("can connect", async () => {
|
||||
expect(smartWalletAddress).toHaveLength(42);
|
||||
});
|
||||
await tempSmartWallet.connect({
|
||||
personalWallet: tempLocalWallet,
|
||||
});
|
||||
const claimTx = await contract.erc20.claimTo.prepare(smartWalletAddress, 1); // tx costs 1
|
||||
const claimReceipt = await tempSmartWallet.execute(claimTx);
|
||||
expect(claimReceipt.receipt.transactionHash).toHaveLength(66);
|
||||
balance = (await contract.erc20.balance()).value;
|
||||
expect(balance).toEqual(ethers.utils.parseEther("1"));
|
||||
|
||||
// transact with newly funded smart wallet using erc20 exclusively
|
||||
const preparedTx = await contract.erc20.claim.prepare(1);
|
||||
const tx = await smartWallet.execute(preparedTx);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
it("should deploy+approve (gasless) and execute a tx", async () => {
|
||||
const tokenContract = await sdk.getContract(erc20TokenAddress);
|
||||
|
||||
approvedAmount = (
|
||||
await tokenContract.erc20.allowanceOf(
|
||||
// initial checks
|
||||
let approvedAmount = (
|
||||
await tokenContract.erc20.allowanceOf(
|
||||
smartWalletAddress,
|
||||
erc20PaymasterAddress,
|
||||
)
|
||||
).value;
|
||||
expect(approvedAmount.toNumber()).toEqual(0);
|
||||
|
||||
let isDeployed = await smartWallet.isDeployed();
|
||||
expect(isDeployed).toEqual(false);
|
||||
|
||||
let balance = (await contract.erc20.balance()).value;
|
||||
expect(balance.toNumber()).toEqual(0);
|
||||
|
||||
// airdrop tokens for payment
|
||||
const tempLocalWallet = new LocalWallet();
|
||||
await tempLocalWallet.generate();
|
||||
const tempSmartWallet = new SmartWallet({
|
||||
chain,
|
||||
factoryAddress,
|
||||
gasless: true,
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
});
|
||||
await tempSmartWallet.connect({
|
||||
personalWallet: tempLocalWallet,
|
||||
});
|
||||
const claimTx = await contract.erc20.claimTo.prepare(
|
||||
smartWalletAddress,
|
||||
erc20PaymasterAddress,
|
||||
)
|
||||
).value;
|
||||
expect(approvedAmount).toEqual(
|
||||
BigNumber.from(2).pow(96).sub(1).sub(ethers.utils.parseEther("1")), // 2^96 - 1 - 1^18 <- 1^18 is the tx cost removed from allowance
|
||||
);
|
||||
1,
|
||||
); // tx costs 1
|
||||
const claimReceipt = await tempSmartWallet.execute(claimTx);
|
||||
expect(claimReceipt.receipt.transactionHash).toHaveLength(66);
|
||||
balance = (await contract.erc20.balance()).value;
|
||||
expect(balance).toEqual(ethers.utils.parseEther("1"));
|
||||
|
||||
isDeployed = await smartWallet.isDeployed();
|
||||
expect(isDeployed).toEqual(true);
|
||||
// transact with newly funded smart wallet using erc20 exclusively
|
||||
const preparedTx = await contract.erc20.claim.prepare(1);
|
||||
const tx = await smartWallet.execute(preparedTx);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
|
||||
balance = (await contract.erc20.balance()).value;
|
||||
expect(balance).toEqual(ethers.utils.parseEther("1")); // we spent 1 token to claim 1 token
|
||||
});
|
||||
});
|
||||
approvedAmount = (
|
||||
await tokenContract.erc20.allowanceOf(
|
||||
smartWalletAddress,
|
||||
erc20PaymasterAddress,
|
||||
)
|
||||
).value;
|
||||
expect(approvedAmount).toEqual(
|
||||
BigNumber.from(2).pow(96).sub(1).sub(ethers.utils.parseEther("1")), // 2^96 - 1 - 1^18 <- 1^18 is the tx cost removed from allowance
|
||||
);
|
||||
|
||||
isDeployed = await smartWallet.isDeployed();
|
||||
expect(isDeployed).toEqual(true);
|
||||
|
||||
balance = (await contract.erc20.balance()).value;
|
||||
expect(balance).toEqual(ethers.utils.parseEther("1")); // we spent 1 token to claim 1 token
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -4,11 +4,10 @@ import { LocalWallet } from "../src/evm/wallets/local-wallet";
|
||||
import { ArbitrumSepolia } from "@thirdweb-dev/chains";
|
||||
import { ThirdwebSDK, SmartContract } from "@thirdweb-dev/sdk";
|
||||
import { checkContractWalletSignature } from "../src/evm/connectors/smart-wallet/lib/check-contract-wallet-signature";
|
||||
import { describe, it, expect, beforeAll } from "vitest";
|
||||
|
||||
require("dotenv-mono").load();
|
||||
|
||||
jest.setTimeout(240_000);
|
||||
|
||||
let smartWallet: SmartWallet;
|
||||
let smartWalletAddress: string;
|
||||
let personalWallet: LocalWallet;
|
||||
@@ -39,116 +38,122 @@ beforeAll(async () => {
|
||||
);
|
||||
});
|
||||
|
||||
describeIf(!!SECRET_KEY)("SmartWallet core tests", () => {
|
||||
it("can connect", async () => {
|
||||
expect(smartWalletAddress).toHaveLength(42);
|
||||
});
|
||||
|
||||
it("can execute a tx via SDK", async () => {
|
||||
const tx = await contract.erc1155.claim(0, 1);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const isDeployed = await smartWallet.isDeployed();
|
||||
expect(isDeployed).toEqual(true);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(1);
|
||||
});
|
||||
|
||||
it("can estimate a tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const estimates = await smartWallet.estimate(preparedTx);
|
||||
expect(estimates.wei.toString()).not.toBe("0");
|
||||
});
|
||||
|
||||
it("can execute a tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const tx = await smartWallet.execute(preparedTx);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(2);
|
||||
});
|
||||
|
||||
it("can execute a batched tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const tx = await smartWallet.executeBatch([preparedTx, preparedTx]);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(4);
|
||||
});
|
||||
|
||||
it("can execute a raw tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const tx = await smartWallet.executeRaw({
|
||||
to: preparedTx.getTarget(),
|
||||
data: preparedTx.encode(),
|
||||
describeIf(!!SECRET_KEY)(
|
||||
"SmartWallet core tests",
|
||||
{
|
||||
timeout: 240_000,
|
||||
},
|
||||
() => {
|
||||
it("can connect", async () => {
|
||||
expect(smartWalletAddress).toHaveLength(42);
|
||||
});
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(5);
|
||||
});
|
||||
|
||||
it("can execute a batched raw tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const tx = await smartWallet.executeBatchRaw([
|
||||
{
|
||||
it("can execute a tx via SDK", async () => {
|
||||
const tx = await contract.erc1155.claim(0, 1);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const isDeployed = await smartWallet.isDeployed();
|
||||
expect(isDeployed).toEqual(true);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(1);
|
||||
});
|
||||
|
||||
it("can estimate a tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const estimates = await smartWallet.estimate(preparedTx);
|
||||
expect(estimates.wei.toString()).not.toBe("0");
|
||||
});
|
||||
|
||||
it("can execute a tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const tx = await smartWallet.execute(preparedTx);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(2);
|
||||
});
|
||||
|
||||
it("can execute a batched tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const tx = await smartWallet.executeBatch([preparedTx, preparedTx]);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(4);
|
||||
});
|
||||
|
||||
it("can execute a raw tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const tx = await smartWallet.executeRaw({
|
||||
to: preparedTx.getTarget(),
|
||||
data: preparedTx.encode(),
|
||||
},
|
||||
{
|
||||
to: preparedTx.getTarget(),
|
||||
data: preparedTx.encode(),
|
||||
},
|
||||
]);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(7);
|
||||
});
|
||||
|
||||
it("can sign and verify 1271 old factory", async () => {
|
||||
const message = "0x1234";
|
||||
const sig = await smartWallet.signMessage(message);
|
||||
const isValidV1 = await smartWallet.verifySignature(
|
||||
message,
|
||||
sig,
|
||||
smartWalletAddress,
|
||||
chain.chainId,
|
||||
);
|
||||
expect(isValidV1).toEqual(true);
|
||||
const isValidV2 = await checkContractWalletSignature(
|
||||
message,
|
||||
sig,
|
||||
smartWalletAddress,
|
||||
chain.chainId,
|
||||
undefined,
|
||||
SECRET_KEY,
|
||||
);
|
||||
expect(isValidV2).toEqual(true);
|
||||
});
|
||||
|
||||
it("can sign and verify 1271 new factory", async () => {
|
||||
smartWallet = new SmartWallet({
|
||||
chain,
|
||||
factoryAddress: factoryAddressV2,
|
||||
gasless: true,
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
});
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(5);
|
||||
});
|
||||
smartWalletAddress = await smartWallet.connect({ personalWallet });
|
||||
const message = "0x1234";
|
||||
const sig = await smartWallet.signMessage(message);
|
||||
const isValidV1 = await smartWallet.verifySignature(
|
||||
message,
|
||||
sig,
|
||||
smartWalletAddress,
|
||||
chain.chainId,
|
||||
);
|
||||
expect(isValidV1).toEqual(true);
|
||||
const isValidV2 = await checkContractWalletSignature(
|
||||
message,
|
||||
sig,
|
||||
smartWalletAddress,
|
||||
chain.chainId,
|
||||
undefined,
|
||||
SECRET_KEY,
|
||||
);
|
||||
expect(isValidV2).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("can execute a batched raw tx", async () => {
|
||||
const preparedTx = await contract.erc1155.claim.prepare(0, 1);
|
||||
const tx = await smartWallet.executeBatchRaw([
|
||||
{
|
||||
to: preparedTx.getTarget(),
|
||||
data: preparedTx.encode(),
|
||||
},
|
||||
{
|
||||
to: preparedTx.getTarget(),
|
||||
data: preparedTx.encode(),
|
||||
},
|
||||
]);
|
||||
expect(tx.receipt.transactionHash).toHaveLength(66);
|
||||
const balance = await contract.erc1155.balance(0);
|
||||
expect(balance.toNumber()).toEqual(7);
|
||||
});
|
||||
|
||||
it("can sign and verify 1271 old factory", async () => {
|
||||
const message = "0x1234";
|
||||
const sig = await smartWallet.signMessage(message);
|
||||
const isValidV1 = await smartWallet.verifySignature(
|
||||
message,
|
||||
sig,
|
||||
smartWalletAddress,
|
||||
chain.chainId,
|
||||
);
|
||||
expect(isValidV1).toEqual(true);
|
||||
const isValidV2 = await checkContractWalletSignature(
|
||||
message,
|
||||
sig,
|
||||
smartWalletAddress,
|
||||
chain.chainId,
|
||||
undefined,
|
||||
SECRET_KEY,
|
||||
);
|
||||
expect(isValidV2).toEqual(true);
|
||||
});
|
||||
|
||||
it("can sign and verify 1271 new factory", async () => {
|
||||
smartWallet = new SmartWallet({
|
||||
chain,
|
||||
factoryAddress: factoryAddressV2,
|
||||
gasless: true,
|
||||
secretKey: process.env.TW_SECRET_KEY,
|
||||
});
|
||||
smartWalletAddress = await smartWallet.connect({ personalWallet });
|
||||
const message = "0x1234";
|
||||
const sig = await smartWallet.signMessage(message);
|
||||
const isValidV1 = await smartWallet.verifySignature(
|
||||
message,
|
||||
sig,
|
||||
smartWalletAddress,
|
||||
chain.chainId,
|
||||
);
|
||||
expect(isValidV1).toEqual(true);
|
||||
const isValidV2 = await checkContractWalletSignature(
|
||||
message,
|
||||
sig,
|
||||
smartWalletAddress,
|
||||
chain.chainId,
|
||||
undefined,
|
||||
SECRET_KEY,
|
||||
);
|
||||
expect(isValidV2).toEqual(true);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { SmartWallet } from "../src/evm/wallets/smart-wallet";
|
||||
import { EthersWallet } from "../src/evm/wallets/ethers";
|
||||
import { EntryPoint__factory } from "@account-abstraction/contracts";
|
||||
import { AbstractWallet } from "../src";
|
||||
import { describe, it, beforeAll } from "vitest";
|
||||
|
||||
// TODO make this work with a mock bundler/paymaster
|
||||
|
||||
|
||||
+5
-3
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"test": "turbo run test --filter=./packages/* --filter=./legacy_packages/*",
|
||||
"test": "turbo run test --filter=./packages/*",
|
||||
"test:legacy": "turbo run test --filter=./legacy_packages/*",
|
||||
"e2e": "turbo run e2e",
|
||||
"build": "turbo run build --filter=./packages/* --filter=./legacy_packages/*",
|
||||
"bench": "turbo run bench",
|
||||
@@ -77,7 +78,7 @@
|
||||
"sharp": "^0.33.3",
|
||||
"size-limit": "11.1.2",
|
||||
"typedoc-gen": "workspace:*",
|
||||
"typescript": "5.3.3",
|
||||
"typescript": "5.4.3",
|
||||
"vitest": "1.4.0"
|
||||
},
|
||||
"manypkg": {
|
||||
@@ -97,7 +98,8 @@
|
||||
"semver@<7.5.2": ">=7.5.2",
|
||||
"json5@<1.0.2": ">=1.0.2",
|
||||
"protobufjs@>=6.10.0 <7.2.4": ">=7.2.4",
|
||||
"tough-cookie@<4.1.3": ">=4.1.3"
|
||||
"tough-cookie@<4.1.3": ">=4.1.3",
|
||||
"@ethereumjs/util@<9.0.0": ">=9.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,13 +46,10 @@
|
||||
"@cloudflare/workers-types": "4.20240320.1",
|
||||
"@preconstruct/cli": "2.7.0",
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"@types/jest": "^29.5.3",
|
||||
"@types/node": "^20.11.30",
|
||||
"@types/node": "20.11.30",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"jest": "^29.6.2",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write 'src/**/*'",
|
||||
@@ -61,6 +58,6 @@
|
||||
"clean": "rm -rf dist/",
|
||||
"build": "tsc && preconstruct build",
|
||||
"push": "yalc push",
|
||||
"test": "jest"
|
||||
"test": "vitest"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { authorize } from ".";
|
||||
import { CoreServiceConfig } from "../api";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
const validServiceConfig: CoreServiceConfig = {
|
||||
apiUrl: "https://api.example.com",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { validApiKeyMeta } from "../../mocks";
|
||||
import { ClientAuthorizationPayload, authorizeClient } from "./client";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("authorizeClient", () => {
|
||||
const validAuthOptions: ClientAuthorizationPayload = {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "../../mocks";
|
||||
import { CoreServiceConfig } from "../api";
|
||||
import { ServiceAuthorizationPayload, authorizeService } from "./service";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("authorizeService", () => {
|
||||
it("should authorize service with valid service scope and action", () => {
|
||||
|
||||
@@ -2,15 +2,16 @@ import { rateLimit } from ".";
|
||||
import { validApiKeyMeta, validServiceConfig } from "../../mocks";
|
||||
import { updateRateLimitedAt } from "../api";
|
||||
import { AuthorizationResult } from "../authorize/types";
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
const mockRedis = {
|
||||
incr: jest.fn(),
|
||||
expire: jest.fn(),
|
||||
incr: vi.fn(),
|
||||
expire: vi.fn(),
|
||||
};
|
||||
|
||||
// Mocking the updateRateLimitedAt function
|
||||
jest.mock("../../../src/core/api", () => ({
|
||||
updateRateLimitedAt: jest.fn(),
|
||||
vi.mock("../../../src/core/api", () => ({
|
||||
updateRateLimitedAt: vi.fn(),
|
||||
}));
|
||||
|
||||
const DEFAULT_AUTHZ_RESULT: AuthorizationResult = {
|
||||
@@ -25,13 +26,13 @@ const DEFAULT_AUTHZ_RESULT: AuthorizationResult = {
|
||||
describe("rateLimit", () => {
|
||||
beforeEach(() => {
|
||||
// Clear mock function calls and reset any necessary state.
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
mockRedis.incr.mockReset();
|
||||
mockRedis.expire.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.spyOn(global.Math, "random").mockRestore();
|
||||
vi.spyOn(global.Math, "random").mockRestore();
|
||||
});
|
||||
|
||||
it("should not rate limit if service scope is not in rate limits", async () => {
|
||||
@@ -128,7 +129,7 @@ describe("rateLimit", () => {
|
||||
|
||||
it("enforces rate limit if sampled (hit)", async () => {
|
||||
mockRedis.incr.mockResolvedValue(10);
|
||||
jest.spyOn(global.Math, "random").mockReturnValue(0.08);
|
||||
vi.spyOn(global.Math, "random").mockReturnValue(0.08);
|
||||
|
||||
const result = await rateLimit({
|
||||
authzResult: DEFAULT_AUTHZ_RESULT,
|
||||
@@ -146,7 +147,7 @@ describe("rateLimit", () => {
|
||||
|
||||
it("does not enforce rate limit if sampled (miss)", async () => {
|
||||
mockRedis.incr.mockResolvedValue(10);
|
||||
jest.spyOn(global.Math, "random").mockReturnValue(0.15);
|
||||
vi.spyOn(global.Math, "random").mockReturnValue(0.15);
|
||||
|
||||
const result = await rateLimit({
|
||||
authzResult: DEFAULT_AUTHZ_RESULT,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { usageLimit } from ".";
|
||||
import { validApiKeyMeta, validServiceConfig } from "../../mocks";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("usageLimit", () => {
|
||||
it("should not usage limit if service scope is not in limits", async () => {
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
"mipd": "0.0.7",
|
||||
"node-libs-browser": "2.2.1",
|
||||
"uqr": "0.1.2",
|
||||
"viem": "2.9.4"
|
||||
"viem": "2.9.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ethers": "^5 || ^6",
|
||||
@@ -225,7 +225,7 @@
|
||||
"push": "yalc push",
|
||||
"size": "size-limit",
|
||||
"test:watch": "vitest -c ./test/vitest.config.ts dev",
|
||||
"test": "CI=true vitest -c ./test/vitest.config.ts --coverage --retry=3 --bail=1 --pool=forks",
|
||||
"test": "CI=true vitest -c ./test/vitest.config.ts --coverage --bail=1",
|
||||
"test:cov": "vitest dev -c ./test/vitest.config.ts --coverage",
|
||||
"test:dev": "vitest dev -c ./test/vitest.config.ts",
|
||||
"typedoc": "node scripts/typedoc.mjs",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
resolveAbiFromContractApi,
|
||||
resolveContractAbi,
|
||||
@@ -7,18 +7,11 @@ import {
|
||||
import { DOODLES_CONTRACT } from "~test/test-contracts.js";
|
||||
import { DOODLES_ABI } from "../../../test/src/abis/doodles.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("resolveContractAbi", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should use the abi on the contract if it exists", async () => {
|
||||
const DOODLES_CONTRACT_CLONE = { ...DOODLES_CONTRACT, abi: DOODLES_ABI };
|
||||
const abi = await resolveContractAbi(DOODLES_CONTRACT_CLONE);
|
||||
expect(abi).toMatchObject(DOODLES_ABI);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("should resolve abi from contract", async () => {
|
||||
@@ -26,7 +19,6 @@ describe("resolveContractAbi", () => {
|
||||
const DOODLES_CONTRACT_CLONE = { ...DOODLES_CONTRACT };
|
||||
const abi = await resolveContractAbi(DOODLES_CONTRACT_CLONE);
|
||||
expect(abi).toMatchObject(DOODLES_ABI);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should cache the result on a contract level", async () => {
|
||||
@@ -34,46 +26,25 @@ describe("resolveContractAbi", () => {
|
||||
const DOODLES_CONTRACT_CLONE = { ...DOODLES_CONTRACT };
|
||||
const abi1 = await resolveContractAbi(DOODLES_CONTRACT_CLONE);
|
||||
expect(abi1).toMatchObject(DOODLES_ABI);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(fetchSpy).toHaveBeenCalledWith(
|
||||
`https://contract.thirdweb.com/abi/1/${DOODLES_CONTRACT_CLONE.address}`,
|
||||
expect.any(Object),
|
||||
);
|
||||
|
||||
const abi2 = await resolveContractAbi(DOODLES_CONTRACT_CLONE);
|
||||
expect(abi2).toMatchObject(DOODLES_ABI);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveAbiFromContractApi", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it("should resolve abi from contract api", async () => {
|
||||
it("should resolve abi from contract api", async () => {
|
||||
// we do this so we don't hit any PRIOR cache
|
||||
const DOODLES_CONTRACT_CLONE = { ...DOODLES_CONTRACT };
|
||||
const abi = await resolveAbiFromContractApi(DOODLES_CONTRACT_CLONE);
|
||||
expect(abi).toMatchObject(DOODLES_ABI);
|
||||
});
|
||||
|
||||
it.runIf(process.env.TW_SECRET_KEY)(
|
||||
"should resolve abi from bytecode",
|
||||
async () => {
|
||||
// we do this so we don't hit any PRIOR cache
|
||||
const DOODLES_CONTRACT_CLONE = { ...DOODLES_CONTRACT };
|
||||
const abi = await resolveAbiFromContractApi(DOODLES_CONTRACT_CLONE);
|
||||
const abi = await resolveAbiFromBytecode(DOODLES_CONTRACT_CLONE);
|
||||
expect(abi).toMatchObject(DOODLES_ABI);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(fetchSpy).toHaveBeenCalledWith(
|
||||
`https://contract.thirdweb.com/abi/1/${DOODLES_CONTRACT_CLONE.address}`,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveAbiFromContractApi", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it.runIf(process.env.TW_SECRET_KEY)(
|
||||
"should resolve abi from contract api",
|
||||
async () => {
|
||||
// we do this so we don't hit any PRIOR cache
|
||||
const DOODLES_CONTRACT_CLONE = { ...DOODLES_CONTRACT };
|
||||
const abi = await resolveAbiFromBytecode(DOODLES_CONTRACT_CLONE);
|
||||
expect(abi).toMatchObject(DOODLES_ABI);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { getContractMetadata } from "./getContractMetadata.js";
|
||||
import { USDC_CONTRACT } from "../../../../test/src/test-contracts.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("shared.getContractMetadata", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should return the correct contract metadata", async () => {
|
||||
const metadata = await getContractMetadata({
|
||||
contract: USDC_CONTRACT,
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { DROP1155_CONTRACT } from "../../../../../test/src/test-contracts.js";
|
||||
import { getActiveClaimCondition } from "./getActiveClaimCondition.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("erc1155.getClaimConditions", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it("should return the correct claim conditions", async () => {
|
||||
const cc = await getActiveClaimCondition({
|
||||
contract: DROP1155_CONTRACT,
|
||||
@@ -28,6 +23,5 @@ describe("erc1155.getClaimConditions", () => {
|
||||
supplyClaimed: 1382n,
|
||||
});
|
||||
// 1 call for the condition id and 1 call for the condition
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { getNFT } from "./getNFT.js";
|
||||
import { DROP1155_CONTRACT } from "~test/test-contracts.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("erc1155.getNFT", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it.runIf(process.env.TW_SECRET_KEY)("without owner", async () => {
|
||||
const nft = await getNFT({
|
||||
contract: DROP1155_CONTRACT,
|
||||
@@ -45,7 +40,5 @@ describe("erc1155.getNFT", () => {
|
||||
"type": "ERC1155",
|
||||
}
|
||||
`);
|
||||
// 2 fetch calls: 1 for RPC, 1 for fetching the tokenUri
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { getBalance } from "./getBalance.js";
|
||||
import { USDC_CONTRACT } from "~test/test-contracts.js";
|
||||
import { VITALIK_WALLET } from "~test/addresses.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("erc20.getBalance", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it("should return the getBalance result", async () => {
|
||||
const balance = await getBalance({
|
||||
contract: USDC_CONTRACT,
|
||||
@@ -24,6 +19,5 @@ describe("erc20.getBalance", () => {
|
||||
"value": 81831338n,
|
||||
}
|
||||
`);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { NFT_DROP_CONTRACT } from "../../../../../test/src/test-contracts.js";
|
||||
import { getActiveClaimCondition } from "./getActiveClaimCondition.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("erc721.getClaimConditions", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it("should return the correct claim conditions", async () => {
|
||||
const cc = await getActiveClaimCondition({
|
||||
contract: NFT_DROP_CONTRACT,
|
||||
@@ -25,7 +20,5 @@ describe("erc721.getClaimConditions", () => {
|
||||
startTimestamp: 1704652200n,
|
||||
supplyClaimed: 5598n,
|
||||
});
|
||||
// 1 call for the condition id and 1 call for the condition
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { getAllOwners } from "./getAllOwners.js";
|
||||
import { AZUKI_CONTRACT, DOODLES_CONTRACT } from "~test/test-contracts.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe.runIf(process.env.TW_SECRET_KEY)("erc721.getAllOwners", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("works for azuki", async () => {
|
||||
const nfts = await getAllOwners({
|
||||
contract: AZUKI_CONTRACT,
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { getNFT } from "./getNFT.js";
|
||||
import { DOODLES_CONTRACT } from "~test/test-contracts.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("erc721.getNFT", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it.runIf(process.env.TW_SECRET_KEY)("without owner", async () => {
|
||||
const nft = await getNFT({
|
||||
contract: DOODLES_CONTRACT,
|
||||
contract: { ...DOODLES_CONTRACT },
|
||||
tokenId: 1n,
|
||||
includeOwner: false,
|
||||
});
|
||||
@@ -50,8 +45,6 @@ describe("erc721.getNFT", () => {
|
||||
"type": "ERC721",
|
||||
}
|
||||
`);
|
||||
// 2 fetch calls: 1 for RPC, 1 for fetching the tokenUri
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it.runIf(process.env.TW_SECRET_KEY)("with owner", async () => {
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { getNFTs } from "./getNFTs.js";
|
||||
import { AZUKI_CONTRACT, DOODLES_CONTRACT } from "~test/test-contracts.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFTs", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("works for azuki", async () => {
|
||||
const nfts = await getNFTs({
|
||||
contract: AZUKI_CONTRACT,
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { getFid } from "./getFid.js";
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
import { FORKED_OPTIMISM_CHAIN } from "~test/chains.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("farcaster.getFid", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should return the address's fid", async () => {
|
||||
const fid = await getFid({
|
||||
client: TEST_CLIENT,
|
||||
@@ -18,7 +12,6 @@ describe("farcaster.getFid", () => {
|
||||
address: "0x6443ed8a3ffb006e3bb5a866bae6edfb93f8ff3e",
|
||||
});
|
||||
expect(fid).toBe(7657n);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should return 0 if the address has no fid", async () => {
|
||||
@@ -28,6 +21,5 @@ describe("farcaster.getFid", () => {
|
||||
address: "0x0000000000000000000000000000000000000000",
|
||||
});
|
||||
expect(fid).toBe(0n);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
import { getNonce } from "./getNonce.js";
|
||||
import { FORKED_OPTIMISM_CHAIN } from "~test/chains.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("farcaster.getNonce", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should return the address's current nonce", async () => {
|
||||
const nonce = await getNonce({
|
||||
client: TEST_CLIENT,
|
||||
@@ -18,6 +12,5 @@ describe("farcaster.getNonce", () => {
|
||||
address: "0x6443ed8a3ffb006e3bb5a866bae6edfb93f8ff3e",
|
||||
});
|
||||
expect(nonce).toBe(12n);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { getRegistrationPrice } from "./getRegistrationPrice.js";
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
import { FORKED_OPTIMISM_CHAIN } from "~test/chains.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("farcaster.getRegistrationPrice", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should return the price to register a new fid", async () => {
|
||||
const price = await getRegistrationPrice({
|
||||
client: TEST_CLIENT,
|
||||
chain: FORKED_OPTIMISM_CHAIN,
|
||||
});
|
||||
expect(price).toBe(824603002115370n);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should return the price to register a new fid with extra storage", async () => {
|
||||
@@ -27,6 +20,5 @@ describe("farcaster.getRegistrationPrice", () => {
|
||||
extraStorage: 3,
|
||||
});
|
||||
expect(price).toBe(3298412008461477n);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
import { getStoragePrice } from "./getStoragePrice.js";
|
||||
import { FORKED_OPTIMISM_CHAIN } from "~test/chains.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("farcaster.getStoragePrice", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should return the price to rent 1 unit of storage", async () => {
|
||||
const price = await getStoragePrice({
|
||||
client: TEST_CLIENT,
|
||||
chain: FORKED_OPTIMISM_CHAIN,
|
||||
});
|
||||
expect(price).toBe(824603002115370n);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should return the price to rent 3 units of storage", async () => {
|
||||
@@ -27,7 +20,6 @@ describe("farcaster.getStoragePrice", () => {
|
||||
units: 3,
|
||||
});
|
||||
expect(price).toBe(824603002115370n * 3n);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should error when 0 units specified", async () => {
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
import { getUsdRegistrationPrice } from "./getUsdRegistrationPrice.js";
|
||||
import { FORKED_OPTIMISM_CHAIN } from "~test/chains.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("farcaster.getRegistrationPrice", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should return the price to register a new fid in USD", async () => {
|
||||
const price = await getUsdRegistrationPrice({
|
||||
client: TEST_CLIENT,
|
||||
chain: FORKED_OPTIMISM_CHAIN,
|
||||
});
|
||||
expect(price).toBe(3);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should return the price to register a new fid with extra storage in USD", async () => {
|
||||
@@ -27,6 +20,5 @@ describe("farcaster.getRegistrationPrice", () => {
|
||||
extraStorage: 3,
|
||||
});
|
||||
expect(price).toBe(12);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
import { getUsdStoragePrice } from "./getUsdStoragePrice.js";
|
||||
import { FORKED_OPTIMISM_CHAIN } from "~test/chains.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("farcaster.getUsdStoragePrice", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should return the price to rent 1 unit of storage", async () => {
|
||||
const price = await getUsdStoragePrice({
|
||||
client: TEST_CLIENT,
|
||||
chain: FORKED_OPTIMISM_CHAIN,
|
||||
});
|
||||
expect(price).toBe(3);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should return the price to rent 3 units of storage", async () => {
|
||||
@@ -27,7 +20,6 @@ describe("farcaster.getUsdStoragePrice", () => {
|
||||
units: 3,
|
||||
});
|
||||
expect(price).toBe(9);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should error when 0 units specified", async () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { getUniswapV3Pools } from "./getUniswapV3Pools.js";
|
||||
import {
|
||||
@@ -8,13 +8,7 @@ import {
|
||||
MOG_CONTRACT_ADDRESS,
|
||||
} from "~test/test-contracts.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("uniswap.getUniswapV3Pool", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should return the WETH/OHM pool address and fee", async () => {
|
||||
const pools = await getUniswapV3Pools({
|
||||
contract: UNISWAPV3_FACTORY_CONTRACT,
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { eth_blockNumber } from "./eth_blockNumber.js";
|
||||
import { getRpcClient } from "../rpc.js";
|
||||
import { FORKED_ETHEREUM_CHAIN } from "~test/chains.js";
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
const rpcClient = getRpcClient({
|
||||
chain: FORKED_ETHEREUM_CHAIN,
|
||||
client: TEST_CLIENT,
|
||||
});
|
||||
|
||||
describe("eth_blockNumber", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it("should return the block number", async () => {
|
||||
const blockNumber = await eth_blockNumber(rpcClient);
|
||||
expect(blockNumber).toEqual(19139495n);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should dedupe requests", async () => {
|
||||
@@ -31,11 +25,5 @@ describe("eth_blockNumber", () => {
|
||||
expect(blockNumber1).toEqual(19139495n);
|
||||
expect(blockNumber2).toEqual(19139495n);
|
||||
expect(blockNumber3).toEqual(19139495n);
|
||||
// should only have been called once
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
// check the exact payload, we should not have more than one ethBlockNumber request in the body!
|
||||
expect(fetchSpy.mock.lastCall?.[1]?.body).toMatchInlineSnapshot(
|
||||
`"[{"method":"eth_blockNumber","id":0,"jsonrpc":"2.0"}]"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { getRpcClient } from "../rpc.js";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
|
||||
import { FORKED_ETHEREUM_CHAIN } from "~test/chains.js";
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
import { eth_getBalance } from "./eth_getBalance.js";
|
||||
@@ -8,8 +8,10 @@ import {
|
||||
VITALIK_WALLET,
|
||||
ZERO_ADDRESS,
|
||||
} from "~test/addresses.js";
|
||||
import { getRpcClient } from "../rpc.js";
|
||||
import * as fetchRpc from "../fetch-rpc.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
const fetchSpy = vi.spyOn(fetchRpc, "fetchRpc");
|
||||
|
||||
const rpcClient = getRpcClient({
|
||||
chain: FORKED_ETHEREUM_CHAIN,
|
||||
@@ -17,9 +19,6 @@ const rpcClient = getRpcClient({
|
||||
});
|
||||
|
||||
describe("eth_getBalance", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it("should return the correct balance at the given block", async () => {
|
||||
const vitalikBalance = await eth_getBalance(rpcClient, {
|
||||
address: VITALIK_WALLET,
|
||||
@@ -44,9 +43,22 @@ describe("eth_getBalance", () => {
|
||||
// should only have been called once
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
// check the exact payload, we should not have more than one ethBlockNumber request in the body!
|
||||
expect(fetchSpy.mock.lastCall?.[1]?.body).toMatchInlineSnapshot(
|
||||
`"[{"method":"eth_getBalance","params":["0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B","latest"],"id":0,"jsonrpc":"2.0"}]"`,
|
||||
);
|
||||
expect(fetchSpy.mock.lastCall?.[2]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"requestTimeoutMs": undefined,
|
||||
"requests": [
|
||||
{
|
||||
"id": 0,
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": [
|
||||
"0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
|
||||
"latest",
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("should batch requests correctly", async () => {
|
||||
@@ -77,8 +89,39 @@ describe("eth_getBalance", () => {
|
||||
// should only have been called once
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
// check the exact payload, we should not have more than one ethBlockNumber request in the body!
|
||||
expect(fetchSpy.mock.lastCall?.[1]?.body).toMatchInlineSnapshot(
|
||||
`"[{"method":"eth_getBalance","params":["0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B","latest"],"id":0,"jsonrpc":"2.0"},{"method":"eth_getBalance","params":["0x0000000000000000000000000000000000000000","latest"],"id":1,"jsonrpc":"2.0"},{"method":"eth_getBalance","params":["0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","latest"],"id":2,"jsonrpc":"2.0"}]"`,
|
||||
);
|
||||
expect(fetchSpy.mock.lastCall?.[2]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"requestTimeoutMs": undefined,
|
||||
"requests": [
|
||||
{
|
||||
"id": 0,
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": [
|
||||
"0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
|
||||
"latest",
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": [
|
||||
"0x0000000000000000000000000000000000000000",
|
||||
"latest",
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": [
|
||||
"0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
|
||||
"latest",
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { getRpcClient } from "../rpc.js";
|
||||
import { FORKED_ETHEREUM_CHAIN } from "~test/chains.js";
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
import { eth_getLogs } from "./eth_getLogs.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
const rpcClient = getRpcClient({
|
||||
chain: FORKED_ETHEREUM_CHAIN,
|
||||
client: TEST_CLIENT,
|
||||
});
|
||||
|
||||
describe("eth_getLogs", () => {
|
||||
afterEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
});
|
||||
it("should return unparsed logs, without events", async () => {
|
||||
const logs = await eth_getLogs(rpcClient);
|
||||
expect(logs).toMatchInlineSnapshot(`[]`);
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import type { ThirdwebClient } from "../client/client.js";
|
||||
import { getClientFetch } from "../utils/fetch.js";
|
||||
import { stringify } from "../utils/json.js";
|
||||
|
||||
export type RpcRequest = {
|
||||
jsonrpc?: "2.0";
|
||||
method: string;
|
||||
params?: any;
|
||||
id?: number;
|
||||
};
|
||||
|
||||
type FetchRpcOptions = {
|
||||
requests: RpcRequest[];
|
||||
requestTimeoutMs?: number;
|
||||
};
|
||||
|
||||
type SuccessResult<T> = {
|
||||
method?: never;
|
||||
result: T;
|
||||
error?: never;
|
||||
};
|
||||
type ErrorResult<T> = {
|
||||
method?: never;
|
||||
result?: never;
|
||||
error: T;
|
||||
};
|
||||
type Subscription<TResult, TError> = {
|
||||
method: "eth_subscription";
|
||||
error?: never;
|
||||
result?: never;
|
||||
params: {
|
||||
subscription: string;
|
||||
} & (
|
||||
| {
|
||||
result: TResult;
|
||||
error?: never;
|
||||
}
|
||||
| {
|
||||
result?: never;
|
||||
error: TError;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
type RpcResponse<TResult = any, TError = any> = {
|
||||
jsonrpc: `${number}`;
|
||||
id: number;
|
||||
} & (
|
||||
| SuccessResult<TResult>
|
||||
| ErrorResult<TError>
|
||||
| Subscription<TResult, TError>
|
||||
);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export async function fetchRpc(
|
||||
rpcUrl: string,
|
||||
client: ThirdwebClient,
|
||||
options: FetchRpcOptions,
|
||||
): Promise<RpcResponse[]> {
|
||||
const response = await getClientFetch(client)(rpcUrl, {
|
||||
headers: {
|
||||
...client.config?.rpc?.fetch?.headers,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: stringify(options.requests),
|
||||
method: "POST",
|
||||
requestTimeoutMs:
|
||||
options.requestTimeoutMs ?? client.config?.rpc?.fetch?.requestTimeoutMs,
|
||||
keepalive: client.config?.rpc?.fetch?.keepalive,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
response.body?.cancel();
|
||||
throw new Error(`RPC request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
if (response.headers.get("Content-Type")?.startsWith("application/json")) {
|
||||
result = await response.json();
|
||||
} else {
|
||||
result = await response.text();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
type FetchSingleRpcOptions = {
|
||||
request: RpcRequest;
|
||||
requestTimeoutMs?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export async function fetchSingleRpc(
|
||||
rpcUrl: string,
|
||||
client: ThirdwebClient,
|
||||
options: FetchSingleRpcOptions,
|
||||
): Promise<RpcResponse> {
|
||||
const response = await getClientFetch(client)(rpcUrl, {
|
||||
headers: {
|
||||
...(client.config?.rpc?.fetch?.headers || {}),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: stringify(options.request),
|
||||
method: "POST",
|
||||
requestTimeoutMs:
|
||||
options.requestTimeoutMs ?? client.config?.rpc?.fetch?.requestTimeoutMs,
|
||||
keepalive: client.config?.rpc?.fetch?.keepalive,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
response.body?.cancel();
|
||||
throw new Error(`RPC request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
if (response.headers.get("Content-Type")?.startsWith("application/json")) {
|
||||
result = await response.json();
|
||||
} else {
|
||||
result = await response.text();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,53 +1,9 @@
|
||||
import type { EIP1193RequestFn, EIP1474Methods } from "viem";
|
||||
import type { ThirdwebClient } from "../client/client.js";
|
||||
import { getClientFetch } from "../utils/fetch.js";
|
||||
import { stringify } from "../utils/json.js";
|
||||
|
||||
import { getRpcUrlForChain } from "../chains/utils.js";
|
||||
import type { Chain } from "../chains/types.js";
|
||||
|
||||
type SuccessResult<T> = {
|
||||
method?: never;
|
||||
result: T;
|
||||
error?: never;
|
||||
};
|
||||
type ErrorResult<T> = {
|
||||
method?: never;
|
||||
result?: never;
|
||||
error: T;
|
||||
};
|
||||
type Subscription<TResult, TError> = {
|
||||
method: "eth_subscription";
|
||||
error?: never;
|
||||
result?: never;
|
||||
params: {
|
||||
subscription: string;
|
||||
} & (
|
||||
| {
|
||||
result: TResult;
|
||||
error?: never;
|
||||
}
|
||||
| {
|
||||
result?: never;
|
||||
error: TError;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
type RpcRequest = {
|
||||
jsonrpc?: "2.0";
|
||||
method: string;
|
||||
params?: any;
|
||||
id?: number;
|
||||
};
|
||||
|
||||
type RpcResponse<TResult = any, TError = any> = {
|
||||
jsonrpc: `${number}`;
|
||||
id: number;
|
||||
} & (
|
||||
| SuccessResult<TResult>
|
||||
| ErrorResult<TError>
|
||||
| Subscription<TResult, TError>
|
||||
);
|
||||
import { fetchRpc, fetchSingleRpc, type RpcRequest } from "./fetch-rpc.js";
|
||||
|
||||
const RPC_CLIENT_MAP = new WeakMap();
|
||||
|
||||
@@ -233,6 +189,7 @@ export function getRpcClient(
|
||||
|
||||
return async function (request) {
|
||||
const requestKey = rpcRequestKey(request);
|
||||
|
||||
// if the request for this key is already inflight, return the promise directly
|
||||
if (inflightRequests.has(requestKey)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
@@ -266,82 +223,3 @@ export function getRpcClient(
|
||||
rpcClientMap.set(chainId, rpcClient);
|
||||
return rpcClient as EIP1193RequestFn<EIP1474Methods>;
|
||||
}
|
||||
|
||||
type FetchRpcOptions = {
|
||||
requests: RpcRequest[];
|
||||
requestTimeoutMs?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
async function fetchRpc(
|
||||
rpcUrl: string,
|
||||
client: ThirdwebClient,
|
||||
options: FetchRpcOptions,
|
||||
): Promise<RpcResponse[]> {
|
||||
const response = await getClientFetch(client)(rpcUrl, {
|
||||
headers: {
|
||||
...client.config?.rpc?.fetch?.headers,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: stringify(options.requests),
|
||||
method: "POST",
|
||||
requestTimeoutMs:
|
||||
options.requestTimeoutMs ?? client.config?.rpc?.fetch?.requestTimeoutMs,
|
||||
keepalive: client.config?.rpc?.fetch?.keepalive,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
response.body?.cancel();
|
||||
throw new Error(`RPC request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
if (response.headers.get("Content-Type")?.startsWith("application/json")) {
|
||||
result = await response.json();
|
||||
} else {
|
||||
result = await response.text();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
type FetchSingleRpcOptions = {
|
||||
request: RpcRequest;
|
||||
requestTimeoutMs?: number;
|
||||
};
|
||||
|
||||
async function fetchSingleRpc(
|
||||
rpcUrl: string,
|
||||
client: ThirdwebClient,
|
||||
options: FetchSingleRpcOptions,
|
||||
): Promise<RpcResponse> {
|
||||
const response = await getClientFetch(client)(rpcUrl, {
|
||||
headers: {
|
||||
...(client.config?.rpc?.fetch?.headers || {}),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: stringify(options.request),
|
||||
method: "POST",
|
||||
requestTimeoutMs:
|
||||
options.requestTimeoutMs ?? client.config?.rpc?.fetch?.requestTimeoutMs,
|
||||
keepalive: client.config?.rpc?.fetch?.keepalive,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
response.body?.cancel();
|
||||
throw new Error(`RPC request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
if (response.headers.get("Content-Type")?.startsWith("application/json")) {
|
||||
result = await response.json();
|
||||
} else {
|
||||
result = await response.text();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ export async function download(options: DownloadOptions) {
|
||||
});
|
||||
if (!res.ok) {
|
||||
res.body?.cancel();
|
||||
console.error("failed to download file", res.status, res.statusText);
|
||||
throw new Error(`Failed to download file: ${res.statusText}`);
|
||||
}
|
||||
return res;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { USDC_CONTRACT, USDC_CONTRACT_WITH_ABI } from "~test/test-contracts.js";
|
||||
import { TEST_WALLET_A } from "~test/addresses.js";
|
||||
import { encode } from "./encode.js";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import { resolveMethod } from "../resolve-method.js";
|
||||
import { prepareContractCall } from "../prepare-contract-call.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
const USDC_TRANSFER_ENCODE_RESULT =
|
||||
"0xa9059cbb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000064";
|
||||
|
||||
@@ -20,8 +18,6 @@ describe("transaction: encode", () => {
|
||||
});
|
||||
const encoded = await encode(tx);
|
||||
expect(encoded).toEqual(USDC_TRANSFER_ENCODE_RESULT);
|
||||
// we should not have made any network requests
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should encode correctly (transaction abi)", async () => {
|
||||
@@ -41,8 +37,6 @@ describe("transaction: encode", () => {
|
||||
});
|
||||
const encoded = await encode(tx);
|
||||
expect(encoded).toEqual(USDC_TRANSFER_ENCODE_RESULT);
|
||||
// we should not have made any network requests
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should encode correctly (contract abi)", async () => {
|
||||
@@ -53,8 +47,6 @@ describe("transaction: encode", () => {
|
||||
});
|
||||
const encoded = await encode(tx);
|
||||
expect(encoded).toEqual(USDC_TRANSFER_ENCODE_RESULT);
|
||||
// we should not have made any network requests
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should encode correctly (auto-abi)", async () => {
|
||||
@@ -65,7 +57,5 @@ describe("transaction: encode", () => {
|
||||
});
|
||||
const encoded = await encode(tx);
|
||||
expect(encoded).toEqual(USDC_TRANSFER_ENCODE_RESULT);
|
||||
// we should have called the contract metadata endpoint exactly once
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,30 +1,18 @@
|
||||
import { USDC_CONTRACT } from "~test/test-contracts.js";
|
||||
import { TEST_WALLET_A } from "~test/addresses.js";
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { it, expect } from "vitest";
|
||||
import { estimateGas } from "./estimate-gas.js";
|
||||
import { prepareContractCall } from "../prepare-contract-call.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("transaction: estimate-gas", () => {
|
||||
beforeEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
it("should estimate gas correctly (human-readable)", async () => {
|
||||
const tx = prepareContractCall({
|
||||
contract: USDC_CONTRACT,
|
||||
method: "function transfer(address, uint256) returns (bool)",
|
||||
params: [TEST_WALLET_A, 100n],
|
||||
});
|
||||
it("should estimate gas correctly (human-readable)", async () => {
|
||||
const tx = prepareContractCall({
|
||||
contract: USDC_CONTRACT,
|
||||
method: "function transfer(address, uint256) returns (bool)",
|
||||
params: [TEST_WALLET_A, 100n],
|
||||
});
|
||||
const gasEstimate = await estimateGas({
|
||||
transaction: tx,
|
||||
from: TEST_WALLET_A,
|
||||
});
|
||||
expect(gasEstimate).toMatchInlineSnapshot(`40504n`);
|
||||
// we should have made exactly 1 network request
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(fetchSpy.mock.lastCall?.[1]?.body).toMatchInlineSnapshot(
|
||||
`"[{"method":"eth_estimateGas","params":[{"to":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","data":"0xa9059cbb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000064","from":"0x0000000000000000000000000000000000000001"}],"id":0,"jsonrpc":"2.0"}]"`,
|
||||
);
|
||||
const gasEstimate = await estimateGas({
|
||||
transaction: tx,
|
||||
from: TEST_WALLET_A,
|
||||
});
|
||||
expect(gasEstimate).toMatchInlineSnapshot(`40504n`);
|
||||
});
|
||||
|
||||
@@ -1,32 +1,18 @@
|
||||
import { USDC_CONTRACT } from "~test/test-contracts.js";
|
||||
import { TEST_WALLET_A } from "~test/addresses.js";
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { it, expect } from "vitest";
|
||||
import { prepareContractCall } from "../prepare-contract-call.js";
|
||||
import { simulateTransaction } from "./simulate.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("transaction: simulate", () => {
|
||||
beforeEach(() => {
|
||||
fetchSpy.mockClear();
|
||||
it("should simulate correctly (human-readable)", async () => {
|
||||
const tx = prepareContractCall({
|
||||
contract: USDC_CONTRACT,
|
||||
method: "function transfer(address, uint256) returns (bool)",
|
||||
params: [TEST_WALLET_A, 100n],
|
||||
});
|
||||
it("should simulate correctly (human-readable)", async () => {
|
||||
const tx = prepareContractCall({
|
||||
contract: USDC_CONTRACT,
|
||||
method: "function transfer(address, uint256) returns (bool)",
|
||||
params: [TEST_WALLET_A, 100n],
|
||||
});
|
||||
const result = await simulateTransaction({
|
||||
transaction: tx,
|
||||
from: TEST_WALLET_A,
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`true`);
|
||||
|
||||
// we should have made exactly 1 network request
|
||||
// 1. eth_call
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(fetchSpy.mock.lastCall?.[1]?.body).toMatchInlineSnapshot(
|
||||
`"[{"method":"eth_call","params":[{"data":"0xa9059cbb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000064","from":"0x0000000000000000000000000000000000000001","to":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},"latest"],"id":0,"jsonrpc":"2.0"}]"`,
|
||||
);
|
||||
const result = await simulateTransaction({
|
||||
transaction: tx,
|
||||
from: TEST_WALLET_A,
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`true`);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import {
|
||||
DOODLES_CONTRACT,
|
||||
@@ -7,8 +7,6 @@ import {
|
||||
import { VITALIK_WALLET } from "../../test/src/addresses.js";
|
||||
import { readContract } from "./read-contract.js";
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
||||
|
||||
describe("transaction: read", () => {
|
||||
it("should read from the contract correctly", async () => {
|
||||
const result = await readContract({
|
||||
@@ -18,11 +16,6 @@ describe("transaction: read", () => {
|
||||
});
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`81831338n`);
|
||||
// we should have made exactly 1 network requests
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(fetchSpy.mock.lastCall?.[1]?.body).toMatchInlineSnapshot(
|
||||
`"[{"method":"eth_call","params":[{"data":"0x70a08231000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b","to":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},"latest"],"id":0,"jsonrpc":"2.0"}]"`,
|
||||
);
|
||||
});
|
||||
|
||||
it("should parse errors correctly", async () => {
|
||||
|
||||
@@ -237,13 +237,13 @@ export class EmbeddedWallet {
|
||||
if (_typedData.types && _typedData.types.EIP712Domain) {
|
||||
delete _typedData.types.EIP712Domain;
|
||||
}
|
||||
const chainId = Number(_typedData.domain?.chainId || 1);
|
||||
const chainId = Number((_typedData.domain as any)?.chainId || 1);
|
||||
|
||||
const { signedTypedData } =
|
||||
await querier.call<SignedTypedDataReturnType>({
|
||||
procedureName: "signTypedDataV4",
|
||||
params: {
|
||||
domain: _typedData.domain,
|
||||
domain: _typedData.domain as any,
|
||||
types:
|
||||
_typedData.types as SignerProcedureTypes["signTypedDataV4"]["types"],
|
||||
message:
|
||||
|
||||
@@ -24,5 +24,10 @@ export default defineConfig({
|
||||
// setupFiles: [join(__dirname, "./setup.ts")],
|
||||
globalSetup: [join(__dirname, "./globalSetup.ts")],
|
||||
testTimeout: 20_000,
|
||||
retry: process.env.CI ? 0 : 3,
|
||||
// clear any mocks between any tests
|
||||
clearMocks: true,
|
||||
pool: "forks",
|
||||
cache: false,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"typedoc": "^0.25.7"
|
||||
"typedoc": "^0.25.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsup": "^6.7.0"
|
||||
"tsup": "8.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"esbuild": "0.15.17",
|
||||
"esbuild": "0.20.2",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"node-stdlib-browser": "^1.2.0",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
|
||||
Generated
+4837
-8340
File diff suppressed because it is too large
Load Diff
@@ -14,8 +14,7 @@
|
||||
"e2e": "bun run.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "0.15.17",
|
||||
"execa": "^8.0.1",
|
||||
"esbuild": "0.20.2",
|
||||
"ora": "^6.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { execa } from "execa";
|
||||
import { $ } from "bun";
|
||||
import fs from "node:fs/promises";
|
||||
import ora from "ora";
|
||||
|
||||
@@ -9,7 +9,7 @@ const spinner = ora("Running fixtures: ").start();
|
||||
for (const fixture of fixtures) {
|
||||
spinner.suffixText = fixture;
|
||||
try {
|
||||
await execa("esbuild", ["fixtures/" + fixture, "--bundle"]);
|
||||
await $`esbuild fixtures/${fixture} --bundle`.text();
|
||||
} catch (error) {
|
||||
if (error.exitCode !== 0) {
|
||||
console.error(error);
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
"e2e": "bun run.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@swc/cli": "^0.1.63",
|
||||
"@swc/core": "^1.3.71",
|
||||
"execa": "^8.0.1",
|
||||
"@swc/cli": "0.3.10",
|
||||
"@swc/core": "^1.4.11",
|
||||
"ora": "^6.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { execa } from "execa";
|
||||
import { $ } from "bun";
|
||||
import fs from "node:fs/promises";
|
||||
import ora from "ora";
|
||||
|
||||
@@ -9,7 +9,7 @@ const spinner = ora("Running fixtures: ").start();
|
||||
for (const fixture of fixtures) {
|
||||
spinner.suffixText = fixture;
|
||||
try {
|
||||
await execa("bunx", ["swc", "fixtures/" + fixture]);
|
||||
await $`bunx swc fixtures/${fixture}`.text();
|
||||
} catch (error) {
|
||||
if (error.exitCode !== 0) {
|
||||
console.error(error);
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
"e2e": "bun run.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"execa": "^8.0.1",
|
||||
"ora": "^6.3.1",
|
||||
"vite": "^5.1.4"
|
||||
"vite": "^5.2.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { execa } from "execa";
|
||||
import { $ } from "bun";
|
||||
import fs from "node:fs/promises";
|
||||
import ora from "ora";
|
||||
|
||||
@@ -20,7 +20,7 @@ for (const fixture of fixtures) {
|
||||
`,
|
||||
);
|
||||
|
||||
await execa("vite", ["build", "fixtures/"]);
|
||||
await $`vite build fixtures/`.text();
|
||||
} catch (error) {
|
||||
if (error.exitCode !== 0) {
|
||||
console.error(error);
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
"e2e": "bun run.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"execa": "^8.0.1",
|
||||
"ora": "^6.3.1",
|
||||
"webpack": "^5.89.0",
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { execa } from "execa";
|
||||
import { $ } from "bun";
|
||||
import fs from "node:fs/promises";
|
||||
import ora from "ora";
|
||||
|
||||
@@ -9,7 +9,7 @@ const spinner = ora("Running fixtures: ").start();
|
||||
for (const fixture of fixtures) {
|
||||
spinner.suffixText = fixture;
|
||||
try {
|
||||
await execa("webpack", ["build", "./fixtures/" + fixture]);
|
||||
await $`webpack build ./fixtures/${fixture} --mode=production`.text();
|
||||
} catch (error) {
|
||||
if (error.exitCode !== 0) {
|
||||
console.error(error);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"e2e": "bun run.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"execa": "^8.0.1",
|
||||
"ora": "^6.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { execa } from "execa";
|
||||
import { $ } from "bun";
|
||||
import fs from "node:fs/promises";
|
||||
import ora from "ora";
|
||||
|
||||
@@ -9,7 +9,7 @@ const spinner = ora("Running fixtures: ").start();
|
||||
for (const fixture of fixtures) {
|
||||
spinner.suffixText = fixture;
|
||||
try {
|
||||
await execa("node", ["fixtures/" + fixture]);
|
||||
await $`node fixtures/${fixture}`.text();
|
||||
} catch (error) {
|
||||
if (error.exitCode !== 0) {
|
||||
console.error(error);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"e2e": "bun run.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"execa": "^8.0.1",
|
||||
"ora": "^6.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { execa } from "execa";
|
||||
import { $ } from "bun";
|
||||
import fs from "node:fs/promises";
|
||||
import ora from "ora";
|
||||
|
||||
@@ -9,7 +9,7 @@ const spinner = ora("Running fixtures: ").start();
|
||||
for (const fixture of fixtures) {
|
||||
spinner.suffixText = fixture;
|
||||
try {
|
||||
await execa("node", ["fixtures/" + fixture]);
|
||||
await $`node fixtures/${fixture}`.text();
|
||||
} catch (error) {
|
||||
if (error.exitCode !== 0) {
|
||||
console.error(error);
|
||||
|
||||
Reference in New Issue
Block a user