Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions packages/thirdweb/src/v2/client/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { createThirdwebClient } from "../../client/client.js";

/**
* Initializes a new Thirdweb client using your client ID or secret key.
* @param options - Options for initializing the client
* @param options.clientId - The client ID for your Thirdweb project
* @param options.secretKey - The secret key for your Thirdweb project
* @returns A new Thirdweb client instance to be used with other thirdweb functions
*
* @example
* ## Initialize client-side
* ```typescript
* import { Client } from "thirdweb/v2";
*
* const thirdwebClient = Client.init({
* clientId: "YOUR_CLIENT_ID",
* });
* ```
*
* ## Initialize server-side
* ```typescript
* import { createThirdwebClient } from "thirdweb/v2";
*
* const thirdwebClient = createThirdwebClient({
Copy link
Member Author

Choose a reason for hiding this comment

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

reminder to fix this example

* secretKey: "YOUR_CLIENT_ID",
* });
* ```
*/
export function init(options: init.Options) {
return createThirdwebClient(options);
}

export declare namespace init {
type Options = {
clientId: string;
secretKey?: string;
} | {
clientId?: string;
secretKey: string;
};
}
70 changes: 70 additions & 0 deletions packages/thirdweb/src/v2/transactions/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { getTransactionById } from "@thirdweb-dev/api";
import type { ThirdwebClient } from "../../client/client.js";
import { getThirdwebBaseUrl } from "../../utils/domains.js";
import { getClientFetch } from "../../utils/fetch.js";
import type { Transaction } from "./types.js";
import type { Address } from "../../utils/address.js";
import type { Hex } from "../../utils/encoding/hex.js";

/**
* Retrieves a transaction by ID.
*
* @param options - Options including the transaction ID
* @param options.transactionId - The ID of the transaction to retrieve
* @returns Promise that resolves to the transaction
* @example
*
* ## Get a transaction by ID
* ```typescript
* import { Client, Transactions } from "thirdweb/v2";
Copy link
Member Author

Choose a reason for hiding this comment

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

thinking more and more that we can get away with just import from "thirdweb" with the new namespaces. Just so much simpler. over time we can deprecate/move things out of the main export

*
* const client = Client.init({
* clientId: "YOUR_CLIENT_ID",
* });
*
* const transaction = await Transactions.get({
* transactionId: "...",
* client,
* });
* ```
*/
export async function get(options: get.Options): Promise<get.Result> {
const result = await getTransactionById({
baseUrl: getThirdwebBaseUrl("api"),
fetch: getClientFetch(options.client),
path: {
transactionId: options.transactionId,
}
});

if (result.error) {
throw new Error(
`Failed to get transaction: ${result.response.status} - ${result.error}`,
);
}
const transaction = result.data?.result;
if (!transaction) {
throw new Error("Failed to get transaction: no transaction");
}

return {
id: transaction.id,
transactionHash: transaction.transactionHash as Hex,
from: transaction.from as Address,
chainId: transaction.chainId,
createdAt: transaction.createdAt,
confirmedAt: transaction.confirmedAt,
confirmedAtBlockNumber: transaction.confirmedAtBlockNumber,
cancelledAt: transaction.cancelledAt,
errorMessage: transaction.errorMessage,
};
}

export declare namespace get {
type Options = {
transactionId: string;
client: ThirdwebClient;
};

type Result = Transaction;
}
77 changes: 77 additions & 0 deletions packages/thirdweb/src/v2/transactions/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { listTransactions } from "@thirdweb-dev/api";
import type { Transaction } from "./types.js";
import { getThirdwebBaseUrl } from "../../utils/domains.js";
import { getClientFetch } from "../../utils/fetch.js";
import type { Address } from "../../utils/address.js";
import type { ThirdwebClient } from "../../client/client.js";
import type { Hex } from "../../utils/encoding/hex.js";


/**
* Retrieves a paginated list of transactions associated with the provided sender address.
*
* @param options - Options including the sender address, chain ID, and pagination
* @param options.client - The Thirdweb client instance
* @param options.sender - The sender address to retrieve transactions for
* @param options.limit - The maximum number of transactions to retrieve (default: 20, max: 100)
* @param options.page - The page number for pagination (default: 1, min: 1)
* @returns Promise that resolves to the transactions
* @example
*
* ## List transactions
* ```typescript
* import { Client, Transactions } from "thirdweb/v2";
*
* const client = Client.init({
* clientId: "YOUR_CLIENT_ID",
* });
*
* const transactions = await Transactions.list({
* sender: "0x...",
* client,
* });
* ```
*/
export async function list(options: list.Options): Promise<list.Result> {
const result = await listTransactions({
baseUrl: getThirdwebBaseUrl("api"),
fetch: getClientFetch(options.client),
query: {
from: options.sender,
limit: options.limit,
page: options.page,
}
});

if (result.error) {
throw new Error(
`Failed to list transactions: ${result.response.status} - ${result.error}`,
);
}
const transactions = result.data?.result?.transactions;
if (!transactions) {
throw new Error("Failed to list transactions: no transactions");
}
return transactions.map((transaction) => ({
id: transaction.id,
transactionHash: transaction.transactionHash as Hex,
from: transaction.from as Address,
chainId: transaction.chainId,
createdAt: transaction.createdAt,
confirmedAt: transaction.confirmedAt,
confirmedAtBlockNumber: transaction.confirmedAtBlockNumber,
cancelledAt: transaction.cancelledAt,
errorMessage: transaction.errorMessage,
Copy link
Member Author

Choose a reason for hiding this comment

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

thinking we probably need a 'status' top level here, in the API too

}));
}

export declare namespace list {
Copy link
Member Author

Choose a reason for hiding this comment

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

Uppercase?

type Options = {
client: ThirdwebClient;
sender: Address;
limit?: number;
page?: number;
};

type Result = Array<Transaction>;
}
141 changes: 141 additions & 0 deletions packages/thirdweb/src/v2/transactions/send.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { sendTransactions } from "@thirdweb-dev/api";
import type { UserWallet } from "../wallets/types.js";
import type { TransactionRequest } from "./types.js";
import { getThirdwebBaseUrl } from "../../utils/domains.js";
import { getClientFetch } from "../../utils/fetch.js";

/**
* Sends a series of transactions for execution. Each transaction can be a human-readable contract call, native transfer, or encoded transaction.
*
* @param options - Options including the wallet, chain id, and transactions
* @param options.wallet - The wallet to use for signing transactions
* @param options.chainId - The chain ID to use for the transactions
* @param options.transactions - An array of transactions to send
* @returns The sent transaction IDs
* @example
*
* ## Call a contract
* ```typescript
* import { Client, Transactions, Wallets } from "thirdweb/v2";
*
* const userWallet = await Wallets.loginWithOauth({
* client: thirdwebClient,
* provider: "google",
* });
*
* const transactionIds = await Transactions.send({
* wallet: userWallet,
* chainId: 1,
* transactions: [
* {
* contractAddress: "0x...",
* method: "function transfer(address,uint256)",
* params: ["0x...", 100n],
* }
* ],
* });
* ```
*
* ## Send native currency
* ```typescript
* import { Client, Transactions, Wallets } from "thirdweb/v2";
*
* const userWallet = await Wallets.loginAsGuest({
* client: thirdwebClient,
* });
*
* const transactionIds = await Transactions.send({
* wallet: userWallet,
* chainId: 1,
* transactions: [
* {
* to: "0x...",
* value: 100n,
* }
* ],
* });
* ```
*
* ## Send an encoded transaction
* ```typescript
* import { Client, Transactions, Wallets } from "thirdweb/v2";
*
* const thirdwebClient = Client.init({
* clientId: "YOUR_CLIENT_ID",
Copy link
Member Author

Choose a reason for hiding this comment

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

you're not initializing the client in the other 2 examples

* });
*
* const userWallet = await Wallets.loginWithOauth({
* client: thirdwebClient,
* provider: "github",
* });
*
* const transactionIds = await Transactions.send({
* wallet: userWallet,
* chainId: 1,
* transactions: [
* {
* to: "0x...",
* data: "0x...",
* }
* ],
* });
*/
export async function send(options: send.Options): Promise<Array<string>> {
const transactions = options.transactions.map((transaction) => {
if ("contractAddress" in transaction) {
return {
contractAddress: transaction.contractAddress,
method: transaction.method,
params: transaction.params,
value: transaction.value?.toString() ?? "0",
type: "contract-call" as const,
} as const;
} else if ("data" in transaction) {
return {
to: transaction.to,
data: transaction.data,
value: transaction.value?.toString() ?? "0",
type: "encoded" as const,
} as const;
} else {
return {
to: transaction.to,
value: transaction.value?.toString() ?? "0",
type: "native-transfer" as const,
} as const;
}
});

const result = await sendTransactions({
baseUrl: getThirdwebBaseUrl("api"),
fetch: getClientFetch(options.wallet.client),
headers: {
Authorization: `Bearer ${options.wallet.authToken}`,
},
body: {
chainId: options.chainId,
from: options.wallet.address,
transactions
}
});

if (result.error) {
throw new Error(
`Failed to send transactions: ${result.response.status} - ${result.error}`,
);
}
const transactionIds = result.data?.result?.transactionIds;
if (!transactionIds) {
throw new Error("Failed to send transactions: no transaction ids");
}
return transactionIds;
Copy link
Member Author

Choose a reason for hiding this comment

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

tempted to change the API to be a single transaction id, feels simpler

};

export namespace send {
export type Options = {
wallet: UserWallet;
chainId: number;
transactions: Array<TransactionRequest>;
};
}

29 changes: 29 additions & 0 deletions packages/thirdweb/src/v2/transactions/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Address } from "../../utils/address.js";
import type { Hex } from "../../utils/encoding/hex.js";

export type TransactionRequest = {
contractAddress: Address;
method: `function ${string}`;
params: Array<(string | bigint | number | boolean | object)>;
value?: bigint;
} | {
to: Address;
data: Hex;
value?: bigint;
} | {
to: Address;
value: bigint;
}

export type Transaction = {
id: string;
transactionHash: Hex;
from: Address;
chainId: string;
createdAt: string;
confirmedAt?: string;
confirmedAtBlockNumber?: string;
cancelledAt?: string;
errorMessage: string | null;
}

1 change: 1 addition & 0 deletions packages/thirdweb/src/v2/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type EthereumAddress = `0x${string}`; // Joaquim do you think we should use the types from the existing SDK or create entirely new ones so we don't cross over at all?
Copy link
Member Author

Choose a reason for hiding this comment

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

i think if we keep things namespaced we can get away with new types.

but tbh for addresses i still fill like 0x{string} is overkill. super annoying when you get back nested structs from api that you're just trying to feed back into a typed function. I would just use string, and validate at runtime

8 changes: 4 additions & 4 deletions packages/thirdweb/src/v2/wallets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export {
loginWithOauthRedirect,
} from "./oauth.js";
export {
loginWithCode as verifyCode,
type SendCodeOptions,
sendCode,
type VerifyCodeOptions,
verifyLoginCode,
Copy link
Member Author

Choose a reason for hiding this comment

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

not loginWithCode?

type SendLoginCodeOptions,
sendLoginCode,
Copy link
Member Author

Choose a reason for hiding this comment

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

we can do sendVerificationCode if you think sendCode is confusing

type VerifyLoginCodeOptions,
} from "./otp.js";
export { type LoginWithPasskeyOptions, loginWithPasskey } from "./passkey.js";
export { type LoginWithWalletOptions, loginWithWallet } from "./siwe.js";
Expand Down
4 changes: 2 additions & 2 deletions packages/thirdweb/src/v2/wallets/login.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ describe.skip("login tests", () => {
clientId: "...",
});

await Wallets.sendCode({
await Wallets.sendLoginCode({
client,
type: "phone",
phoneNumber: "+1111111111",
});

const wallet = await Wallets.verifyCode({
const wallet = await Wallets.verifyLoginCode({
client,
type: "phone",
phoneNumber: "+1111111111",
Expand Down
Loading
Loading