@intentlayer/sdk 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Intent Layer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,259 @@
1
+ # @intentlayer/sdk
2
+
3
+ A TypeScript SDK for building, signing, and verifying intents on Intent Layer.
4
+
5
+ [![npm version](https://badge.fury.io/js/@intentlayer%2Fsdk.svg)](https://badge.fury.io/js/@intentlayer%2Fsdk)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Beta Notice
9
+
10
+ This SDK is in **beta**. Here's what that means for you:
11
+
12
+ | Area | Stability |
13
+ |------|-----------|
14
+ | `CloudRuntime` API | Stable — safe to build against |
15
+ | Intent signing (`buildIntent`, `signIntent`, `verifyIntent`) | Stable |
16
+ | Error types | May expand — new error classes may be added |
17
+ | Constraint builders | Stable API, coverage expanding |
18
+
19
+ Breaking changes will follow semver: breaking = major version bump.
20
+
21
+ ## Features
22
+
23
+ - **EIP-712 Intent Signing** - Cryptographically secure intent signing with viem
24
+ - **Intent Builders** - Easy-to-use functions for building intents
25
+ - **Signature Verification** - Verify intent signatures off-chain
26
+ - **Type-Safe** - Full TypeScript support with strict types
27
+ - **Modern Bundling** - ESM/CJS dual exports for maximum compatibility
28
+ - **Minimal Dependencies** - Only depends on viem for maximum efficiency
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npm install @intentlayer/sdk viem
34
+ # or
35
+ yarn add @intentlayer/sdk viem
36
+ # or
37
+ pnpm add @intentlayer/sdk viem
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ```typescript
43
+ import {
44
+ buildIntent,
45
+ signIntent,
46
+ verifyIntent,
47
+ makeDomain,
48
+ } from "@intentlayer/sdk";
49
+ import { createWalletClient, http } from "viem";
50
+ import { privateKeyToAccount } from "viem/accounts";
51
+
52
+ // Setup wallet
53
+ const account = privateKeyToAccount("0x...");
54
+ const wallet = createWalletClient({
55
+ account,
56
+ chain: mainnet, // or your target chain
57
+ transport: http(),
58
+ });
59
+
60
+ // Build an intent
61
+ const intent = buildIntent({
62
+ policyId: "0x1234...", // Policy ID for the intent
63
+ amount: 100n,
64
+ tokenOrRecip: "0xA0b86a33E6842740073e8138aA1204a5d9e31e85", // Token address
65
+ nonce: 0,
66
+ intentType: 0,
67
+ sigType: 0,
68
+ callData: "0x...", // ABI-encoded function call
69
+ validFor: 3600, // Valid for 1 hour
70
+ });
71
+
72
+ // Sign the intent
73
+ const domain = makeDomain(1, "0x..."); // chainId, Intent Manager address
74
+ const signedIntent = await signIntent(intent, domain, wallet);
75
+
76
+ // Verify the signature
77
+ const isValid = await verifyIntent(signedIntent, domain, account.address);
78
+
79
+ console.log("Intent signed and verified:", isValid);
80
+ ```
81
+
82
+ ## Core Concepts
83
+
84
+ ### Intent Structure
85
+
86
+ An intent represents a user's desire to perform an action:
87
+
88
+ ```typescript
89
+ interface Intent {
90
+ policyId: HexString; // Policy governing this intent
91
+ amount: bigint; // Amount in smallest units
92
+ tokenOrRecip: Address; // Token or recipient address
93
+ validUntil: number; // Unix timestamp when intent expires
94
+ nonce: number; // Anti-replay nonce
95
+ intentType: number; // Intent type discriminator
96
+ sigType: number; // Signature type (0 = EOA/EIP-712)
97
+ callData: HexString; // Executor call data
98
+ signature?: HexString; // EIP-712 signature (added by signIntent)
99
+ }
100
+ ```
101
+
102
+ ### EIP-712 Domain
103
+
104
+ The SDK uses EIP-712 for secure intent signing:
105
+
106
+ ```typescript
107
+ const domain = makeDomain(
108
+ 31337, // Chain ID (Hardhat local)
109
+ "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" // Intent Manager contract address
110
+ );
111
+ ```
112
+
113
+ ## API Reference
114
+
115
+ ### Core Functions
116
+
117
+ #### `buildIntent(args: BuildIntentArgs): Intent`
118
+
119
+ Creates an unsigned intent object.
120
+
121
+ ```typescript
122
+ const intent = buildIntent({
123
+ policyId: "0x...",
124
+ amount: 100n,
125
+ tokenOrRecip: "0x...",
126
+ nonce: 0,
127
+ intentType: 0,
128
+ sigType: 0,
129
+ callData: "0x...",
130
+ validFor: 3600, // Seconds until expiry
131
+ });
132
+ ```
133
+
134
+ #### `signIntent(intent, domain, wallet): Promise<Intent>`
135
+
136
+ Signs an intent using EIP-712.
137
+
138
+ ```typescript
139
+ const signedIntent = await signIntent(intent, domain, walletClient);
140
+ ```
141
+
142
+ #### `verifyIntent(intent, domain, signer): Promise<boolean>`
143
+
144
+ Verifies an intent signature.
145
+
146
+ ```typescript
147
+ const isValid = await verifyIntent(signedIntent, domain, signerAddress);
148
+ ```
149
+
150
+ #### `getDigest(intent, domain): HexString`
151
+
152
+ Computes the EIP-712 digest for an intent.
153
+
154
+ ```typescript
155
+ const digest = getDigest(intent, domain);
156
+ ```
157
+
158
+ #### `makeDomain(chainId, verifyingContract): TypedDataDomain`
159
+
160
+ Creates the EIP-712 domain for intent signing.
161
+
162
+ ```typescript
163
+ const domain = makeDomain(1, intentManagerAddress);
164
+ ```
165
+
166
+ ### Executor Helpers
167
+
168
+ #### `buildPermit2ExecutorCalldata(args): HexString`
169
+
170
+ Builds calldata for ERC20 token transfers via Permit2Executor.
171
+
172
+ ```typescript
173
+ import { buildPermit2ExecutorCalldata } from "@intentlayer/sdk";
174
+
175
+ const callData = buildPermit2ExecutorCalldata({
176
+ owner: "0x...", // Token owner address
177
+ token: "0x...", // Token contract address
178
+ to: "0x...", // Recipient address
179
+ amount: 100n, // Amount in token's smallest unit
180
+ });
181
+ ```
182
+
183
+ ## Testing
184
+
185
+ The SDK includes comprehensive tests:
186
+
187
+ ```bash
188
+ # Run tests
189
+ yarn test
190
+
191
+ # Run tests in watch mode
192
+ yarn test:watch
193
+
194
+ # Run specific test
195
+ yarn test intent.test.ts
196
+ ```
197
+
198
+ ## Build from Source
199
+
200
+ ```bash
201
+ # Install dependencies
202
+ yarn install
203
+
204
+ # Build the SDK
205
+ yarn build
206
+
207
+ # Run tests
208
+ yarn test
209
+
210
+ # Generate documentation
211
+ yarn docs
212
+ ```
213
+
214
+ ## Examples
215
+
216
+ ### Token Transfer Intent
217
+
218
+ ```typescript
219
+ import { buildIntent, buildPermit2ExecutorCalldata } from "@intentlayer/sdk";
220
+
221
+ // Build executor calldata for token transfer
222
+ const callData = buildPermit2ExecutorCalldata({
223
+ owner: wallet.account.address,
224
+ token: "0xA0b86a33E6842740073e8138aA1204a5d9e31e85",
225
+ to: "0x742d35Cc641C0532F23c7C4de5Ff2ff2B2Acf5A2",
226
+ amount: parseUnits("100", 18), // 100 tokens
227
+ });
228
+
229
+ // Build the intent
230
+ const intent = buildIntent({
231
+ policyId: "0x1234...",
232
+ amount: parseUnits("100", 18),
233
+ tokenOrRecip: "0xA0b86a33E6842740073e8138aA1204a5d9e31e85",
234
+ nonce: 0,
235
+ intentType: 0,
236
+ sigType: 0,
237
+ callData,
238
+ validFor: 3600, // 1 hour
239
+ });
240
+ ```
241
+
242
+ ### Batch Operations
243
+
244
+ ```typescript
245
+ // Multiple intents can be created and signed together
246
+ const intents = await Promise.all([
247
+ signIntent(intent1, domain, wallet),
248
+ signIntent(intent2, domain, wallet),
249
+ signIntent(intent3, domain, wallet),
250
+ ]);
251
+ ```
252
+
253
+ ## Development
254
+
255
+ This SDK is part of the Intent Layer monorepo. See the main README for development setup.
256
+
257
+ ## License
258
+
259
+ MIT License - see LICENSE file for details.
@@ -0,0 +1,9 @@
1
+ import {
2
+ buildV2ConstraintsFromAgent,
3
+ constraintsToV2Params
4
+ } from "./chunk-GRBEB2EV.mjs";
5
+ import "./chunk-XP4I75K7.mjs";
6
+ export {
7
+ buildV2ConstraintsFromAgent,
8
+ constraintsToV2Params
9
+ };
@@ -0,0 +1,227 @@
1
+ // src/registryBootstrapBuilder.ts
2
+ import { keccak256, encodePacked } from "viem";
3
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
4
+ var ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
5
+ var DEFAULT_VALIDITY_HOURS = 1;
6
+ var DEFAULT_ORACLE_MAX_AGE = 3600;
7
+ var DEFAULT_TIMEOUT_SECONDS = 3600;
8
+ function buildRegistryBootstrapIntent(params) {
9
+ const validUntil = params.validUntil ?? params.currentTimestamp + BigInt(DEFAULT_VALIDITY_HOURS * 3600);
10
+ return {
11
+ // Common fields
12
+ wallet: params.wallet,
13
+ nonce: params.nonce ?? 0n,
14
+ validUntil,
15
+ chainId: params.chainId,
16
+ // Vendor Registry
17
+ enableVendor: params.enableVendor ?? false,
18
+ vendorProfileId: params.vendorProfileId ?? ZERO_BYTES32,
19
+ vendorProfileOwner: params.vendorProfileOwner ?? ZERO_ADDRESS,
20
+ vendorGroups: params.vendorGroups ?? [],
21
+ sealVendorVersion: params.sealVendorVersion ?? false,
22
+ // Venue Registry
23
+ enableVenue: params.enableVenue ?? false,
24
+ venueProfileId: params.venueProfileId ?? ZERO_BYTES32,
25
+ venueVersion: params.venueVersion ?? 0,
26
+ venueTargets: params.venueTargets ?? [],
27
+ venueSelectors: params.venueSelectors ?? [],
28
+ // Oracle Registry
29
+ enableOracle: params.enableOracle ?? false,
30
+ oracleProfileId: params.oracleProfileId ?? ZERO_BYTES32,
31
+ oracleVersion: params.oracleVersion ?? 0,
32
+ oracleMaxAge: params.oracleMaxAge ?? DEFAULT_ORACLE_MAX_AGE,
33
+ // Approval Registry
34
+ enableApproval: params.enableApproval ?? false,
35
+ approvalProfileId: params.approvalProfileId ?? ZERO_BYTES32,
36
+ softCapUSD: params.softCapUSD ?? 0n,
37
+ hardCapUSD: params.hardCapUSD ?? 0n,
38
+ approvers: params.approvers ?? [],
39
+ requiredSigs: params.requiredSigs ?? 0,
40
+ timeoutSeconds: params.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS
41
+ };
42
+ }
43
+ function buildVendorOnlyIntent(params) {
44
+ return buildRegistryBootstrapIntent({
45
+ ...params,
46
+ enableVendor: true,
47
+ sealVendorVersion: params.sealVendorVersion ?? true
48
+ });
49
+ }
50
+ function buildVenueOnlyIntent(params) {
51
+ return buildRegistryBootstrapIntent({
52
+ ...params,
53
+ enableVenue: true
54
+ });
55
+ }
56
+ function buildOracleOnlyIntent(params) {
57
+ return buildRegistryBootstrapIntent({
58
+ ...params,
59
+ enableOracle: true
60
+ });
61
+ }
62
+ function buildApprovalOnlyIntent(params) {
63
+ return buildRegistryBootstrapIntent({
64
+ ...params,
65
+ enableApproval: true
66
+ });
67
+ }
68
+ function buildAllRegistriesIntent(params) {
69
+ return buildRegistryBootstrapIntent({
70
+ ...params,
71
+ enableVendor: true,
72
+ sealVendorVersion: params.sealVendorVersion ?? true,
73
+ enableVenue: true,
74
+ enableOracle: true,
75
+ enableApproval: true
76
+ });
77
+ }
78
+ function validateRegistryBootstrapIntent(intent, currentTimestamp) {
79
+ const errors = [];
80
+ if (!intent.wallet || intent.wallet === ZERO_ADDRESS) {
81
+ errors.push("Wallet address is required");
82
+ }
83
+ if (intent.chainId === 0n) {
84
+ errors.push("Chain ID must be non-zero");
85
+ }
86
+ if (currentTimestamp && intent.validUntil <= currentTimestamp) {
87
+ errors.push("Intent has already expired");
88
+ }
89
+ if (!intent.enableVendor && !intent.enableVenue && !intent.enableOracle && !intent.enableApproval) {
90
+ errors.push("At least one registry must be enabled");
91
+ }
92
+ if (intent.enableVendor) {
93
+ if (intent.vendorProfileId === ZERO_BYTES32) {
94
+ errors.push("Vendor profile ID is required when vendor is enabled");
95
+ }
96
+ if (intent.vendorGroups.length === 0) {
97
+ errors.push("At least one vendor group is required when vendor is enabled");
98
+ }
99
+ for (let i = 0; i < intent.vendorGroups.length; i++) {
100
+ const group = intent.vendorGroups[i];
101
+ if (group.groupId === ZERO_BYTES32) {
102
+ errors.push(`Vendor group ${i} has invalid groupId`);
103
+ }
104
+ if (group.merkleRoot === ZERO_BYTES32) {
105
+ errors.push(`Vendor group ${i} has invalid merkleRoot`);
106
+ }
107
+ }
108
+ }
109
+ if (intent.enableVenue) {
110
+ if (intent.venueProfileId === ZERO_BYTES32) {
111
+ errors.push("Venue profile ID is required when venue is enabled");
112
+ }
113
+ if (intent.venueTargets.length === 0) {
114
+ errors.push("At least one venue target is required when venue is enabled");
115
+ }
116
+ if (intent.venueSelectors.length === 0) {
117
+ errors.push("At least one venue selector is required when venue is enabled");
118
+ }
119
+ }
120
+ if (intent.enableOracle) {
121
+ if (intent.oracleProfileId === ZERO_BYTES32) {
122
+ errors.push("Oracle profile ID is required when oracle is enabled");
123
+ }
124
+ if (intent.oracleMaxAge === 0) {
125
+ errors.push("Oracle max age must be non-zero");
126
+ }
127
+ }
128
+ if (intent.enableApproval) {
129
+ if (intent.approvalProfileId === ZERO_BYTES32) {
130
+ errors.push("Approval profile ID is required when approval is enabled");
131
+ }
132
+ if (intent.approvers.length === 0) {
133
+ errors.push("At least one approver is required when approval is enabled");
134
+ }
135
+ if (intent.requiredSigs === 0) {
136
+ errors.push("Required signatures must be non-zero");
137
+ }
138
+ if (intent.requiredSigs > intent.approvers.length) {
139
+ errors.push("Required signatures cannot exceed number of approvers");
140
+ }
141
+ if (intent.softCapUSD > intent.hardCapUSD) {
142
+ errors.push("Soft cap cannot exceed hard cap");
143
+ }
144
+ }
145
+ return {
146
+ valid: errors.length === 0,
147
+ errors
148
+ };
149
+ }
150
+ function generateProfileId(organization, profileName) {
151
+ return keccak256(encodePacked(["string", "string"], [organization, profileName]));
152
+ }
153
+ function hasRegistriesEnabled(intent) {
154
+ return intent.enableVendor || intent.enableVenue || intent.enableOracle || intent.enableApproval;
155
+ }
156
+ function summarizeRegistryFeatures(intent) {
157
+ const features = [];
158
+ if (intent.enableVendor) {
159
+ features.push(`Vendor (${intent.vendorGroups.length} groups${intent.sealVendorVersion ? ", sealed" : ""})`);
160
+ }
161
+ if (intent.enableVenue) {
162
+ features.push(`Venue (${intent.venueTargets.length} targets, ${intent.venueSelectors.length} selectors)`);
163
+ }
164
+ if (intent.enableOracle) {
165
+ features.push(`Oracle (maxAge: ${intent.oracleMaxAge}s)`);
166
+ }
167
+ if (intent.enableApproval) {
168
+ features.push(`Approval (${intent.requiredSigs}/${intent.approvers.length} sigs, soft: $${Number(intent.softCapUSD) / 1e8}, hard: $${Number(intent.hardCapUSD) / 1e8})`);
169
+ }
170
+ if (features.length === 0) {
171
+ return "No registries enabled";
172
+ }
173
+ return features.join(", ");
174
+ }
175
+ function stringToProfileId(profileName) {
176
+ const bytes = new TextEncoder().encode(profileName);
177
+ const padded = new Uint8Array(32);
178
+ padded.set(bytes.slice(0, 32));
179
+ return "0x" + Array.from(padded).map((b) => b.toString(16).padStart(2, "0")).join("");
180
+ }
181
+ function serializeRegistryIntent(intent) {
182
+ return {
183
+ wallet: intent.wallet,
184
+ nonce: intent.nonce.toString(),
185
+ validUntil: intent.validUntil.toString(),
186
+ chainId: intent.chainId.toString(),
187
+ enableVendor: intent.enableVendor,
188
+ vendorProfileId: intent.vendorProfileId,
189
+ vendorProfileOwner: intent.vendorProfileOwner,
190
+ vendorGroups: intent.vendorGroups.map((g) => ({
191
+ groupId: g.groupId,
192
+ merkleRoot: g.merkleRoot
193
+ })),
194
+ sealVendorVersion: intent.sealVendorVersion,
195
+ enableVenue: intent.enableVenue,
196
+ venueProfileId: intent.venueProfileId,
197
+ venueVersion: intent.venueVersion,
198
+ venueTargets: intent.venueTargets,
199
+ venueSelectors: intent.venueSelectors,
200
+ enableOracle: intent.enableOracle,
201
+ oracleProfileId: intent.oracleProfileId,
202
+ oracleVersion: intent.oracleVersion,
203
+ oracleMaxAge: intent.oracleMaxAge,
204
+ enableApproval: intent.enableApproval,
205
+ approvalProfileId: intent.approvalProfileId,
206
+ softCapUSD: intent.softCapUSD.toString(),
207
+ hardCapUSD: intent.hardCapUSD.toString(),
208
+ approvers: intent.approvers,
209
+ requiredSigs: intent.requiredSigs,
210
+ timeoutSeconds: intent.timeoutSeconds
211
+ };
212
+ }
213
+
214
+ export {
215
+ buildRegistryBootstrapIntent,
216
+ buildVendorOnlyIntent,
217
+ buildVenueOnlyIntent,
218
+ buildOracleOnlyIntent,
219
+ buildApprovalOnlyIntent,
220
+ buildAllRegistriesIntent,
221
+ validateRegistryBootstrapIntent,
222
+ generateProfileId,
223
+ hasRegistriesEnabled,
224
+ summarizeRegistryFeatures,
225
+ stringToProfileId,
226
+ serializeRegistryIntent
227
+ };