Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0299034fc1 | ||
|
|
861e623a1b | ||
|
|
6a335bfa77 | ||
|
|
cb994dcb8f | ||
|
|
4d38a7e8fe |
@@ -73,15 +73,18 @@ export async function createWebhook(
|
||||
): Promise<WebhookSingleResponse> {
|
||||
try {
|
||||
const authToken = await getAuthToken();
|
||||
const response = await fetch(`${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id": clientId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
const response = await fetch(
|
||||
`https://${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id": clientId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
@@ -105,13 +108,16 @@ export async function getWebhooks(
|
||||
): Promise<WebhooksListResponse> {
|
||||
try {
|
||||
const authToken = await getAuthToken();
|
||||
const response = await fetch(`${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"x-client-id": clientId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
const response = await fetch(
|
||||
`https://${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"x-client-id": clientId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
@@ -137,7 +143,7 @@ export async function deleteWebhook(
|
||||
try {
|
||||
const authToken = await getAuthToken();
|
||||
const response = await fetch(
|
||||
`${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks/${encodeURIComponent(webhookId)}`,
|
||||
`https://${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks/${encodeURIComponent(webhookId)}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
@@ -171,7 +177,7 @@ export async function testWebhook(
|
||||
try {
|
||||
const authToken = await getAuthToken();
|
||||
const response = await fetch(
|
||||
`${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks/test`,
|
||||
`https://${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks/test`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
||||
+11
-11
@@ -2,6 +2,7 @@
|
||||
import { FullWidthSidebarLayout } from "@/components/blocks/SidebarLayout";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
BellIcon,
|
||||
BookTextIcon,
|
||||
BoxIcon,
|
||||
CoinsIcon,
|
||||
@@ -94,17 +95,16 @@ export function ProjectSidebarLayout(props: {
|
||||
icon: NebulaIcon,
|
||||
tracking: tracking("nebula"),
|
||||
},
|
||||
// Commented until we solve the scrolling issue
|
||||
// {
|
||||
// href: `${layoutPath}/webhooks`,
|
||||
// label: (
|
||||
// <span className="flex items-center gap-2">
|
||||
// Webhooks <Badge>New</Badge>
|
||||
// </span>
|
||||
// ),
|
||||
// icon: BellIcon,
|
||||
// tracking: tracking("webhooks"),
|
||||
// },
|
||||
{
|
||||
href: `${layoutPath}/webhooks`,
|
||||
label: (
|
||||
<span className="flex items-center gap-2">
|
||||
Webhooks <Badge>New</Badge>
|
||||
</span>
|
||||
),
|
||||
icon: BellIcon,
|
||||
tracking: tracking("webhooks"),
|
||||
},
|
||||
]}
|
||||
footerSidebarLinks={[
|
||||
{
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ export interface WebhookPayload {
|
||||
signatures?: Array<{
|
||||
sig_hash: string;
|
||||
abi?: string;
|
||||
params: Record<string, unknown> | unknown[];
|
||||
params: Record<string, unknown>;
|
||||
}>;
|
||||
}
|
||||
>;
|
||||
@@ -59,7 +59,7 @@ export function buildTransactionWebhookPayload(
|
||||
txSignatures.push({
|
||||
sig_hash: data.sigHash,
|
||||
abi: data.sigHashAbi,
|
||||
params: [],
|
||||
params: {},
|
||||
});
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -27,7 +27,7 @@ You can manage ecosystem partners in the Permissions tab of your Ecosystem Walle
|
||||
|
||||
### Which login providers can display my Ecosystem Wallet?
|
||||
|
||||
thirdweb Connect, with WalletConnect and any wagmi- or viem-based login providers coming soon.
|
||||
thirdweb Connect, WalletConnect and any wagmi- or viem-based login providers.
|
||||
|
||||
### How can Partners integrate my Ecosystem Wallet?
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @thirdweb-dev/insight
|
||||
|
||||
## 3.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#7190](https://github.com/thirdweb-dev/js/pull/7190) [`861e623`](https://github.com/thirdweb-dev/js/commit/861e623a1b7519bcac09c0c6d975cad2c0c5be4f) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Updated to latest API
|
||||
|
||||
## 3.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/engine",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/thirdweb-dev/js.git#main"
|
||||
|
||||
@@ -7,6 +7,8 @@ import type {
|
||||
} from "@hey-api/client-fetch";
|
||||
import { client as _heyApiClient } from "./client.gen.js";
|
||||
import type {
|
||||
CreateAccountData,
|
||||
CreateAccountResponse,
|
||||
EncodeFunctionDataData,
|
||||
EncodeFunctionDataResponse,
|
||||
GetNativeBalanceData,
|
||||
@@ -15,6 +17,8 @@ import type {
|
||||
GetTransactionAnalyticsResponse,
|
||||
GetTransactionAnalyticsSummaryData,
|
||||
GetTransactionAnalyticsSummaryResponse,
|
||||
ListAccountsData,
|
||||
ListAccountsResponse,
|
||||
ReadContractData,
|
||||
ReadContractResponse,
|
||||
SearchTransactionsData,
|
||||
@@ -48,6 +52,56 @@ export type Options<
|
||||
meta?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
/**
|
||||
* List Server Wallets
|
||||
* List all engine server wallets for the current project. Returns an array of EOA addresses with their corresponding predicted smart account addresses.
|
||||
*/
|
||||
export const listAccounts = <ThrowOnError extends boolean = false>(
|
||||
options?: Options<ListAccountsData, ThrowOnError>,
|
||||
) => {
|
||||
return (options?.client ?? _heyApiClient).get<
|
||||
ListAccountsResponse,
|
||||
unknown,
|
||||
ThrowOnError
|
||||
>({
|
||||
security: [
|
||||
{
|
||||
name: "x-secret-key",
|
||||
type: "apiKey",
|
||||
},
|
||||
],
|
||||
url: "/v1/accounts",
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create Server Wallet
|
||||
* Create a new engine server wallet. This is a helper route for creating a new EOA with your KMS provider, provided as a convenient alternative to creating an EOA directly with your KMS provider. Your KMS credentials are not stored, and usage of created accounts require your KMS credentials to be sent with requests.
|
||||
*/
|
||||
export const createAccount = <ThrowOnError extends boolean = false>(
|
||||
options?: Options<CreateAccountData, ThrowOnError>,
|
||||
) => {
|
||||
return (options?.client ?? _heyApiClient).post<
|
||||
CreateAccountResponse,
|
||||
unknown,
|
||||
ThrowOnError
|
||||
>({
|
||||
security: [
|
||||
{
|
||||
name: "x-secret-key",
|
||||
type: "apiKey",
|
||||
},
|
||||
],
|
||||
url: "/v1/accounts",
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Write Contract
|
||||
* Call a write function on a contract.
|
||||
|
||||
@@ -9,7 +9,7 @@ export type TransactionsFilterValue = {
|
||||
| "smartAccountAddress"
|
||||
| "chainId";
|
||||
values: Array<string>;
|
||||
operation: "AND" | "OR";
|
||||
operation?: "AND" | "OR";
|
||||
};
|
||||
|
||||
export type TransactionsFilterNested = {
|
||||
@@ -92,6 +92,70 @@ export type AaZksyncExecutionOptions = {
|
||||
chainId: string;
|
||||
};
|
||||
|
||||
export type ListAccountsData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: "/v1/accounts";
|
||||
};
|
||||
|
||||
export type ListAccountsResponses = {
|
||||
/**
|
||||
* Accounts retrieved successfully
|
||||
*/
|
||||
200: {
|
||||
result: Array<{
|
||||
/**
|
||||
* EVM address in hex format
|
||||
*/
|
||||
address: string;
|
||||
/**
|
||||
* The predicted smart account address for use with the default thirdweb v0.7 AccountFactory
|
||||
*/
|
||||
smartAccountAddress?: string;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
|
||||
export type ListAccountsResponse =
|
||||
ListAccountsResponses[keyof ListAccountsResponses];
|
||||
|
||||
export type CreateAccountData = {
|
||||
body?: {
|
||||
label: string;
|
||||
};
|
||||
headers?: {
|
||||
/**
|
||||
* Vault Access Token used to access your EOA
|
||||
*/
|
||||
"x-vault-access-token"?: string;
|
||||
};
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: "/v1/accounts";
|
||||
};
|
||||
|
||||
export type CreateAccountResponses = {
|
||||
/**
|
||||
* Account created successfully
|
||||
*/
|
||||
201: {
|
||||
result: {
|
||||
/**
|
||||
* EVM address in hex format
|
||||
*/
|
||||
address: string;
|
||||
/**
|
||||
* The predicted smart account address for use with the default thirdweb v0.7 AccountFactory
|
||||
*/
|
||||
smartAccountAddress?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type CreateAccountResponse =
|
||||
CreateAccountResponses[keyof CreateAccountResponses];
|
||||
|
||||
export type WriteContractData = {
|
||||
body?: {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,106 @@
|
||||
# thirdweb
|
||||
|
||||
## 5.102.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#7190](https://github.com/thirdweb-dev/js/pull/7190) [`861e623`](https://github.com/thirdweb-dev/js/commit/861e623a1b7519bcac09c0c6d975cad2c0c5be4f) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Enhanced Engine functionality with server wallet management, search transactions and batch transaction support:
|
||||
|
||||
- Added `Engine.createServerWallet()` to create a new server wallet with a custom label
|
||||
|
||||
```ts
|
||||
import { Engine } from "thirdweb";
|
||||
|
||||
const serverWallet = await Engine.createServerWallet({
|
||||
client,
|
||||
label: "My Server Wallet",
|
||||
});
|
||||
console.log(serverWallet.address);
|
||||
console.log(serverWallet.smartAccountAddress);
|
||||
```
|
||||
|
||||
- Added `Engine.getServerWallets()` to list all existing server wallets
|
||||
|
||||
```ts
|
||||
import { Engine } from "thirdweb";
|
||||
|
||||
const serverWallets = await Engine.getServerWallets({
|
||||
client,
|
||||
});
|
||||
console.log(serverWallets);
|
||||
```
|
||||
|
||||
- Added `Engine.searchTransactions()` to search for transactions by various filters (id, chainId, from address, etc.)
|
||||
|
||||
```ts
|
||||
// Search by transaction IDs
|
||||
const transactions = await Engine.searchTransactions({
|
||||
client,
|
||||
filters: [
|
||||
{
|
||||
field: "id",
|
||||
values: ["1", "2", "3"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Search by chain ID and sender address
|
||||
const transactions = await Engine.searchTransactions({
|
||||
client,
|
||||
filters: [
|
||||
{
|
||||
filters: [
|
||||
{
|
||||
field: "from",
|
||||
values: ["0x1234567890123456789012345678901234567890"],
|
||||
},
|
||||
{
|
||||
field: "chainId",
|
||||
values: ["8453"],
|
||||
},
|
||||
],
|
||||
operation: "AND",
|
||||
},
|
||||
],
|
||||
pageSize: 100,
|
||||
page: 0,
|
||||
});
|
||||
```
|
||||
|
||||
- Added `serverWallet.enqueueBatchTransaction()` to enqueue multiple transactions in a single batch
|
||||
|
||||
```ts
|
||||
// Prepare multiple transactions
|
||||
const transaction1 = claimTo({
|
||||
contract,
|
||||
to: firstRecipient,
|
||||
quantity: 1n,
|
||||
});
|
||||
const transaction2 = claimTo({
|
||||
contract,
|
||||
to: secondRecipient,
|
||||
quantity: 1n,
|
||||
});
|
||||
|
||||
// Enqueue as a batch
|
||||
const { transactionId } = await serverWallet.enqueueBatchTransaction({
|
||||
transactions: [transaction1, transaction2],
|
||||
});
|
||||
|
||||
// Wait for batch completion
|
||||
const { transactionHash } = await Engine.waitForTransactionHash({
|
||||
client,
|
||||
transactionId,
|
||||
});
|
||||
```
|
||||
|
||||
- Improved server wallet transaction handling with better error reporting
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`861e623`](https://github.com/thirdweb-dev/js/commit/861e623a1b7519bcac09c0c6d975cad2c0c5be4f)]:
|
||||
- @thirdweb-dev/engine@3.0.3
|
||||
|
||||
## 5.101.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"version": "5.101.2",
|
||||
"version": "5.102.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/thirdweb-dev/js.git#main"
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { TEST_CLIENT } from "~test/test-clients.js";
|
||||
import { chains } from "./Chains.js";
|
||||
|
||||
describe("chains", () => {
|
||||
describe.runIf(process.env.TW_SECRET_KEY)("chains", () => {
|
||||
it("should fetch chains", async () => {
|
||||
// Setup
|
||||
const client = TEST_CLIENT;
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { createAccount } from "@thirdweb-dev/engine";
|
||||
import { stringify } from "viem";
|
||||
import type { ThirdwebClient } from "../client/client.js";
|
||||
import { getThirdwebBaseUrl } from "../utils/domains.js";
|
||||
import { getClientFetch } from "../utils/fetch.js";
|
||||
|
||||
export type CreateServerWalletArgs = {
|
||||
client: ThirdwebClient;
|
||||
label: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a server wallet.
|
||||
* @param params - The parameters for the server wallet.
|
||||
* @param params.client - The thirdweb client to use.
|
||||
* @param params.label - The label for the server wallet.
|
||||
* @returns The server wallet signer address and the predicted smart account address.
|
||||
* @engine
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Engine } from "thirdweb";
|
||||
*
|
||||
* const serverWallet = await Engine.createServerWallet({
|
||||
* client,
|
||||
* label: "My Server Wallet",
|
||||
* });
|
||||
* console.log(serverWallet.address);
|
||||
* console.log(serverWallet.smartAccountAddress);
|
||||
* ```
|
||||
*/
|
||||
export async function createServerWallet(params: CreateServerWalletArgs) {
|
||||
const { client, label } = params;
|
||||
const result = await createAccount({
|
||||
baseUrl: getThirdwebBaseUrl("engineCloud"),
|
||||
bodySerializer: stringify,
|
||||
fetch: getClientFetch(client),
|
||||
body: {
|
||||
label,
|
||||
},
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw new Error(
|
||||
`Error creating server wallet with label ${label}: ${stringify(
|
||||
result.error,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = result.data?.result;
|
||||
|
||||
if (!data) {
|
||||
throw new Error(`No server wallet created with label ${label}`);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import { searchTransactions } from "@thirdweb-dev/engine";
|
||||
import type { Chain } from "../chains/types.js";
|
||||
import { getCachedChain } from "../chains/utils.js";
|
||||
import type { ThirdwebClient } from "../client/client.js";
|
||||
import type { WaitForReceiptOptions } from "../transaction/actions/wait-for-tx-receipt.js";
|
||||
import { getThirdwebBaseUrl } from "../utils/domains.js";
|
||||
import type { Hex } from "../utils/encoding/hex.js";
|
||||
import { getClientFetch } from "../utils/fetch.js";
|
||||
@@ -117,71 +116,3 @@ export async function getTransactionStatus(args: {
|
||||
id: data.id,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a transaction to be submitted onchain and return the transaction hash.
|
||||
* @param args - The arguments for the transaction.
|
||||
* @param args.client - The thirdweb client to use.
|
||||
* @param args.transactionId - The id of the transaction to wait for.
|
||||
* @param args.timeoutInSeconds - The timeout in seconds.
|
||||
* @engine
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Engine } from "thirdweb";
|
||||
*
|
||||
* const { transactionHash } = await Engine.waitForTransactionHash({
|
||||
* client,
|
||||
* transactionId, // the transaction id returned from enqueueTransaction
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export async function waitForTransactionHash(args: {
|
||||
client: ThirdwebClient;
|
||||
transactionId: string;
|
||||
timeoutInSeconds?: number;
|
||||
}): Promise<WaitForReceiptOptions> {
|
||||
const startTime = Date.now();
|
||||
const TIMEOUT_IN_MS = args.timeoutInSeconds
|
||||
? args.timeoutInSeconds * 1000
|
||||
: 5 * 60 * 1000; // 5 minutes in milliseconds
|
||||
|
||||
while (Date.now() - startTime < TIMEOUT_IN_MS) {
|
||||
const executionResult = await getTransactionStatus(args);
|
||||
const status = executionResult.status;
|
||||
|
||||
switch (status) {
|
||||
case "FAILED": {
|
||||
throw new Error(
|
||||
`Transaction failed: ${executionResult.error || "Unknown error"}`,
|
||||
);
|
||||
}
|
||||
case "CONFIRMED": {
|
||||
const onchainStatus =
|
||||
executionResult && "onchainStatus" in executionResult
|
||||
? executionResult.onchainStatus
|
||||
: null;
|
||||
if (onchainStatus === "REVERTED") {
|
||||
const revertData =
|
||||
"revertData" in executionResult
|
||||
? executionResult.revertData
|
||||
: undefined;
|
||||
throw new Error(
|
||||
`Transaction reverted: ${revertData?.errorName || ""} ${revertData?.errorArgs ? stringify(revertData.errorArgs) : ""}`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
transactionHash: executionResult.transactionHash as Hex,
|
||||
client: args.client,
|
||||
chain: executionResult.chain,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
// wait for the transaction to be confirmed
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`Transaction timed out after ${TIMEOUT_IN_MS / 1000} seconds`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,19 @@ export {
|
||||
} from "./server-wallet.js";
|
||||
export {
|
||||
getTransactionStatus,
|
||||
waitForTransactionHash,
|
||||
type ExecutionResult,
|
||||
type RevertData,
|
||||
} from "./get-status.js";
|
||||
export { waitForTransactionHash } from "./wait-for-tx-hash.js";
|
||||
export {
|
||||
searchTransactions,
|
||||
type SearchTransactionsArgs,
|
||||
} from "./search-transactions.js";
|
||||
export {
|
||||
createServerWallet,
|
||||
type CreateServerWalletArgs,
|
||||
} from "./create-server-wallet.js";
|
||||
export {
|
||||
getServerWallets,
|
||||
type GetServerWalletsArgs,
|
||||
} from "./list-server-wallets.js";
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { listAccounts } from "@thirdweb-dev/engine";
|
||||
import { stringify } from "viem";
|
||||
import type { ThirdwebClient } from "../client/client.js";
|
||||
import { getThirdwebBaseUrl } from "../utils/domains.js";
|
||||
import { getClientFetch } from "../utils/fetch.js";
|
||||
|
||||
export type GetServerWalletsArgs = {
|
||||
client: ThirdwebClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* List all server wallets.
|
||||
* @param params - The parameters for the server wallet.
|
||||
* @param params.client - The thirdweb client to use.
|
||||
* @returns an array of server wallets with their signer address and predicted smart account address.
|
||||
* @engine
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Engine } from "thirdweb";
|
||||
*
|
||||
* const serverWallets = await Engine.getServerWallets({
|
||||
* client,
|
||||
* });
|
||||
* console.log(serverWallets);
|
||||
* ```
|
||||
*/
|
||||
export async function getServerWallets(params: GetServerWalletsArgs) {
|
||||
const { client } = params;
|
||||
const result = await listAccounts({
|
||||
baseUrl: getThirdwebBaseUrl("engineCloud"),
|
||||
bodySerializer: stringify,
|
||||
fetch: getClientFetch(client),
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw new Error(`Error listing server wallets: ${stringify(result.error)}`);
|
||||
}
|
||||
|
||||
const data = result.data?.result;
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No server wallets found");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import {
|
||||
type TransactionsFilterNested,
|
||||
type TransactionsFilterValue,
|
||||
searchTransactions as engineSearchTransactions,
|
||||
} from "@thirdweb-dev/engine";
|
||||
import type { ThirdwebClient } from "../client/client.js";
|
||||
import { getThirdwebBaseUrl } from "../utils/domains.js";
|
||||
import { getClientFetch } from "../utils/fetch.js";
|
||||
import { stringify } from "../utils/json.js";
|
||||
|
||||
export type SearchTransactionsArgs = {
|
||||
client: ThirdwebClient;
|
||||
filters?: (TransactionsFilterValue | TransactionsFilterNested)[];
|
||||
pageSize?: number;
|
||||
page?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search for transactions by their ids.
|
||||
* @param args - The arguments for the search.
|
||||
* @param args.client - The thirdweb client to use.
|
||||
* @param args.transactionIds - The ids of the transactions to search for.
|
||||
* @engine
|
||||
* @example
|
||||
* ## Search for transactions by their ids
|
||||
*
|
||||
* ```ts
|
||||
* import { Engine } from "thirdweb";
|
||||
*
|
||||
* const transactions = await Engine.searchTransactions({
|
||||
* client,
|
||||
* filters: [
|
||||
* {
|
||||
* field: "id",
|
||||
* values: ["1", "2", "3"],
|
||||
* },
|
||||
* ],
|
||||
* });
|
||||
* console.log(transactions);
|
||||
* ```
|
||||
*
|
||||
* ## Search for transactions by chain id
|
||||
*
|
||||
* ```ts
|
||||
* import { Engine } from "thirdweb";
|
||||
*
|
||||
* const transactions = await Engine.searchTransactions({
|
||||
* client,
|
||||
* filters: [
|
||||
* {
|
||||
* field: "chainId",
|
||||
* values: ["1", "137"],
|
||||
* },
|
||||
* ],
|
||||
* });
|
||||
* console.log(transactions);
|
||||
* ```
|
||||
*
|
||||
* ## Search for transactions by sender wallet address
|
||||
*
|
||||
* ```ts
|
||||
* import { Engine } from "thirdweb";
|
||||
*
|
||||
* const transactions = await Engine.searchTransactions({
|
||||
* client,
|
||||
* filters: [
|
||||
* {
|
||||
* field: "from",
|
||||
* values: ["0x1234567890123456789012345678901234567890"],
|
||||
* },
|
||||
* ],
|
||||
* });
|
||||
* console.log(transactions);
|
||||
* ```
|
||||
*
|
||||
* ## Combined search
|
||||
*
|
||||
* ```ts
|
||||
* import { Engine } from "thirdweb";
|
||||
*
|
||||
* const transactions = await Engine.searchTransactions({
|
||||
* client,
|
||||
* filters: [
|
||||
* {
|
||||
* filters: [
|
||||
* {
|
||||
* field: "from",
|
||||
* values: ["0x1234567890123456789012345678901234567890"],
|
||||
* },
|
||||
* {
|
||||
* field: "chainId",
|
||||
* values: ["8453"],
|
||||
* },
|
||||
* ],
|
||||
* operation: "AND",
|
||||
* },
|
||||
* ],
|
||||
* pageSize: 100,
|
||||
* page: 0,
|
||||
* });
|
||||
* console.log(transactions);
|
||||
* ```
|
||||
*/
|
||||
export async function searchTransactions(args: SearchTransactionsArgs) {
|
||||
const { client, filters, pageSize = 100, page = 1 } = args;
|
||||
const searchResult = await engineSearchTransactions({
|
||||
baseUrl: getThirdwebBaseUrl("engineCloud"),
|
||||
bodySerializer: stringify,
|
||||
fetch: getClientFetch(client),
|
||||
body: {
|
||||
filters,
|
||||
limit: pageSize,
|
||||
page,
|
||||
},
|
||||
});
|
||||
|
||||
if (searchResult.error) {
|
||||
throw new Error(
|
||||
`Error searching for transaction with filters ${stringify(filters)}: ${stringify(
|
||||
searchResult.error,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = searchResult.data?.result;
|
||||
|
||||
if (!data) {
|
||||
throw new Error(`No transactions found with filters ${stringify(filters)}`);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -47,6 +47,23 @@ describe.runIf(
|
||||
});
|
||||
});
|
||||
|
||||
it("should create a server wallet", async () => {
|
||||
const serverWallet = await Engine.createServerWallet({
|
||||
client: TEST_CLIENT,
|
||||
label: "My Server Wallet",
|
||||
});
|
||||
expect(serverWallet).toBeDefined();
|
||||
|
||||
const serverWallets = await Engine.getServerWallets({
|
||||
client: TEST_CLIENT,
|
||||
});
|
||||
expect(serverWallets).toBeDefined();
|
||||
expect(serverWallets.length).toBeGreaterThan(0);
|
||||
expect(
|
||||
serverWallets.find((s) => s.address === serverWallet.address),
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
it("should sign a message", async () => {
|
||||
const signature = await serverWallet.signMessage({
|
||||
message: "hello",
|
||||
@@ -95,6 +112,16 @@ describe.runIf(
|
||||
transactionId: result.transactionId,
|
||||
});
|
||||
expect(txHash.transactionHash).toBeDefined();
|
||||
|
||||
const res = await Engine.searchTransactions({
|
||||
client: TEST_CLIENT,
|
||||
filters: [
|
||||
{ field: "id", values: [result.transactionId], operation: "OR" },
|
||||
],
|
||||
});
|
||||
expect(res).toBeDefined();
|
||||
expect(res.transactions.length).toBe(1);
|
||||
expect(res.transactions[0]?.id).toBe(result.transactionId);
|
||||
});
|
||||
|
||||
it("should send a extension tx", async () => {
|
||||
@@ -115,6 +142,33 @@ describe.runIf(
|
||||
expect(tx).toBeDefined();
|
||||
});
|
||||
|
||||
it("should enqueue a batch of txs", async () => {
|
||||
const tokenContract = getContract({
|
||||
client: TEST_CLIENT,
|
||||
chain: baseSepolia,
|
||||
address: "0x87C52295891f208459F334975a3beE198fE75244",
|
||||
});
|
||||
const claimTx1 = mintTo({
|
||||
contract: tokenContract,
|
||||
to: serverWallet.address,
|
||||
amount: "0.001",
|
||||
});
|
||||
const claimTx2 = mintTo({
|
||||
contract: tokenContract,
|
||||
to: serverWallet.address,
|
||||
amount: "0.002",
|
||||
});
|
||||
const tx = await serverWallet.enqueueBatchTransaction({
|
||||
transactions: [claimTx1, claimTx2],
|
||||
});
|
||||
expect(tx.transactionId).toBeDefined();
|
||||
const txHash = await Engine.waitForTransactionHash({
|
||||
client: TEST_CLIENT,
|
||||
transactionId: tx.transactionId,
|
||||
});
|
||||
expect(txHash.transactionHash).toBeDefined();
|
||||
});
|
||||
|
||||
it("should get revert reason", async () => {
|
||||
const nftContract = getContract({
|
||||
client: TEST_CLIENT,
|
||||
|
||||
@@ -19,7 +19,7 @@ import type {
|
||||
Account,
|
||||
SendTransactionOption,
|
||||
} from "../wallets/interfaces/wallet.js";
|
||||
import { waitForTransactionHash } from "./get-status.js";
|
||||
import { waitForTransactionHash } from "./wait-for-tx-hash.js";
|
||||
|
||||
/**
|
||||
* Options for creating an server wallet.
|
||||
@@ -54,6 +54,9 @@ export type ServerWallet = Account & {
|
||||
transaction: PreparedTransaction;
|
||||
simulate?: boolean;
|
||||
}) => Promise<{ transactionId: string }>;
|
||||
enqueueBatchTransaction: (args: {
|
||||
transactions: PreparedTransaction[];
|
||||
}) => Promise<{ transactionId: string }>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -102,6 +105,37 @@ export type ServerWallet = Account & {
|
||||
* console.log("Transaction sent:", transactionHash);
|
||||
* ```
|
||||
*
|
||||
* ### Sending a batch of transactions
|
||||
* ```ts
|
||||
* // prepare the transactions
|
||||
* const transaction1 = claimTo({
|
||||
* contract,
|
||||
* to: firstRecipient,
|
||||
* quantity: 1n,
|
||||
* });
|
||||
* const transaction2 = claimTo({
|
||||
* contract,
|
||||
* to: secondRecipient,
|
||||
* quantity: 1n,
|
||||
* });
|
||||
*
|
||||
*
|
||||
* // enqueue the transactions in a batch
|
||||
* const { transactionId } = await myServerWallet.enqueueBatchTransaction({
|
||||
* transactions: [transaction1, transaction2],
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ### Polling for the batch of transactions to be submitted onchain
|
||||
* ```ts
|
||||
* // optionally poll for the transaction to be submitted onchain
|
||||
* const { transactionHash } = await Engine.waitForTransactionHash({
|
||||
* client,
|
||||
* transactionId,
|
||||
* });
|
||||
* console.log("Transaction sent:", transactionHash);
|
||||
* ```
|
||||
*
|
||||
* ### Getting the execution status of a transaction
|
||||
* ```ts
|
||||
* const executionResult = await Engine.getTransactionStatus({
|
||||
@@ -130,16 +164,30 @@ export function serverWallet(options: ServerWalletOptions): ServerWallet {
|
||||
};
|
||||
};
|
||||
|
||||
const enqueueTx = async (transaction: SendTransactionOption) => {
|
||||
const enqueueTx = async (transaction: SendTransactionOption[]) => {
|
||||
if (transaction.length === 0) {
|
||||
throw new Error("No transactions to enqueue");
|
||||
}
|
||||
const firstTransaction = transaction[0];
|
||||
if (!firstTransaction) {
|
||||
throw new Error("No transactions to enqueue");
|
||||
}
|
||||
const chainId = firstTransaction.chainId;
|
||||
// Validate all transactions are on the same chain
|
||||
for (let i = 1; i < transaction.length; i++) {
|
||||
if (transaction[i]?.chainId !== chainId) {
|
||||
throw new Error(
|
||||
`All transactions in batch must be on the same chain. Expected ${chainId}, got ${transaction[i]?.chainId} at index ${i}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const body = {
|
||||
executionOptions: getExecutionOptions(transaction.chainId),
|
||||
params: [
|
||||
{
|
||||
to: transaction.to ?? undefined,
|
||||
data: transaction.data,
|
||||
value: transaction.value?.toString(),
|
||||
},
|
||||
],
|
||||
executionOptions: getExecutionOptions(chainId),
|
||||
params: transaction.map((t) => ({
|
||||
to: t.to ?? undefined,
|
||||
data: t.data,
|
||||
value: t.value?.toString(),
|
||||
})),
|
||||
};
|
||||
|
||||
const result = await sendTransaction({
|
||||
@@ -158,11 +206,7 @@ export function serverWallet(options: ServerWalletOptions): ServerWallet {
|
||||
if (!data) {
|
||||
throw new Error("No data returned from engine");
|
||||
}
|
||||
const transactionId = data.transactions?.[0]?.id;
|
||||
if (!transactionId) {
|
||||
throw new Error("No transactionId returned from engine");
|
||||
}
|
||||
return transactionId;
|
||||
return data.transactions.map((t) => t.id);
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -193,11 +237,54 @@ export function serverWallet(options: ServerWalletOptions): ServerWallet {
|
||||
value: value ?? undefined,
|
||||
};
|
||||
}
|
||||
const transactionId = await enqueueTx(serializedTransaction);
|
||||
const transactionIds = await enqueueTx([serializedTransaction]);
|
||||
const transactionId = transactionIds[0];
|
||||
if (!transactionId) {
|
||||
throw new Error("No transactionId returned from engine");
|
||||
}
|
||||
return { transactionId };
|
||||
},
|
||||
enqueueBatchTransaction: async (args: {
|
||||
transactions: PreparedTransaction[];
|
||||
}) => {
|
||||
const serializedTransactions: SendTransactionOption[] = [];
|
||||
for (const transaction of args.transactions) {
|
||||
const [to, data, value] = await Promise.all([
|
||||
transaction.to ? resolvePromisedValue(transaction.to) : null,
|
||||
encode(transaction),
|
||||
transaction.value ? resolvePromisedValue(transaction.value) : null,
|
||||
]);
|
||||
serializedTransactions.push({
|
||||
chainId: transaction.chain.id,
|
||||
data,
|
||||
to: to ?? undefined,
|
||||
value: value ?? undefined,
|
||||
});
|
||||
}
|
||||
const transactionIds = await enqueueTx(serializedTransactions);
|
||||
const transactionId = transactionIds[0];
|
||||
if (!transactionId) {
|
||||
throw new Error("No transactionId returned from engine");
|
||||
}
|
||||
return { transactionId };
|
||||
},
|
||||
sendTransaction: async (transaction: SendTransactionOption) => {
|
||||
const transactionId = await enqueueTx(transaction);
|
||||
const transactionIds = await enqueueTx([transaction]);
|
||||
const transactionId = transactionIds[0];
|
||||
if (!transactionId) {
|
||||
throw new Error("No transactionId returned from engine");
|
||||
}
|
||||
return waitForTransactionHash({
|
||||
client,
|
||||
transactionId,
|
||||
});
|
||||
},
|
||||
sendBatchTransaction: async (transactions: SendTransactionOption[]) => {
|
||||
const transactionIds = await enqueueTx(transactions);
|
||||
const transactionId = transactionIds[0];
|
||||
if (!transactionId) {
|
||||
throw new Error("No transactionId returned from engine");
|
||||
}
|
||||
return waitForTransactionHash({
|
||||
client,
|
||||
transactionId,
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { stringify } from "viem";
|
||||
|
||||
import type { Hex } from "../utils/encoding/hex.js";
|
||||
|
||||
import type { ThirdwebClient } from "../client/client.js";
|
||||
import type { WaitForReceiptOptions } from "../transaction/actions/wait-for-tx-receipt.js";
|
||||
import { getTransactionStatus } from "./get-status.js";
|
||||
|
||||
/**
|
||||
* Wait for a transaction to be submitted onchain and return the transaction hash.
|
||||
* @param args - The arguments for the transaction.
|
||||
* @param args.client - The thirdweb client to use.
|
||||
* @param args.transactionId - The id of the transaction to wait for.
|
||||
* @param args.timeoutInSeconds - The timeout in seconds.
|
||||
* @engine
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Engine } from "thirdweb";
|
||||
*
|
||||
* const { transactionHash } = await Engine.waitForTransactionHash({
|
||||
* client,
|
||||
* transactionId, // the transaction id returned from enqueueTransaction
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export async function waitForTransactionHash(args: {
|
||||
client: ThirdwebClient;
|
||||
transactionId: string;
|
||||
timeoutInSeconds?: number;
|
||||
}): Promise<WaitForReceiptOptions> {
|
||||
const startTime = Date.now();
|
||||
const TIMEOUT_IN_MS = args.timeoutInSeconds
|
||||
? args.timeoutInSeconds * 1000
|
||||
: 5 * 60 * 1000; // 5 minutes in milliseconds
|
||||
|
||||
while (Date.now() - startTime < TIMEOUT_IN_MS) {
|
||||
const executionResult = await getTransactionStatus(args);
|
||||
const status = executionResult.status;
|
||||
|
||||
switch (status) {
|
||||
case "FAILED": {
|
||||
throw new Error(
|
||||
`Transaction failed: ${executionResult.error || "Unknown error"}`,
|
||||
);
|
||||
}
|
||||
case "CONFIRMED": {
|
||||
const onchainStatus =
|
||||
executionResult && "onchainStatus" in executionResult
|
||||
? executionResult.onchainStatus
|
||||
: null;
|
||||
if (onchainStatus === "REVERTED") {
|
||||
const revertData =
|
||||
"revertData" in executionResult
|
||||
? executionResult.revertData
|
||||
: undefined;
|
||||
throw new Error(
|
||||
`Transaction reverted: ${revertData?.errorName || "unknown error"} ${revertData?.errorArgs ? stringify(revertData.errorArgs) : ""} - ${executionResult.transactionHash ? executionResult.transactionHash : ""}`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
transactionHash: executionResult.transactionHash as Hex,
|
||||
client: args.client,
|
||||
chain: executionResult.chain,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
// wait for the transaction to be confirmed
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`Transaction timed out after ${TIMEOUT_IN_MS / 1000} seconds`,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { DROP1155_CONTRACT } from "~test/test-contracts.js";
|
||||
import { getOwnedNFTs } from "./getOwnedNFTs.js";
|
||||
|
||||
describe.runIf(process.env.TW_SECRET_KEY)("erc1155.getOwnedNFTs", () => {
|
||||
it("with indexer", async () => {
|
||||
const nfts = await getOwnedNFTs({
|
||||
contract: DROP1155_CONTRACT,
|
||||
address: "0x00d4da27dedce60f859471d8f595fdb4ae861557",
|
||||
});
|
||||
expect(nfts.length).toBe(3);
|
||||
expect(nfts.find((nft) => nft.id === 4n)?.quantityOwned).toBe(411n);
|
||||
});
|
||||
|
||||
it("without indexer", async () => {
|
||||
const nfts = await getOwnedNFTs({
|
||||
contract: DROP1155_CONTRACT,
|
||||
address: "0x00d4da27dedce60f859471d8f595fdb4ae861557",
|
||||
useIndexer: false,
|
||||
});
|
||||
expect(nfts.length).toBe(3);
|
||||
expect(nfts.find((nft) => nft.id === 4n)?.quantityOwned).toBe(411n);
|
||||
});
|
||||
});
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import { type ThirdwebContract, getContract } from "../../contract/contract.js";
|
||||
import { sendAndConfirmTransaction } from "../../transaction/actions/send-and-confirm-transaction.js";
|
||||
import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value.js";
|
||||
import { toEther } from "../../utils/units.js";
|
||||
import { toEther, toWei } from "../../utils/units.js";
|
||||
import { name } from "../common/read/name.js";
|
||||
import { deployERC20Contract } from "../prebuilts/deploy-erc20.js";
|
||||
import { canClaim } from "./drops/read/canClaim.js";
|
||||
@@ -20,6 +20,8 @@ import { claimTo } from "./drops/write/claimTo.js";
|
||||
import { resetClaimEligibility } from "./drops/write/resetClaimEligibility.js";
|
||||
import { setClaimConditions } from "./drops/write/setClaimConditions.js";
|
||||
import { getBalance } from "./read/getBalance.js";
|
||||
import { getApprovalForTransaction } from "./write/getApprovalForTransaction.js";
|
||||
import { mintTo } from "./write/mintTo.js";
|
||||
|
||||
describe.runIf(process.env.TW_SECRET_KEY)(
|
||||
"DropERC20",
|
||||
@@ -135,6 +137,94 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
||||
).toBe("2");
|
||||
});
|
||||
|
||||
it("should allow to claim tokens with erc20 price", async () => {
|
||||
expect(
|
||||
(await getBalance({ contract, address: TEST_ACCOUNT_C.address }))
|
||||
.displayValue,
|
||||
).toBe("2");
|
||||
const erc20ContractAddres = await deployERC20Contract({
|
||||
account: TEST_ACCOUNT_A,
|
||||
chain: ANVIL_CHAIN,
|
||||
client: TEST_CLIENT,
|
||||
type: "TokenERC20",
|
||||
params: {
|
||||
name: "Test DropERC20",
|
||||
},
|
||||
});
|
||||
const erc20Contract = getContract({
|
||||
address: erc20ContractAddres,
|
||||
chain: ANVIL_CHAIN,
|
||||
client: TEST_CLIENT,
|
||||
});
|
||||
const mintToTx = mintTo({
|
||||
contract: erc20Contract,
|
||||
to: TEST_ACCOUNT_C.address,
|
||||
amount: "0.02",
|
||||
});
|
||||
await sendAndConfirmTransaction({
|
||||
transaction: mintToTx,
|
||||
account: TEST_ACCOUNT_A,
|
||||
});
|
||||
expect(
|
||||
(
|
||||
await getBalance({
|
||||
contract: erc20Contract,
|
||||
address: TEST_ACCOUNT_C.address,
|
||||
})
|
||||
).displayValue,
|
||||
).toBe("0.02");
|
||||
// set cc with price
|
||||
await sendAndConfirmTransaction({
|
||||
transaction: setClaimConditions({
|
||||
contract,
|
||||
phases: [
|
||||
{
|
||||
price: "0.01",
|
||||
currencyAddress: erc20ContractAddres,
|
||||
},
|
||||
],
|
||||
}),
|
||||
account: TEST_ACCOUNT_A,
|
||||
});
|
||||
const claimTx = claimTo({
|
||||
contract,
|
||||
to: TEST_ACCOUNT_C.address,
|
||||
quantity: "2",
|
||||
});
|
||||
// assert value is set correctly
|
||||
const value = await resolvePromisedValue(claimTx.erc20Value);
|
||||
expect(value).toBeDefined();
|
||||
if (!value) throw new Error("value is undefined");
|
||||
expect(value.amountWei).toBe(toWei("0.02"));
|
||||
const approve = await getApprovalForTransaction({
|
||||
transaction: claimTx,
|
||||
account: TEST_ACCOUNT_C,
|
||||
});
|
||||
if (approve) {
|
||||
await sendAndConfirmTransaction({
|
||||
transaction: approve,
|
||||
account: TEST_ACCOUNT_C,
|
||||
});
|
||||
}
|
||||
// claim
|
||||
await sendAndConfirmTransaction({
|
||||
transaction: claimTx,
|
||||
account: TEST_ACCOUNT_C,
|
||||
});
|
||||
expect(
|
||||
(await getBalance({ contract, address: TEST_ACCOUNT_C.address }))
|
||||
.displayValue,
|
||||
).toBe("4");
|
||||
expect(
|
||||
(
|
||||
await getBalance({
|
||||
contract: erc20Contract,
|
||||
address: TEST_ACCOUNT_C.address,
|
||||
})
|
||||
).displayValue,
|
||||
).toBe("0");
|
||||
});
|
||||
|
||||
describe("Allowlists", () => {
|
||||
it("should allow to claim tokens with an allowlist", async () => {
|
||||
await sendAndConfirmTransaction({
|
||||
|
||||
@@ -319,7 +319,12 @@ async function transformNFTModel(
|
||||
});
|
||||
}
|
||||
|
||||
return parsedNft;
|
||||
return {
|
||||
...parsedNft,
|
||||
...(contract?.type === "erc1155"
|
||||
? { quantityOwned: balance ? BigInt(balance) : undefined }
|
||||
: {}),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ const THIRDWEB_DOMAINS = [
|
||||
// dev domains
|
||||
".thirdweb.dev",
|
||||
".thirdweb-dev.com",
|
||||
".thirdwebstorage-dev.com",
|
||||
] as const;
|
||||
|
||||
export const IS_THIRDWEB_URL_CACHE = new LruMap<boolean>(4096);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @thirdweb-dev/wagmi-adapter
|
||||
|
||||
## 0.2.86
|
||||
|
||||
## 0.2.85
|
||||
|
||||
## 0.2.84
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/wagmi-adapter",
|
||||
"version": "0.2.85",
|
||||
"version": "0.2.86",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/thirdweb-dev/js.git#main"
|
||||
|
||||
Reference in New Issue
Block a user