alchemy 0.69.1 → 0.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/alchemy.js +3883 -3906
- package/bin/alchemy.ts +2 -0
- package/bin/commands/telemetry.ts +33 -0
- package/bin/services/execute-alchemy.ts +7 -0
- package/bin/trpc.ts +4 -9
- package/lib/alchemy.d.ts +7 -3
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +3 -15
- package/lib/alchemy.js.map +1 -1
- package/lib/apply.js +24 -9
- package/lib/apply.js.map +1 -1
- package/lib/aws/utils.d.ts +2 -1
- package/lib/aws/utils.d.ts.map +1 -1
- package/lib/aws/utils.js.map +1 -1
- package/lib/clickhouse/api/client.gen.d.ts +13 -0
- package/lib/clickhouse/api/client.gen.d.ts.map +1 -0
- package/lib/clickhouse/api/client.gen.js +7 -0
- package/lib/clickhouse/api/client.gen.js.map +1 -0
- package/lib/clickhouse/api/sdk.gen.d.ts +322 -0
- package/lib/clickhouse/api/sdk.gen.d.ts.map +1 -0
- package/lib/clickhouse/api/sdk.gen.js +683 -0
- package/lib/clickhouse/api/sdk.gen.js.map +1 -0
- package/lib/clickhouse/api/types.gen.d.ts +5030 -0
- package/lib/clickhouse/api/types.gen.d.ts.map +1 -0
- package/lib/clickhouse/api/types.gen.js +2 -0
- package/lib/clickhouse/api/types.gen.js.map +1 -0
- package/lib/clickhouse/api-key.d.ts +29 -0
- package/lib/clickhouse/api-key.d.ts.map +1 -0
- package/lib/clickhouse/api-key.js +83 -0
- package/lib/clickhouse/api-key.js.map +1 -0
- package/lib/clickhouse/api.d.ts +36 -0
- package/lib/clickhouse/api.d.ts.map +1 -0
- package/lib/clickhouse/api.js +48 -0
- package/lib/clickhouse/api.js.map +1 -0
- package/lib/clickhouse/index.d.ts +4 -0
- package/lib/clickhouse/index.d.ts.map +1 -0
- package/lib/clickhouse/index.js +4 -0
- package/lib/clickhouse/index.js.map +1 -0
- package/lib/clickhouse/organization.d.ts +16 -0
- package/lib/clickhouse/organization.d.ts.map +1 -0
- package/lib/clickhouse/organization.js +21 -0
- package/lib/clickhouse/organization.js.map +1 -0
- package/lib/clickhouse/service.d.ts +275 -0
- package/lib/clickhouse/service.d.ts.map +1 -0
- package/lib/clickhouse/service.js +274 -0
- package/lib/clickhouse/service.js.map +1 -0
- package/lib/cloudflare/account-api-token.d.ts.map +1 -1
- package/lib/cloudflare/account-api-token.js +1 -1
- package/lib/cloudflare/account-api-token.js.map +1 -1
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +2 -0
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts +13 -0
- package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -0
- package/lib/cloudflare/bun-spa/bun-spa.js +95 -0
- package/lib/cloudflare/bun-spa/bun-spa.js.map +1 -0
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/container.d.ts +11 -1
- package/lib/cloudflare/container.d.ts.map +1 -1
- package/lib/cloudflare/container.js.map +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +33 -6
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +2 -0
- package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +36 -49
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
- package/lib/cloudflare/permission-groups.d.ts +2 -3
- package/lib/cloudflare/permission-groups.d.ts.map +1 -1
- package/lib/cloudflare/permission-groups.js +10 -15
- package/lib/cloudflare/permission-groups.js.map +1 -1
- package/lib/cloudflare/redwood/redwood.d.ts +1 -1
- package/lib/cloudflare/redwood/redwood.js +1 -1
- package/lib/cloudflare/util/filter-env-bindings.d.ts +62 -0
- package/lib/cloudflare/util/filter-env-bindings.d.ts.map +1 -0
- package/lib/cloudflare/util/filter-env-bindings.js +81 -0
- package/lib/cloudflare/util/filter-env-bindings.js.map +1 -0
- package/lib/cloudflare/website.d.ts +2 -2
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +6 -18
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts +1 -0
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +1 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +11 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +8 -0
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +3 -4
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/coinbase/client.d.ts +54 -0
- package/lib/coinbase/client.d.ts.map +1 -0
- package/lib/coinbase/client.js +89 -0
- package/lib/coinbase/client.js.map +1 -0
- package/lib/coinbase/evm-account.d.ts +118 -0
- package/lib/coinbase/evm-account.d.ts.map +1 -0
- package/lib/coinbase/evm-account.js +198 -0
- package/lib/coinbase/evm-account.js.map +1 -0
- package/lib/coinbase/evm-smart-account.d.ts +122 -0
- package/lib/coinbase/evm-smart-account.d.ts.map +1 -0
- package/lib/coinbase/evm-smart-account.js +213 -0
- package/lib/coinbase/evm-smart-account.js.map +1 -0
- package/lib/coinbase/faucet.d.ts +11 -0
- package/lib/coinbase/faucet.d.ts.map +1 -0
- package/lib/coinbase/faucet.js +146 -0
- package/lib/coinbase/faucet.js.map +1 -0
- package/lib/coinbase/index.d.ts +4 -0
- package/lib/coinbase/index.d.ts.map +1 -0
- package/lib/coinbase/index.js +4 -0
- package/lib/coinbase/index.js.map +1 -0
- package/lib/coinbase/types.d.ts +28 -0
- package/lib/coinbase/types.d.ts.map +1 -0
- package/lib/coinbase/types.js +4 -0
- package/lib/{util/telemetry → coinbase}/types.js.map +1 -1
- package/lib/coinbase/utils.d.ts +13 -0
- package/lib/coinbase/utils.d.ts.map +1 -0
- package/lib/coinbase/utils.js +17 -0
- package/lib/coinbase/utils.js.map +1 -0
- package/lib/neon/api/types.gen.d.ts +7 -2
- package/lib/neon/api/types.gen.d.ts.map +1 -1
- package/lib/planetscale/api/types.gen.d.ts +20 -4
- package/lib/planetscale/api/types.gen.d.ts.map +1 -1
- package/lib/planetscale/api.d.ts.map +1 -1
- package/lib/planetscale/api.js +1 -0
- package/lib/planetscale/api.js.map +1 -1
- package/lib/planetscale/password.d.ts.map +1 -1
- package/lib/planetscale/password.js +1 -13
- package/lib/planetscale/password.js.map +1 -1
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js +7 -3
- package/lib/resource.js.map +1 -1
- package/lib/scope.d.ts +9 -9
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +18 -37
- package/lib/scope.js.map +1 -1
- package/lib/state/instrumented-state-store.d.ts +1 -3
- package/lib/state/instrumented-state-store.d.ts.map +1 -1
- package/lib/state/instrumented-state-store.js +19 -21
- package/lib/state/instrumented-state-store.js.map +1 -1
- package/lib/test/bun.d.ts.map +1 -1
- package/lib/test/bun.js +1 -2
- package/lib/test/bun.js.map +1 -1
- package/lib/test/vitest.d.ts.map +1 -1
- package/lib/test/vitest.js +1 -2
- package/lib/test/vitest.js.map +1 -1
- package/lib/util/diff.d.ts +5 -0
- package/lib/util/diff.d.ts.map +1 -0
- package/lib/util/diff.js +14 -0
- package/lib/util/diff.js.map +1 -0
- package/lib/util/idempotent-spawn.js +1 -1
- package/lib/util/idempotent-spawn.js.map +1 -1
- package/lib/util/telemetry.d.ts +53 -0
- package/lib/util/telemetry.d.ts.map +1 -0
- package/lib/util/telemetry.js +215 -0
- package/lib/util/telemetry.js.map +1 -0
- package/package.json +19 -1
- package/src/alchemy.ts +10 -20
- package/src/apply.ts +29 -12
- package/src/aws/utils.ts +2 -1
- package/src/clickhouse/api/client.gen.ts +28 -0
- package/src/clickhouse/api/sdk.gen.ts +1286 -0
- package/src/clickhouse/api/types.gen.ts +5898 -0
- package/src/clickhouse/api-key.ts +136 -0
- package/src/clickhouse/api.ts +84 -0
- package/src/clickhouse/index.ts +3 -0
- package/src/clickhouse/organization.ts +31 -0
- package/src/clickhouse/service.ts +668 -0
- package/src/cloudflare/account-api-token.ts +1 -4
- package/src/cloudflare/api.ts +2 -0
- package/src/cloudflare/bun-spa/bun-spa.ts +136 -0
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/container.ts +20 -1
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/miniflare/build-worker-options.ts +38 -8
- package/src/cloudflare/miniflare/miniflare-controller.ts +2 -0
- package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +41 -58
- package/src/cloudflare/permission-groups.ts +21 -49
- package/src/cloudflare/redwood/redwood.ts +1 -1
- package/src/cloudflare/util/filter-env-bindings.ts +96 -0
- package/src/cloudflare/website.ts +11 -24
- package/src/cloudflare/worker-metadata.ts +2 -0
- package/src/cloudflare/worker.ts +12 -1
- package/src/cloudflare/wrangler.json.ts +12 -8
- package/src/coinbase/client.ts +146 -0
- package/src/coinbase/evm-account.ts +317 -0
- package/src/coinbase/evm-smart-account.ts +339 -0
- package/src/coinbase/faucet.ts +173 -0
- package/src/coinbase/index.ts +3 -0
- package/src/coinbase/types.ts +33 -0
- package/src/coinbase/utils.ts +19 -0
- package/src/neon/api/types.gen.ts +13 -1
- package/src/planetscale/api/types.gen.ts +22 -2
- package/src/planetscale/api.ts +1 -0
- package/src/planetscale/password.ts +1 -14
- package/src/resource.ts +11 -4
- package/src/scope.ts +29 -45
- package/src/state/instrumented-state-store.ts +27 -26
- package/src/test/bun.ts +1 -2
- package/src/test/vitest.ts +1 -2
- package/src/util/diff.ts +14 -0
- package/src/util/idempotent-spawn.ts +1 -1
- package/src/util/telemetry.ts +328 -0
- package/templates/tanstack-start/package.json +2 -2
- package/workers/tunnel-proxy.js +1 -1
- package/lib/util/telemetry/client.d.ts +0 -31
- package/lib/util/telemetry/client.d.ts.map +0 -1
- package/lib/util/telemetry/client.js +0 -121
- package/lib/util/telemetry/client.js.map +0 -1
- package/lib/util/telemetry/constants.d.ts +0 -5
- package/lib/util/telemetry/constants.d.ts.map +0 -1
- package/lib/util/telemetry/constants.js +0 -8
- package/lib/util/telemetry/constants.js.map +0 -1
- package/lib/util/telemetry/context.d.ts +0 -15
- package/lib/util/telemetry/context.d.ts.map +0 -1
- package/lib/util/telemetry/context.js +0 -128
- package/lib/util/telemetry/context.js.map +0 -1
- package/lib/util/telemetry/index.d.ts +0 -3
- package/lib/util/telemetry/index.d.ts.map +0 -1
- package/lib/util/telemetry/index.js +0 -3
- package/lib/util/telemetry/index.js.map +0 -1
- package/lib/util/telemetry/types.d.ts +0 -68
- package/lib/util/telemetry/types.d.ts.map +0 -1
- package/lib/util/telemetry/types.js +0 -1
- package/src/util/telemetry/client.ts +0 -156
- package/src/util/telemetry/constants.ts +0 -13
- package/src/util/telemetry/context.ts +0 -154
- package/src/util/telemetry/index.ts +0 -2
- package/src/util/telemetry/types.ts +0 -105
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import type { Context } from "../context.ts";
|
|
2
|
+
import { Resource } from "../resource.ts";
|
|
3
|
+
import { createCdpClient, type CoinbaseClientOptions } from "./client.ts";
|
|
4
|
+
import type { EvmAccount } from "./evm-account.ts";
|
|
5
|
+
import type {
|
|
6
|
+
Address,
|
|
7
|
+
FaucetConfig,
|
|
8
|
+
FaucetNetwork,
|
|
9
|
+
FaucetToken,
|
|
10
|
+
} from "./types.ts";
|
|
11
|
+
import { validateAccountName } from "./utils.ts";
|
|
12
|
+
|
|
13
|
+
export interface EvmSmartAccountProps extends CoinbaseClientOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Name for the smart account.
|
|
16
|
+
* If not provided, inherits the name from the owner account.
|
|
17
|
+
* This allows EOA and Smart Account to have matching names in CDP.
|
|
18
|
+
* Used for identification in CDP.
|
|
19
|
+
* Must contain only letters, numbers, and hyphens.
|
|
20
|
+
* @default Inherits from owner account name
|
|
21
|
+
*/
|
|
22
|
+
name?: string;
|
|
23
|
+
/**
|
|
24
|
+
* The owner account that controls this smart account.
|
|
25
|
+
* Can be either an EvmAccount resource or an owner address.
|
|
26
|
+
*/
|
|
27
|
+
owner: EvmAccount | Address;
|
|
28
|
+
/**
|
|
29
|
+
* Whether to adopt an existing smart account with the same name if it already exists.
|
|
30
|
+
* Without adoption, creation will fail if a smart account with the same name exists.
|
|
31
|
+
* With adoption, the existing smart account will be used.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
adopt?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Faucet configuration for development funding.
|
|
37
|
+
* Declares which tokens this smart account should have.
|
|
38
|
+
* Used by external funding scripts - not processed by the resource.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* faucet: {
|
|
43
|
+
* "base-sepolia": ["eth", "usdc"],
|
|
44
|
+
* "ethereum-sepolia": ["eth"]
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
faucet?: FaucetConfig;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface EvmSmartAccount
|
|
52
|
+
extends Resource<"coinbase::evm-smart-account"> {
|
|
53
|
+
/**
|
|
54
|
+
* The smart account name in CDP
|
|
55
|
+
*/
|
|
56
|
+
name: string;
|
|
57
|
+
/**
|
|
58
|
+
* The smart account address (same across all EVM networks)
|
|
59
|
+
*/
|
|
60
|
+
address: Address;
|
|
61
|
+
/**
|
|
62
|
+
* The owner account address
|
|
63
|
+
*/
|
|
64
|
+
ownerAddress: Address;
|
|
65
|
+
/**
|
|
66
|
+
* Faucet configuration (passed through from props)
|
|
67
|
+
*/
|
|
68
|
+
faucet?: FaucetConfig;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Manages ERC-4337 smart accounts on Coinbase Developer Platform.
|
|
73
|
+
* Smart accounts enable gasless transactions and advanced features like batch operations.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ## Create a smart account with an EVM account owner
|
|
77
|
+
*
|
|
78
|
+
* ```ts
|
|
79
|
+
* const owner = await EvmAccount("owner", {
|
|
80
|
+
* name: "owner-account"
|
|
81
|
+
* });
|
|
82
|
+
*
|
|
83
|
+
* const smartAccount = await EvmSmartAccount("my-smart-account", {
|
|
84
|
+
* name: "my-smart-account",
|
|
85
|
+
* owner: owner
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* console.log("Smart account address:", smartAccount.address);
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ## Create a smart account with existing owner
|
|
93
|
+
*
|
|
94
|
+
* ```ts
|
|
95
|
+
* const smartAccount = await EvmSmartAccount("my-smart-account", {
|
|
96
|
+
* name: "my-smart-account",
|
|
97
|
+
* owner: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" // Owner address
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ## Smart account with inherited name
|
|
103
|
+
*
|
|
104
|
+
* When name is omitted, the smart account inherits the owner's name
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* const owner = await EvmAccount("owner", {
|
|
108
|
+
* name: "my-app-wallet"
|
|
109
|
+
* });
|
|
110
|
+
*
|
|
111
|
+
* // Smart account will also be named "my-app-wallet" in CDP
|
|
112
|
+
* const smartAccount = await EvmSmartAccount("smart", {
|
|
113
|
+
* owner: owner
|
|
114
|
+
* // name is omitted, inherits "my-app-wallet" from owner
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* console.log(smartAccount.name); // "my-app-wallet"
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ## Adopt an existing smart account
|
|
122
|
+
*
|
|
123
|
+
* ```ts
|
|
124
|
+
* const smartAccount = await EvmSmartAccount("my-smart-account", {
|
|
125
|
+
* name: "existing-smart-account",
|
|
126
|
+
* owner: ownerAccount, // or "0x..." address
|
|
127
|
+
* adopt: true // Uses existing smart account if it exists
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export const EvmSmartAccount = Resource(
|
|
132
|
+
"coinbase::evm-smart-account",
|
|
133
|
+
async function (
|
|
134
|
+
this: Context<EvmSmartAccount>,
|
|
135
|
+
_id: string,
|
|
136
|
+
props: EvmSmartAccountProps,
|
|
137
|
+
): Promise<EvmSmartAccount> {
|
|
138
|
+
// Initialize CDP client with credentials from props or environment
|
|
139
|
+
const cdp = await createCdpClient({
|
|
140
|
+
apiKeyId: props.apiKeyId,
|
|
141
|
+
apiKeySecret: props.apiKeySecret,
|
|
142
|
+
walletSecret: props.walletSecret,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Get owner address
|
|
146
|
+
const ownerAddress =
|
|
147
|
+
typeof props.owner === "string" ? props.owner : props.owner.address;
|
|
148
|
+
|
|
149
|
+
// Get owner account for CDP operations
|
|
150
|
+
// When owner is an address string, we use it to get the account
|
|
151
|
+
const ownerAccount =
|
|
152
|
+
typeof props.owner === "string"
|
|
153
|
+
? await cdp.evm.getAccount({ address: props.owner })
|
|
154
|
+
: await cdp.evm.getAccount({ address: props.owner.address });
|
|
155
|
+
|
|
156
|
+
// If no name provided, inherit from owner
|
|
157
|
+
// this mean that if you pass only { owner } the created smartAccount will be same name as the EOA owner
|
|
158
|
+
// this to support https://github.com/base/account-sdk/blob/master/packages/account-sdk/src/interface/payment/charge.ts#L120
|
|
159
|
+
let accountName = props.name ? props.name : ownerAccount.name;
|
|
160
|
+
if (!accountName) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Smart account requires a name. Either provide 'name' in props or ensure the owner account has a name. Owner address: ${ownerAddress}`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Validate account name format
|
|
167
|
+
validateAccountName(accountName);
|
|
168
|
+
|
|
169
|
+
// Handle update phase
|
|
170
|
+
if (this.phase === "update" && this.output) {
|
|
171
|
+
// Check if owner changed (which would require replacement)
|
|
172
|
+
if (ownerAddress !== this.output.ownerAddress) {
|
|
173
|
+
this.replace();
|
|
174
|
+
}
|
|
175
|
+
// Check if name changed
|
|
176
|
+
if (accountName !== this.output.name) {
|
|
177
|
+
await cdp.evm.updateSmartAccount({
|
|
178
|
+
address: this.output.address,
|
|
179
|
+
owner: ownerAccount,
|
|
180
|
+
update: {
|
|
181
|
+
name: accountName,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
...this.output,
|
|
187
|
+
name: accountName,
|
|
188
|
+
// faucet: props.faucet, not sure we need this?
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// Update faucet configuration if changed
|
|
192
|
+
if (JSON.stringify(props.faucet) !== JSON.stringify(this.output.faucet)) {
|
|
193
|
+
// Only fund NEW combinations that weren't in the previous config
|
|
194
|
+
try {
|
|
195
|
+
// Get all old combinations
|
|
196
|
+
const oldCombinations = new Set(
|
|
197
|
+
Object.entries(this.output.faucet ?? {}).flatMap(
|
|
198
|
+
([network, tokens]) =>
|
|
199
|
+
tokens.map((token) => `${network}:${token}`),
|
|
200
|
+
),
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Get all new combinations
|
|
204
|
+
const newCombinations = Object.entries(props.faucet ?? {}).flatMap(
|
|
205
|
+
([network, tokens]) =>
|
|
206
|
+
tokens.map((token) => ({
|
|
207
|
+
network: network as FaucetNetwork,
|
|
208
|
+
token: token as FaucetToken,
|
|
209
|
+
key: `${network}:${token}`,
|
|
210
|
+
})),
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// Filter to only combinations that are actually new
|
|
214
|
+
const toFund = newCombinations.filter(
|
|
215
|
+
({ key }) => !oldCombinations.has(key),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
if (toFund.length > 0) {
|
|
219
|
+
await Promise.all(
|
|
220
|
+
toFund.map(({ network, token }) =>
|
|
221
|
+
cdp.evm
|
|
222
|
+
.requestFaucet({
|
|
223
|
+
address: this.output.address,
|
|
224
|
+
network,
|
|
225
|
+
token,
|
|
226
|
+
})
|
|
227
|
+
.catch((err) => {
|
|
228
|
+
console.warn(
|
|
229
|
+
`⚠️ Failed to request ${token} on ${network} for smart account ${this.output.address}: ${err.message}`,
|
|
230
|
+
);
|
|
231
|
+
return null;
|
|
232
|
+
}),
|
|
233
|
+
),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
console.log(
|
|
237
|
+
`💧 Requested ${toFund.length} new faucet fund${toFund.length === 1 ? "" : "s"} for smart account ${this.output.address}`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
} catch (error: any) {
|
|
241
|
+
console.warn(
|
|
242
|
+
`⚠️ Failed to request additional faucet funds for smart account ${this.output.address}: ${error.message}`,
|
|
243
|
+
);
|
|
244
|
+
// Continue - don't break the update
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
...this.output,
|
|
249
|
+
faucet: props.faucet,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
return this.output;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Handle delete phase
|
|
256
|
+
if (this.phase === "delete") {
|
|
257
|
+
// CDP SDK doesn't support deleting accounts
|
|
258
|
+
// Accounts remain in CDP but are no longer tracked by Alchemy
|
|
259
|
+
console.log(`🗑️ Untracking Smart Account: ${this.output?.address}`);
|
|
260
|
+
return this.destroy();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let smartAccount;
|
|
264
|
+
|
|
265
|
+
// Check for adoption or use getOrCreate pattern
|
|
266
|
+
const adopt = props.adopt ?? this.scope.adopt;
|
|
267
|
+
if (adopt) {
|
|
268
|
+
// With adoption, use getOrCreateSmartAccount which returns existing if it exists
|
|
269
|
+
smartAccount = await cdp.evm.getOrCreateSmartAccount({
|
|
270
|
+
name: accountName,
|
|
271
|
+
owner: ownerAccount,
|
|
272
|
+
});
|
|
273
|
+
} else {
|
|
274
|
+
// Without adoption, create a new smart account
|
|
275
|
+
try {
|
|
276
|
+
smartAccount = await cdp.evm.createSmartAccount({
|
|
277
|
+
name: accountName,
|
|
278
|
+
owner: ownerAccount,
|
|
279
|
+
});
|
|
280
|
+
} catch (error: any) {
|
|
281
|
+
// Provide helpful error message if smart account already exists
|
|
282
|
+
if (error.errorType === "already_exists") {
|
|
283
|
+
throw new Error(
|
|
284
|
+
`Smart account with name '${accountName}' already exists. Use adopt: true to use the existing smart account.`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
// Rethrow other errors unchanged
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Fund the smart account if faucet configuration is provided
|
|
293
|
+
if (props.faucet) {
|
|
294
|
+
try {
|
|
295
|
+
const combinations = Object.entries(props.faucet).flatMap(
|
|
296
|
+
([network, tokens]) =>
|
|
297
|
+
tokens.map((token) => ({
|
|
298
|
+
network: network as FaucetNetwork,
|
|
299
|
+
token: token as FaucetToken,
|
|
300
|
+
})),
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
await Promise.all(
|
|
304
|
+
combinations.map(({ network, token }) =>
|
|
305
|
+
cdp.evm
|
|
306
|
+
.requestFaucet({
|
|
307
|
+
address: smartAccount.address,
|
|
308
|
+
network,
|
|
309
|
+
token,
|
|
310
|
+
})
|
|
311
|
+
.catch((err) => {
|
|
312
|
+
console.warn(
|
|
313
|
+
`⚠️ Failed to request ${token} on ${network} for smart account ${smartAccount.address}: ${err.message}`,
|
|
314
|
+
);
|
|
315
|
+
return null;
|
|
316
|
+
}),
|
|
317
|
+
),
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
console.log(
|
|
321
|
+
`💧 Requested faucet funds for smart account ${smartAccount.address}`,
|
|
322
|
+
);
|
|
323
|
+
} catch (error: any) {
|
|
324
|
+
console.warn(
|
|
325
|
+
`⚠️ Failed to request faucet funds for smart account ${smartAccount.address}: ${error.message}`,
|
|
326
|
+
);
|
|
327
|
+
// Continue - don't break account creation
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Return smart account details
|
|
332
|
+
return {
|
|
333
|
+
name: accountName,
|
|
334
|
+
address: smartAccount.address,
|
|
335
|
+
ownerAddress,
|
|
336
|
+
faucet: props.faucet,
|
|
337
|
+
} as EvmSmartAccount;
|
|
338
|
+
},
|
|
339
|
+
);
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Request testnet tokens from Coinbase faucet for accounts with faucet metadata
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bun node_modules/alchemy/src/coinbase/faucet.ts # Fund all accounts with faucet config
|
|
7
|
+
* bun node_modules/alchemy/src/coinbase/faucet.ts dev # Fund only accounts in dev scope
|
|
8
|
+
* bun node_modules/alchemy/src/coinbase/faucet.ts backend/dev # Fund specific scope
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFile, access } from "node:fs/promises";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { glob } from "glob";
|
|
14
|
+
import { createCdpClient } from "./client.ts";
|
|
15
|
+
import type { EvmAccount } from "./evm-account.ts";
|
|
16
|
+
import type { EvmSmartAccount } from "./evm-smart-account.ts";
|
|
17
|
+
import type { FaucetNetwork, FaucetToken } from "./types.ts";
|
|
18
|
+
|
|
19
|
+
// Track funded combinations to avoid duplicates within this run
|
|
20
|
+
const funded = new Set<string>();
|
|
21
|
+
|
|
22
|
+
// Get optional scope argument
|
|
23
|
+
const scope = process.argv[2];
|
|
24
|
+
|
|
25
|
+
// Initialize CDP client (uses CDP_* env vars automatically)
|
|
26
|
+
const cdp = await createCdpClient();
|
|
27
|
+
|
|
28
|
+
let stateFiles: string[];
|
|
29
|
+
|
|
30
|
+
if (scope) {
|
|
31
|
+
// If scope provided, find matching directories
|
|
32
|
+
const scopePath = join(".alchemy", scope);
|
|
33
|
+
|
|
34
|
+
let scopeExists = false;
|
|
35
|
+
try {
|
|
36
|
+
await access(scopePath);
|
|
37
|
+
scopeExists = true;
|
|
38
|
+
} catch {
|
|
39
|
+
scopeExists = false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (scopeExists) {
|
|
43
|
+
// Exact match found
|
|
44
|
+
console.log(`Using scope: ${scope}`);
|
|
45
|
+
stateFiles = await glob("*.json", {
|
|
46
|
+
cwd: scopePath,
|
|
47
|
+
absolute: true,
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
// Try to find directories that end with the provided scope
|
|
51
|
+
// We need to find directories, so we'll look for JSON files and extract the directory paths
|
|
52
|
+
const allFiles = await glob(`**/${scope}/*.json`, {
|
|
53
|
+
cwd: ".alchemy",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Extract unique directory paths
|
|
57
|
+
const possibleScopes = [
|
|
58
|
+
...new Set(allFiles.map((f) => f.replace(/\/[^/]+\.json$/, ""))),
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
if (possibleScopes.length === 0) {
|
|
62
|
+
console.error(`No scope found matching: ${scope}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
} else if (possibleScopes.length === 1) {
|
|
65
|
+
// Found exactly one match
|
|
66
|
+
const foundScope = possibleScopes[0];
|
|
67
|
+
console.log(`Using scope: ${foundScope}`);
|
|
68
|
+
stateFiles = await glob("*.json", {
|
|
69
|
+
cwd: join(".alchemy", foundScope),
|
|
70
|
+
absolute: true,
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
// Multiple matches found - use all of them
|
|
74
|
+
console.log(`Found ${possibleScopes.length} scopes matching '${scope}':`);
|
|
75
|
+
for (const s of possibleScopes) {
|
|
76
|
+
console.log(` - ${s}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Collect state files from all matching scopes
|
|
80
|
+
stateFiles = [];
|
|
81
|
+
for (const foundScope of possibleScopes) {
|
|
82
|
+
const files = await glob("*.json", {
|
|
83
|
+
cwd: join(".alchemy", foundScope),
|
|
84
|
+
absolute: true,
|
|
85
|
+
});
|
|
86
|
+
stateFiles.push(...files);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
// No scope provided, search all .alchemy subdirectories
|
|
92
|
+
console.log("Searching all scopes for accounts with faucet configuration...");
|
|
93
|
+
stateFiles = await glob("**/*.json", {
|
|
94
|
+
cwd: ".alchemy",
|
|
95
|
+
absolute: true,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const allStates = (
|
|
100
|
+
await Promise.all(
|
|
101
|
+
stateFiles.map(async (file) => {
|
|
102
|
+
try {
|
|
103
|
+
const content = await readFile(file, "utf-8");
|
|
104
|
+
return JSON.parse(content);
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}),
|
|
109
|
+
)
|
|
110
|
+
).filter(Boolean);
|
|
111
|
+
|
|
112
|
+
const accounts = allStates
|
|
113
|
+
.filter(
|
|
114
|
+
(state) =>
|
|
115
|
+
state?.kind?.startsWith("coinbase::evm") &&
|
|
116
|
+
state.output &&
|
|
117
|
+
(state.output as EvmAccount | EvmSmartAccount).faucet,
|
|
118
|
+
)
|
|
119
|
+
.map((state) => {
|
|
120
|
+
const output = state.output as EvmAccount | EvmSmartAccount;
|
|
121
|
+
return {
|
|
122
|
+
address: output.address,
|
|
123
|
+
name: output.name,
|
|
124
|
+
faucet: output.faucet!,
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (accounts.length === 0) {
|
|
129
|
+
console.log("No accounts with faucet configuration found");
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
console.log(`Found ${accounts.length} accounts with faucet configuration\n`);
|
|
134
|
+
|
|
135
|
+
for (const account of accounts) {
|
|
136
|
+
console.log(`Account: ${account.name} (${account.address})`);
|
|
137
|
+
|
|
138
|
+
for (const [network, tokens] of Object.entries(account.faucet)) {
|
|
139
|
+
for (const token of tokens) {
|
|
140
|
+
const key = `${account.address}-${network}-${token}`;
|
|
141
|
+
|
|
142
|
+
// Skip if already processed in this run
|
|
143
|
+
if (funded.has(key)) {
|
|
144
|
+
console.log(` ⏭️ Skipping ${token} on ${network} (already requested)`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Request tokens from faucet
|
|
149
|
+
try {
|
|
150
|
+
console.log(` 💧 Requesting ${token} on ${network}...`);
|
|
151
|
+
|
|
152
|
+
const response = await cdp.evm.requestFaucet({
|
|
153
|
+
address: account.address,
|
|
154
|
+
network: network as FaucetNetwork,
|
|
155
|
+
token: token as FaucetToken,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
console.log(` ✅ Funded: ${response.transactionHash}`);
|
|
159
|
+
funded.add(key);
|
|
160
|
+
|
|
161
|
+
// Small delay to avoid rate limits
|
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
163
|
+
} catch (error: any) {
|
|
164
|
+
console.log(
|
|
165
|
+
` ❌ Error funding ${token} on ${network}: ${error.message}`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
console.log();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log("✨ Funding complete!");
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common types for Coinbase CDP resources
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Ethereum address format (20 bytes, 40 hex chars)
|
|
7
|
+
*/
|
|
8
|
+
export type Address = `0x${string}`;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Private key format (32 bytes, 64 hex chars)
|
|
12
|
+
*/
|
|
13
|
+
export type PrivateKey = `0x${string}`;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Transaction hash format (32 bytes, 64 hex chars)
|
|
17
|
+
*/
|
|
18
|
+
export type TransactionHash = `0x${string}`;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Supported testnet networks for faucet requests
|
|
22
|
+
*/
|
|
23
|
+
export type FaucetNetwork = "base-sepolia" | "ethereum-sepolia";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Supported tokens for faucet requests
|
|
27
|
+
*/
|
|
28
|
+
export type FaucetToken = "eth" | "usdc" | "eurc" | "cbbtc";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Faucet configuration for requesting testnet tokens
|
|
32
|
+
*/
|
|
33
|
+
export type FaucetConfig = Partial<Record<FaucetNetwork, FaucetToken[]>>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for Coinbase CDP resources
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validates that a CDP account name follows the required format.
|
|
7
|
+
* CDP only allows letters, numbers, and hyphens in account names.
|
|
8
|
+
* Names cannot start or end with a hyphen.
|
|
9
|
+
*
|
|
10
|
+
* @param name - The account name to validate
|
|
11
|
+
* @throws Error if the name contains invalid characters or starts/ends with hyphen
|
|
12
|
+
*/
|
|
13
|
+
export function validateAccountName(name: string): void {
|
|
14
|
+
if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(name)) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Invalid account name '${name}'. CDP only allows letters, numbers, and hyphens. Names cannot start or end with a hyphen.`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1899,6 +1899,7 @@ export type BillingAccountState =
|
|
|
1899
1899
|
export type BillingSubscriptionType =
|
|
1900
1900
|
| "UNKNOWN"
|
|
1901
1901
|
| "direct_sales"
|
|
1902
|
+
| "direct_sales_v3"
|
|
1902
1903
|
| "aws_marketplace"
|
|
1903
1904
|
| "free_v2"
|
|
1904
1905
|
| "free_v3"
|
|
@@ -2527,6 +2528,11 @@ export type NeonAuthCreateIntegrationRequest = {
|
|
|
2527
2528
|
role_name?: string;
|
|
2528
2529
|
};
|
|
2529
2530
|
|
|
2531
|
+
export type EnableNeonAuthIntegrationRequest = {
|
|
2532
|
+
auth_provider: NeonAuthSupportedAuthProvider;
|
|
2533
|
+
database_name?: string;
|
|
2534
|
+
};
|
|
2535
|
+
|
|
2530
2536
|
export type NeonAuthCreateIntegrationResponse = {
|
|
2531
2537
|
auth_provider: NeonAuthSupportedAuthProvider;
|
|
2532
2538
|
auth_provider_project_id: string;
|
|
@@ -2677,7 +2683,12 @@ export type RoleOperations = RoleResponse & OperationsResponse;
|
|
|
2677
2683
|
|
|
2678
2684
|
export type JwksCreationOperation = JwksResponse & OperationsResponse;
|
|
2679
2685
|
|
|
2680
|
-
export type SupportTicketSeverity =
|
|
2686
|
+
export type SupportTicketSeverity =
|
|
2687
|
+
| "low"
|
|
2688
|
+
| "normal"
|
|
2689
|
+
| "high"
|
|
2690
|
+
| "critical"
|
|
2691
|
+
| "urgent";
|
|
2681
2692
|
|
|
2682
2693
|
export type AnnotationData = {
|
|
2683
2694
|
object: AnnotationObjectData;
|
|
@@ -2758,6 +2769,7 @@ export type Snapshot = {
|
|
|
2758
2769
|
source_branch_id?: string;
|
|
2759
2770
|
created_at: string;
|
|
2760
2771
|
expires_at?: string;
|
|
2772
|
+
manual?: boolean;
|
|
2761
2773
|
};
|
|
2762
2774
|
|
|
2763
2775
|
export type SnapshotUpdateRequest = {
|
|
@@ -514,6 +514,10 @@ export type PaginatedPostgresClusterResizeRequest = {
|
|
|
514
514
|
* The SKU representing the branch cluster for display
|
|
515
515
|
*/
|
|
516
516
|
cluster_display_name: string;
|
|
517
|
+
/**
|
|
518
|
+
* Whether or not this is a metal database
|
|
519
|
+
*/
|
|
520
|
+
cluster_metal: boolean;
|
|
517
521
|
/**
|
|
518
522
|
* The total number of replicas
|
|
519
523
|
*/
|
|
@@ -532,6 +536,10 @@ export type PaginatedPostgresClusterResizeRequest = {
|
|
|
532
536
|
* The previous SKU representing the branch cluster for display
|
|
533
537
|
*/
|
|
534
538
|
previous_cluster_display_name: string;
|
|
539
|
+
/**
|
|
540
|
+
* Whether or not the previous SKU was a metal database
|
|
541
|
+
*/
|
|
542
|
+
previous_cluster_metal: boolean;
|
|
535
543
|
/**
|
|
536
544
|
* The previous total number of replicas
|
|
537
545
|
*/
|
|
@@ -652,6 +660,10 @@ export type PostgresClusterResizeRequest = {
|
|
|
652
660
|
* The SKU representing the branch cluster for display
|
|
653
661
|
*/
|
|
654
662
|
cluster_display_name: string;
|
|
663
|
+
/**
|
|
664
|
+
* Whether or not this is a metal database
|
|
665
|
+
*/
|
|
666
|
+
cluster_metal: boolean;
|
|
655
667
|
/**
|
|
656
668
|
* The total number of replicas
|
|
657
669
|
*/
|
|
@@ -670,6 +682,10 @@ export type PostgresClusterResizeRequest = {
|
|
|
670
682
|
* The previous SKU representing the branch cluster for display
|
|
671
683
|
*/
|
|
672
684
|
previous_cluster_display_name: string;
|
|
685
|
+
/**
|
|
686
|
+
* Whether or not the previous SKU was a metal database
|
|
687
|
+
*/
|
|
688
|
+
previous_cluster_metal: boolean;
|
|
673
689
|
/**
|
|
674
690
|
* The previous total number of replicas
|
|
675
691
|
*/
|
|
@@ -1301,7 +1317,8 @@ export type PostgresClusterExtension = {
|
|
|
1301
1317
|
| "milliseconds"
|
|
1302
1318
|
| "seconds"
|
|
1303
1319
|
| "select"
|
|
1304
|
-
| "string"
|
|
1320
|
+
| "string"
|
|
1321
|
+
| "time";
|
|
1305
1322
|
/**
|
|
1306
1323
|
* The default value of the parameter
|
|
1307
1324
|
*/
|
|
@@ -1408,7 +1425,8 @@ export type PostgresClusterParameter = {
|
|
|
1408
1425
|
| "milliseconds"
|
|
1409
1426
|
| "seconds"
|
|
1410
1427
|
| "select"
|
|
1411
|
-
| "string"
|
|
1428
|
+
| "string"
|
|
1429
|
+
| "time";
|
|
1412
1430
|
/**
|
|
1413
1431
|
* The default value of the parameter
|
|
1414
1432
|
*/
|
|
@@ -6943,6 +6961,7 @@ export type PaginatedDatabaseWebhook = {
|
|
|
6943
6961
|
| "branch.anomaly"
|
|
6944
6962
|
| "branch.schema_recommendation"
|
|
6945
6963
|
| "branch.sleeping"
|
|
6964
|
+
| "branch.start_maintenance"
|
|
6946
6965
|
| "cluster.storage"
|
|
6947
6966
|
| "database.access_request"
|
|
6948
6967
|
| "deploy_request.closed"
|
|
@@ -7004,6 +7023,7 @@ export type DatabaseWebhook = {
|
|
|
7004
7023
|
| "branch.anomaly"
|
|
7005
7024
|
| "branch.schema_recommendation"
|
|
7006
7025
|
| "branch.sleeping"
|
|
7026
|
+
| "branch.start_maintenance"
|
|
7007
7027
|
| "cluster.storage"
|
|
7008
7028
|
| "database.access_request"
|
|
7009
7029
|
| "deploy_request.closed"
|
package/src/planetscale/api.ts
CHANGED