@seapay-ai/erc3009 0.1.0 → 0.2.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/README.md +255 -49
- package/dist/build.d.ts +27 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +47 -0
- package/dist/domain.d.ts +29 -0
- package/dist/domain.d.ts.map +1 -0
- package/dist/domain.js +57 -0
- package/dist/index.d.ts +10 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -65
- package/dist/registry.d.ts +44 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +166 -0
- package/dist/types/index.d.ts +45 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +19 -0
- package/dist/verify.d.ts +14 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +36 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,80 +1,286 @@
|
|
|
1
1
|
# @seapay-ai/erc3009
|
|
2
2
|
|
|
3
|
-
TypeScript
|
|
3
|
+
Simplified TypeScript library for ERC-3009 (TransferWithAuthorization) EIP-712 signing and verification.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @seapay-ai/erc3009
|
|
9
|
-
# or
|
|
10
8
|
pnpm add @seapay-ai/erc3009
|
|
11
9
|
# or
|
|
12
|
-
|
|
10
|
+
npm install @seapay-ai/erc3009
|
|
13
11
|
```
|
|
14
12
|
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
### Basic Example
|
|
13
|
+
## Quick Start
|
|
18
14
|
|
|
19
15
|
```typescript
|
|
16
|
+
import {
|
|
17
|
+
buildTypedData,
|
|
18
|
+
buildMessage,
|
|
19
|
+
resolveDomain,
|
|
20
|
+
nowPlusSeconds,
|
|
21
|
+
} from "@seapay-ai/erc3009";
|
|
20
22
|
import { Wallet } from "ethers";
|
|
23
|
+
|
|
24
|
+
// 1. Build the message
|
|
25
|
+
const message = buildMessage({
|
|
26
|
+
from: wallet.address,
|
|
27
|
+
to: "0xRecipient...",
|
|
28
|
+
value: 1000000n, // 1 USDC (6 decimals)
|
|
29
|
+
validBefore: nowPlusSeconds(300), // Valid for 5 minutes
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// 2. Build typed data (includes domain resolution from registry)
|
|
33
|
+
const {
|
|
34
|
+
domain,
|
|
35
|
+
types,
|
|
36
|
+
message: msg,
|
|
37
|
+
} = buildTypedData({
|
|
38
|
+
chainId: 8453, // Base mainnet
|
|
39
|
+
token: "USDC",
|
|
40
|
+
message,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 3. Sign with ethers wallet
|
|
44
|
+
const signature = await wallet.signTypedData(domain, types, msg);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Core Functions
|
|
48
|
+
|
|
49
|
+
### 1. Build Message
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { buildMessage, nowPlusSeconds, randomNonce } from "@seapay-ai/erc3009";
|
|
53
|
+
|
|
54
|
+
const message = buildMessage({
|
|
55
|
+
from: "0xSender...",
|
|
56
|
+
to: "0xRecipient...",
|
|
57
|
+
value: 1000000n, // Amount in token's smallest unit
|
|
58
|
+
validAfter: 0n, // Optional, defaults to 0
|
|
59
|
+
validBefore: nowPlusSeconds(300), // Unix timestamp
|
|
60
|
+
nonce: randomNonce(), // Optional, auto-generated if not provided
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 2. Build Typed Data
|
|
65
|
+
|
|
66
|
+
Automatically resolves token info from the registry:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { buildTypedData } from "@seapay-ai/erc3009";
|
|
70
|
+
|
|
71
|
+
const typedData = buildTypedData({
|
|
72
|
+
chainId: 8453, // Base mainnet
|
|
73
|
+
token: "USDC", // Symbol from registry
|
|
74
|
+
message,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Returns:
|
|
78
|
+
// {
|
|
79
|
+
// domain: { name, version, chainId, verifyingContract },
|
|
80
|
+
// types: { TransferWithAuthorization: [...] },
|
|
81
|
+
// message: { from, to, value, ... },
|
|
82
|
+
// primaryType: "TransferWithAuthorization"
|
|
83
|
+
// }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For custom tokens not in the registry:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const typedData = buildTypedData({
|
|
90
|
+
chainId: 8453,
|
|
91
|
+
token: "0xCustomTokenAddress",
|
|
92
|
+
message,
|
|
93
|
+
domainOverrides: {
|
|
94
|
+
name: "My Token",
|
|
95
|
+
version: "1",
|
|
96
|
+
verifyingContract: "0xCustomTokenAddress",
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 3. Resolve Domain
|
|
102
|
+
|
|
103
|
+
Resolve EIP-712 domain from chain ID and token:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { resolveDomain } from "@seapay-ai/erc3009";
|
|
107
|
+
|
|
108
|
+
// Resolve USDC domain on Base
|
|
109
|
+
const domain = resolveDomain(8453, "USDC");
|
|
110
|
+
// Returns:
|
|
111
|
+
// {
|
|
112
|
+
// name: "USD Coin",
|
|
113
|
+
// version: "2",
|
|
114
|
+
// chainId: 8453,
|
|
115
|
+
// verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
116
|
+
// }
|
|
117
|
+
|
|
118
|
+
// For custom tokens
|
|
119
|
+
const customDomain = resolveDomain(8453, "0xCustomTokenAddress", {
|
|
120
|
+
name: "My Token",
|
|
121
|
+
version: "1",
|
|
122
|
+
verifyingContract: "0xCustomTokenAddress",
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This function is useful when you need just the domain object without building the complete typed data structure. It's used internally by `buildTypedData`.
|
|
127
|
+
|
|
128
|
+
### 4. Verify Signature
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { verifySignature, recoverSigner } from "@seapay-ai/erc3009";
|
|
132
|
+
|
|
133
|
+
// Verify signature matches expected signer
|
|
134
|
+
const isValid = verifySignature(domain, message, signature, expectedSigner);
|
|
135
|
+
|
|
136
|
+
// Or recover the signer address
|
|
137
|
+
const signer = recoverSigner(domain, message, signature);
|
|
138
|
+
console.log("Signed by:", signer);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Registry
|
|
142
|
+
|
|
143
|
+
The package includes a built-in registry of USDC deployments across multiple chains.
|
|
144
|
+
|
|
145
|
+
### Query the Registry
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { registry, getTokenInfo } from "@seapay-ai/erc3009";
|
|
149
|
+
|
|
150
|
+
// Get token config
|
|
151
|
+
const usdcBase = registry.getToken("USDC", 8453);
|
|
152
|
+
// => { symbol: "USDC", chainId: 8453, address: "0x...", name: "USD Coin", version: "2", decimals: 6 }
|
|
153
|
+
|
|
154
|
+
// Check if token is supported
|
|
155
|
+
if (registry.isSupported("USDC", 8453)) {
|
|
156
|
+
console.log("USDC supported on Base");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// List all chains
|
|
160
|
+
const chains = registry.listChains();
|
|
161
|
+
|
|
162
|
+
// List tokens on a chain
|
|
163
|
+
const tokens = registry.listTokensOnChain(8453);
|
|
164
|
+
|
|
165
|
+
// Get chain info
|
|
166
|
+
const baseChain = registry.getChain(8453);
|
|
167
|
+
// => { chainId: 8453, name: "Base", testnet: false }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Supported Chains
|
|
171
|
+
|
|
172
|
+
| Chain | Chain ID | Testnet |
|
|
173
|
+
| ---------------- | -------- | ------- |
|
|
174
|
+
| Ethereum | 1 | - |
|
|
175
|
+
| Sepolia | 11155111 | ✅ |
|
|
176
|
+
| Base | 8453 | - |
|
|
177
|
+
| Base Sepolia | 84532 | ✅ |
|
|
178
|
+
| Arbitrum One | 42161 | - |
|
|
179
|
+
| Arbitrum Sepolia | 421614 | ✅ |
|
|
180
|
+
| Optimism | 10 | - |
|
|
181
|
+
| Optimism Sepolia | 11155420 | ✅ |
|
|
182
|
+
| Polygon | 137 | - |
|
|
183
|
+
| Polygon Amoy | 80002 | ✅ |
|
|
184
|
+
|
|
185
|
+
### Supported Tokens
|
|
186
|
+
|
|
187
|
+
Currently includes **USDC** on all chains above.
|
|
188
|
+
|
|
189
|
+
### ⚠️ Important: Base Network Domain Names
|
|
190
|
+
|
|
191
|
+
USDC has **different EIP-712 domain names** on Base networks:
|
|
192
|
+
|
|
193
|
+
| Network | Chain ID | Domain Name |
|
|
194
|
+
| ------------ | -------- | ------------ |
|
|
195
|
+
| Base Mainnet | 8453 | `"USD Coin"` |
|
|
196
|
+
| Base Sepolia | 84532 | `"USDC"` |
|
|
197
|
+
|
|
198
|
+
The registry handles this automatically. Always use the registry to ensure correct domain parameters.
|
|
199
|
+
|
|
200
|
+
## Complete Example
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
21
203
|
import {
|
|
22
204
|
buildTypedData,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
type EIP712Domain,
|
|
27
|
-
type TransferWithAuthorization,
|
|
205
|
+
buildMessage,
|
|
206
|
+
nowPlusSeconds,
|
|
207
|
+
verifySignature,
|
|
28
208
|
} from "@seapay-ai/erc3009";
|
|
209
|
+
import { Wallet } from "ethers";
|
|
29
210
|
|
|
30
|
-
// Create
|
|
211
|
+
// Create wallet
|
|
31
212
|
const wallet = new Wallet("0x...");
|
|
32
213
|
|
|
33
|
-
//
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
214
|
+
// Build message
|
|
215
|
+
const message = buildMessage({
|
|
216
|
+
from: wallet.address,
|
|
217
|
+
to: "0xRecipient...",
|
|
218
|
+
value: 1000000n, // 1 USDC
|
|
219
|
+
validBefore: nowPlusSeconds(300), // 5 minutes
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Build typed data
|
|
223
|
+
const {
|
|
224
|
+
domain,
|
|
225
|
+
types,
|
|
226
|
+
message: msg,
|
|
227
|
+
} = buildTypedData({
|
|
37
228
|
chainId: 84532, // Base Sepolia
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
// Sign the authorization
|
|
49
|
-
const signature = await signTransferWithAuthorization(wallet, domain, message);
|
|
50
|
-
|
|
51
|
-
// Or build typed data manually
|
|
52
|
-
const typedData = buildTypedData({ domain, message });
|
|
53
|
-
const types = buildTypes();
|
|
54
|
-
// Use with wallet.signTypedData(typedData.domain, types, typedData.message)
|
|
229
|
+
token: "USDC",
|
|
230
|
+
message,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Sign
|
|
234
|
+
const signature = await wallet.signTypedData(domain, types, msg);
|
|
235
|
+
|
|
236
|
+
// Verify
|
|
237
|
+
const isValid = verifySignature(domain, message, signature, wallet.address);
|
|
238
|
+
console.log("Signature valid:", isValid);
|
|
55
239
|
```
|
|
56
240
|
|
|
57
|
-
|
|
241
|
+
## API Reference
|
|
58
242
|
|
|
59
|
-
|
|
243
|
+
### Build Functions
|
|
60
244
|
|
|
61
|
-
-
|
|
62
|
-
-
|
|
245
|
+
- **`buildMessage(params)`** - Create TransferWithAuthorization message
|
|
246
|
+
- **`buildTypedData(params)`** - Create complete EIP-712 typed data with domain resolution
|
|
63
247
|
|
|
64
|
-
|
|
248
|
+
### Domain Resolution
|
|
65
249
|
|
|
66
|
-
-
|
|
67
|
-
- `buildTypes()`: Returns the EIP-712 types for TransferWithAuthorization
|
|
68
|
-
- `buildMessage(message: TransferWithAuthorization)`: Normalizes message values
|
|
69
|
-
- `buildTypedData(params)`: Builds complete typed data for signing
|
|
70
|
-
- `signTransferWithAuthorization(wallet, domain, message)`: Convenience function to sign
|
|
71
|
-
- `message_5_minutes(from, to, value)`: Creates a message valid for 5 minutes
|
|
72
|
-
- `USDC_Domain()`: Returns USDC domain for Base Sepolia (example)
|
|
250
|
+
- **`resolveDomain(chainId, token, domainOverrides?)`** - Resolve EIP-712 domain from chain and token
|
|
73
251
|
|
|
74
|
-
|
|
252
|
+
### Verification Functions
|
|
75
253
|
|
|
76
|
-
-
|
|
77
|
-
-
|
|
254
|
+
- **`verifySignature(domain, message, signature, expectedSigner)`** - Verify signature
|
|
255
|
+
- **`recoverSigner(domain, message, signature)`** - Recover signer address
|
|
256
|
+
|
|
257
|
+
### Registry Functions
|
|
258
|
+
|
|
259
|
+
- **`getTokenInfo(symbol, chainId)`** - Get token configuration
|
|
260
|
+
- **`registry.getToken(symbol, chainId)`** - Get token config
|
|
261
|
+
- **`registry.getChain(chainId)`** - Get chain config
|
|
262
|
+
- **`registry.listChains()`** - List all chains
|
|
263
|
+
- **`registry.listChainIds()`** - List all chain IDs
|
|
264
|
+
- **`registry.isSupported(symbol, chainId)`** - Check if token is supported
|
|
265
|
+
- **`registry.listTokensOnChain(chainId)`** - List tokens on a chain
|
|
266
|
+
|
|
267
|
+
### Utility Functions
|
|
268
|
+
|
|
269
|
+
- **`randomNonce()`** - Generate random bytes32 nonce
|
|
270
|
+
- **`nowPlusSeconds(seconds)`** - Get Unix timestamp N seconds from now
|
|
271
|
+
- **`nowSeconds()`** - Get current Unix timestamp
|
|
272
|
+
|
|
273
|
+
## TypeScript Types
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import type {
|
|
277
|
+
TransferWithAuthorization,
|
|
278
|
+
EIP712Domain,
|
|
279
|
+
TypedData,
|
|
280
|
+
TokenConfig,
|
|
281
|
+
ChainConfig,
|
|
282
|
+
} from "@seapay-ai/erc3009";
|
|
283
|
+
```
|
|
78
284
|
|
|
79
285
|
## License
|
|
80
286
|
|
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TransferWithAuthorization, EIP712Domain, TypedData } from "./types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build TransferWithAuthorization message
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildMessage(params: {
|
|
6
|
+
from: string;
|
|
7
|
+
to: string;
|
|
8
|
+
value: bigint;
|
|
9
|
+
validAfter?: bigint;
|
|
10
|
+
validBefore: bigint;
|
|
11
|
+
nonce?: string;
|
|
12
|
+
}): TransferWithAuthorization;
|
|
13
|
+
/**
|
|
14
|
+
* Build complete EIP-712 typed data for signing
|
|
15
|
+
*
|
|
16
|
+
* @param chainId - Chain ID (e.g. 8453 for Base)
|
|
17
|
+
* @param token - Token symbol (e.g. "USDC") or contract address
|
|
18
|
+
* @param message - TransferWithAuthorization message
|
|
19
|
+
* @param domainOverrides - Optional domain parameter overrides for custom tokens
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildTypedData(params: {
|
|
22
|
+
chainId: number;
|
|
23
|
+
token: string;
|
|
24
|
+
message: TransferWithAuthorization;
|
|
25
|
+
domainOverrides?: Partial<EIP712Domain>;
|
|
26
|
+
}): TypedData;
|
|
27
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,YAAY,EACZ,SAAS,EACV,MAAM,kBAAkB,CAAC;AAgB1B;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,yBAAyB,CAS5B;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,yBAAyB,CAAC;IACnC,eAAe,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CACzC,GAAG,SAAS,CAcZ"}
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { randomNonce } from "./utils.js";
|
|
2
|
+
import { resolveDomain } from "./domain.js";
|
|
3
|
+
/**
|
|
4
|
+
* ERC-3009 TransferWithAuthorization type definition
|
|
5
|
+
*/
|
|
6
|
+
const TRANSFER_WITH_AUTHORIZATION_TYPE = [
|
|
7
|
+
{ name: "from", type: "address" },
|
|
8
|
+
{ name: "to", type: "address" },
|
|
9
|
+
{ name: "value", type: "uint256" },
|
|
10
|
+
{ name: "validAfter", type: "uint256" },
|
|
11
|
+
{ name: "validBefore", type: "uint256" },
|
|
12
|
+
{ name: "nonce", type: "bytes32" },
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Build TransferWithAuthorization message
|
|
16
|
+
*/
|
|
17
|
+
export function buildMessage(params) {
|
|
18
|
+
return {
|
|
19
|
+
from: params.from,
|
|
20
|
+
to: params.to,
|
|
21
|
+
value: params.value,
|
|
22
|
+
validAfter: params.validAfter ?? 0n,
|
|
23
|
+
validBefore: params.validBefore,
|
|
24
|
+
nonce: params.nonce ?? randomNonce(),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build complete EIP-712 typed data for signing
|
|
29
|
+
*
|
|
30
|
+
* @param chainId - Chain ID (e.g. 8453 for Base)
|
|
31
|
+
* @param token - Token symbol (e.g. "USDC") or contract address
|
|
32
|
+
* @param message - TransferWithAuthorization message
|
|
33
|
+
* @param domainOverrides - Optional domain parameter overrides for custom tokens
|
|
34
|
+
*/
|
|
35
|
+
export function buildTypedData(params) {
|
|
36
|
+
const { chainId, token, message, domainOverrides } = params;
|
|
37
|
+
// Resolve domain using the resolveDomain function
|
|
38
|
+
const domain = resolveDomain(chainId, token, domainOverrides);
|
|
39
|
+
return {
|
|
40
|
+
domain,
|
|
41
|
+
types: {
|
|
42
|
+
TransferWithAuthorization: TRANSFER_WITH_AUTHORIZATION_TYPE,
|
|
43
|
+
},
|
|
44
|
+
message,
|
|
45
|
+
primaryType: "TransferWithAuthorization",
|
|
46
|
+
};
|
|
47
|
+
}
|
package/dist/domain.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { EIP712Domain } from "./types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve EIP-712 domain from chain ID and token
|
|
4
|
+
*
|
|
5
|
+
* @param chainId - Chain ID (e.g. 8453 for Base)
|
|
6
|
+
* @param token - Token symbol (e.g. "USDC") or contract address (0x...)
|
|
7
|
+
* @param domainOverrides - Optional domain parameter overrides for custom tokens
|
|
8
|
+
* @returns EIP-712 Domain object
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Resolve USDC on Base
|
|
12
|
+
* const domain = resolveDomain(8453, "USDC");
|
|
13
|
+
* // Returns:
|
|
14
|
+
* // {
|
|
15
|
+
* // name: "USD Coin",
|
|
16
|
+
* // version: "2",
|
|
17
|
+
* // chainId: 8453,
|
|
18
|
+
* // verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
19
|
+
* // }
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Resolve custom token with overrides
|
|
23
|
+
* const domain = resolveDomain(8453, "0x123...", {
|
|
24
|
+
* name: "My Token",
|
|
25
|
+
* version: "1"
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveDomain(chainId: number, token: string, domainOverrides?: Partial<EIP712Domain>): EIP712Domain;
|
|
29
|
+
//# sourceMappingURL=domain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../src/domain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,eAAe,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GACtC,YAAY,CAmCd"}
|
package/dist/domain.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getTokenInfo } from "./registry.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve EIP-712 domain from chain ID and token
|
|
4
|
+
*
|
|
5
|
+
* @param chainId - Chain ID (e.g. 8453 for Base)
|
|
6
|
+
* @param token - Token symbol (e.g. "USDC") or contract address (0x...)
|
|
7
|
+
* @param domainOverrides - Optional domain parameter overrides for custom tokens
|
|
8
|
+
* @returns EIP-712 Domain object
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Resolve USDC on Base
|
|
12
|
+
* const domain = resolveDomain(8453, "USDC");
|
|
13
|
+
* // Returns:
|
|
14
|
+
* // {
|
|
15
|
+
* // name: "USD Coin",
|
|
16
|
+
* // version: "2",
|
|
17
|
+
* // chainId: 8453,
|
|
18
|
+
* // verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
19
|
+
* // }
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Resolve custom token with overrides
|
|
23
|
+
* const domain = resolveDomain(8453, "0x123...", {
|
|
24
|
+
* name: "My Token",
|
|
25
|
+
* version: "1"
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
28
|
+
export function resolveDomain(chainId, token, domainOverrides) {
|
|
29
|
+
let domain;
|
|
30
|
+
if (token.startsWith("0x")) {
|
|
31
|
+
// Custom token address - use overrides or fail
|
|
32
|
+
if (!domainOverrides?.name || !domainOverrides?.verifyingContract) {
|
|
33
|
+
throw new Error("Custom token address requires domain overrides (name, verifyingContract)");
|
|
34
|
+
}
|
|
35
|
+
domain = {
|
|
36
|
+
name: domainOverrides.name,
|
|
37
|
+
version: domainOverrides.version ?? "1",
|
|
38
|
+
chainId,
|
|
39
|
+
verifyingContract: domainOverrides.verifyingContract,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Try registry lookup
|
|
44
|
+
const tokenInfo = getTokenInfo(token, chainId);
|
|
45
|
+
if (!tokenInfo) {
|
|
46
|
+
throw new Error(`Token ${token} not found in registry for chain ${chainId}. Use domainOverrides for custom tokens.`);
|
|
47
|
+
}
|
|
48
|
+
domain = {
|
|
49
|
+
name: tokenInfo.name,
|
|
50
|
+
version: tokenInfo.version,
|
|
51
|
+
chainId: tokenInfo.chainId,
|
|
52
|
+
verifyingContract: tokenInfo.address,
|
|
53
|
+
...domainOverrides, // Allow overriding registry values
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return domain;
|
|
57
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,38 +1,13 @@
|
|
|
1
|
-
import { type TypedDataDomain, type TypedDataField, Wallet } from "ethers";
|
|
2
|
-
export type TransferWithAuthorization = {
|
|
3
|
-
from: string;
|
|
4
|
-
to: string;
|
|
5
|
-
value: bigint;
|
|
6
|
-
validAfter: bigint;
|
|
7
|
-
validBefore: bigint;
|
|
8
|
-
nonce: string;
|
|
9
|
-
};
|
|
10
|
-
export type EIP712Domain = {
|
|
11
|
-
name: string;
|
|
12
|
-
version: string;
|
|
13
|
-
chainId: bigint | number;
|
|
14
|
-
verifyingContract: string;
|
|
15
|
-
};
|
|
16
|
-
export declare const TRANSFER_WITH_AUTHORIZATION_TYPE = "TransferWithAuthorization";
|
|
17
|
-
export declare function buildDomain(domain: EIP712Domain): TypedDataDomain;
|
|
18
|
-
export declare function USDC_Domain(): EIP712Domain;
|
|
19
|
-
export declare function message_5_minutes(from: string, to: string, value: bigint): TransferWithAuthorization;
|
|
20
|
-
export declare function buildTypes(): Record<string, TypedDataField[]>;
|
|
21
|
-
export declare function buildMessage(message: TransferWithAuthorization): TransferWithAuthorization;
|
|
22
1
|
/**
|
|
23
|
-
*
|
|
2
|
+
* @seapay-ai/erc3009
|
|
3
|
+
*
|
|
4
|
+
* Simplified ERC-3009 (TransferWithAuthorization) library
|
|
5
|
+
* for building, signing, and verifying EIP-712 typed data.
|
|
24
6
|
*/
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
message: TransferWithAuthorization;
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* Convenience helper to sign the ERC-3009 TransferWithAuthorization payload.
|
|
35
|
-
* Requires an ethers Wallet (or Signer with signTypedData support).
|
|
36
|
-
*/
|
|
37
|
-
export declare function signTransferWithAuthorization(wallet: Wallet, domain: EIP712Domain, message: TransferWithAuthorization): Promise<string>;
|
|
7
|
+
export type { TransferWithAuthorization, EIP712Domain, TokenConfig, ChainConfig, } from "./types/index.js";
|
|
8
|
+
export { registry, getTokenInfo } from "./registry.js";
|
|
9
|
+
export { resolveDomain } from "./domain.js";
|
|
10
|
+
export { buildTypedData, buildMessage } from "./build.js";
|
|
11
|
+
export { verifySignature, recoverSigner } from "./verify.js";
|
|
12
|
+
export { randomNonce, nowPlusSeconds } from "./utils.js";
|
|
38
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,yBAAyB,EACzB,YAAY,EACZ,WAAW,EACX,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGvD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,67 +1,16 @@
|
|
|
1
|
-
import { randomBytes, hexlify, } from "ethers";
|
|
2
|
-
const TRANSFER_WITH_AUTHORIZATION_FIELDS = [
|
|
3
|
-
{ name: "from", type: "address" },
|
|
4
|
-
{ name: "to", type: "address" },
|
|
5
|
-
{ name: "value", type: "uint256" },
|
|
6
|
-
{ name: "validAfter", type: "uint256" },
|
|
7
|
-
{ name: "validBefore", type: "uint256" },
|
|
8
|
-
{ name: "nonce", type: "bytes32" },
|
|
9
|
-
];
|
|
10
|
-
export const TRANSFER_WITH_AUTHORIZATION_TYPE = "TransferWithAuthorization";
|
|
11
|
-
export function buildDomain(domain) {
|
|
12
|
-
return {
|
|
13
|
-
name: domain.name,
|
|
14
|
-
version: domain.version,
|
|
15
|
-
chainId: domain.chainId,
|
|
16
|
-
verifyingContract: domain.verifyingContract,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
export function USDC_Domain() {
|
|
20
|
-
return {
|
|
21
|
-
name: "USD Coin",
|
|
22
|
-
version: "2",
|
|
23
|
-
chainId: 84532,
|
|
24
|
-
verifyingContract: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
export function message_5_minutes(from, to, value) {
|
|
28
|
-
return {
|
|
29
|
-
from: from,
|
|
30
|
-
to: to,
|
|
31
|
-
value: value,
|
|
32
|
-
validAfter: 0n,
|
|
33
|
-
validBefore: BigInt(Math.floor(Date.now() / 1000) + 300),
|
|
34
|
-
nonce: hexlify(randomBytes(32)),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
export function buildTypes() {
|
|
38
|
-
return {
|
|
39
|
-
[TRANSFER_WITH_AUTHORIZATION_TYPE]: TRANSFER_WITH_AUTHORIZATION_FIELDS,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
export function buildMessage(message) {
|
|
43
|
-
return {
|
|
44
|
-
...message,
|
|
45
|
-
value: BigInt(message.value),
|
|
46
|
-
validAfter: BigInt(message.validAfter),
|
|
47
|
-
validBefore: BigInt(message.validBefore),
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
1
|
/**
|
|
51
|
-
*
|
|
2
|
+
* @seapay-ai/erc3009
|
|
3
|
+
*
|
|
4
|
+
* Simplified ERC-3009 (TransferWithAuthorization) library
|
|
5
|
+
* for building, signing, and verifying EIP-712 typed data.
|
|
52
6
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
*/
|
|
64
|
-
export async function signTransferWithAuthorization(wallet, domain, message) {
|
|
65
|
-
const typed = buildTypedData({ domain, message });
|
|
66
|
-
return await wallet.signTypedData(typed.domain, typed.types, typed.message);
|
|
67
|
-
}
|
|
7
|
+
// === Registry ===
|
|
8
|
+
export { registry, getTokenInfo } from "./registry.js";
|
|
9
|
+
// === Domain Resolution ===
|
|
10
|
+
export { resolveDomain } from "./domain.js";
|
|
11
|
+
// === Build Functions ===
|
|
12
|
+
export { buildTypedData, buildMessage } from "./build.js";
|
|
13
|
+
// === Verify Functions ===
|
|
14
|
+
export { verifySignature, recoverSigner } from "./verify.js";
|
|
15
|
+
// === Utils ===
|
|
16
|
+
export { randomNonce, nowPlusSeconds } from "./utils.js";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { TokenConfig, ChainConfig } from "./types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Chain registry - supported chains
|
|
4
|
+
*/
|
|
5
|
+
export declare const CHAINS: Record<number, ChainConfig>;
|
|
6
|
+
/**
|
|
7
|
+
* Token registry - USDC addresses and domain parameters per chain
|
|
8
|
+
* Note: Base mainnet uses "USD Coin" while Base Sepolia uses "USDC"
|
|
9
|
+
*/
|
|
10
|
+
export declare const TOKENS: Record<string, Record<number, TokenConfig>>;
|
|
11
|
+
/**
|
|
12
|
+
* Get token information for a given chain
|
|
13
|
+
*/
|
|
14
|
+
export declare function getTokenInfo(tokenSymbol: string, chainId: number): TokenConfig | null;
|
|
15
|
+
/**
|
|
16
|
+
* Registry utilities
|
|
17
|
+
*/
|
|
18
|
+
export declare const registry: {
|
|
19
|
+
/**
|
|
20
|
+
* Get token configuration
|
|
21
|
+
*/
|
|
22
|
+
readonly getToken: (symbol: string, chainId: number) => TokenConfig | null;
|
|
23
|
+
/**
|
|
24
|
+
* Get chain configuration
|
|
25
|
+
*/
|
|
26
|
+
readonly getChain: (chainId: number) => ChainConfig | null;
|
|
27
|
+
/**
|
|
28
|
+
* List all supported chains
|
|
29
|
+
*/
|
|
30
|
+
readonly listChains: () => ChainConfig[];
|
|
31
|
+
/**
|
|
32
|
+
* List all supported chain IDs
|
|
33
|
+
*/
|
|
34
|
+
readonly listChainIds: () => number[];
|
|
35
|
+
/**
|
|
36
|
+
* Check if a token is supported on a chain
|
|
37
|
+
*/
|
|
38
|
+
readonly isSupported: (symbol: string, chainId: number) => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* List all tokens on a chain
|
|
41
|
+
*/
|
|
42
|
+
readonly listTokensOnChain: (chainId: number) => TokenConfig[];
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEjE;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAW9C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAwF9D,CAAC;AAEF;;GAEG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,WAAW,GAAG,IAAI,CAMpB;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ;IACnB;;OAEG;gCACgB,MAAM,WAAW,MAAM,KAAG,WAAW,GAAG,IAAI;IAI/D;;OAEG;iCACiB,MAAM,KAAG,WAAW,GAAG,IAAI;IAI/C;;OAEG;+BACa,WAAW,EAAE;IAI7B;;OAEG;iCACe,MAAM,EAAE;IAI1B;;OAEG;mCACmB,MAAM,WAAW,MAAM,KAAG,OAAO;IAIvD;;OAEG;0CAC0B,MAAM,KAAG,WAAW,EAAE;CAU3C,CAAC"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain registry - supported chains
|
|
3
|
+
*/
|
|
4
|
+
export const CHAINS = {
|
|
5
|
+
1: { chainId: 1, name: "Ethereum", testnet: false },
|
|
6
|
+
11155111: { chainId: 11155111, name: "Sepolia", testnet: true },
|
|
7
|
+
8453: { chainId: 8453, name: "Base", testnet: false },
|
|
8
|
+
84532: { chainId: 84532, name: "Base Sepolia", testnet: true },
|
|
9
|
+
42161: { chainId: 42161, name: "Arbitrum One", testnet: false },
|
|
10
|
+
421614: { chainId: 421614, name: "Arbitrum Sepolia", testnet: true },
|
|
11
|
+
10: { chainId: 10, name: "Optimism", testnet: false },
|
|
12
|
+
11155420: { chainId: 11155420, name: "Optimism Sepolia", testnet: true },
|
|
13
|
+
137: { chainId: 137, name: "Polygon", testnet: false },
|
|
14
|
+
80002: { chainId: 80002, name: "Polygon Amoy", testnet: true },
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Token registry - USDC addresses and domain parameters per chain
|
|
18
|
+
* Note: Base mainnet uses "USD Coin" while Base Sepolia uses "USDC"
|
|
19
|
+
*/
|
|
20
|
+
export const TOKENS = {
|
|
21
|
+
USDC: {
|
|
22
|
+
// Ethereum
|
|
23
|
+
1: {
|
|
24
|
+
symbol: "USDC",
|
|
25
|
+
chainId: 1,
|
|
26
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
27
|
+
name: "USD Coin",
|
|
28
|
+
version: "2",
|
|
29
|
+
decimals: 6,
|
|
30
|
+
},
|
|
31
|
+
11155111: {
|
|
32
|
+
symbol: "USDC",
|
|
33
|
+
chainId: 11155111,
|
|
34
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
35
|
+
name: "USD Coin",
|
|
36
|
+
version: "2",
|
|
37
|
+
decimals: 6,
|
|
38
|
+
},
|
|
39
|
+
// Base
|
|
40
|
+
8453: {
|
|
41
|
+
symbol: "USDC",
|
|
42
|
+
chainId: 8453,
|
|
43
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
44
|
+
name: "USD Coin", // Important: Base mainnet uses "USD Coin"
|
|
45
|
+
version: "2",
|
|
46
|
+
decimals: 6,
|
|
47
|
+
},
|
|
48
|
+
84532: {
|
|
49
|
+
symbol: "USDC",
|
|
50
|
+
chainId: 84532,
|
|
51
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
52
|
+
name: "USDC", // Important: Base Sepolia uses "USDC"
|
|
53
|
+
version: "2",
|
|
54
|
+
decimals: 6,
|
|
55
|
+
},
|
|
56
|
+
// Arbitrum
|
|
57
|
+
42161: {
|
|
58
|
+
symbol: "USDC",
|
|
59
|
+
chainId: 42161,
|
|
60
|
+
address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
61
|
+
name: "USD Coin",
|
|
62
|
+
version: "2",
|
|
63
|
+
decimals: 6,
|
|
64
|
+
},
|
|
65
|
+
421614: {
|
|
66
|
+
symbol: "USDC",
|
|
67
|
+
chainId: 421614,
|
|
68
|
+
address: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
69
|
+
name: "USD Coin",
|
|
70
|
+
version: "2",
|
|
71
|
+
decimals: 6,
|
|
72
|
+
},
|
|
73
|
+
// Optimism
|
|
74
|
+
10: {
|
|
75
|
+
symbol: "USDC",
|
|
76
|
+
chainId: 10,
|
|
77
|
+
address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
78
|
+
name: "USD Coin",
|
|
79
|
+
version: "2",
|
|
80
|
+
decimals: 6,
|
|
81
|
+
},
|
|
82
|
+
11155420: {
|
|
83
|
+
symbol: "USDC",
|
|
84
|
+
chainId: 11155420,
|
|
85
|
+
address: "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
|
|
86
|
+
name: "USD Coin",
|
|
87
|
+
version: "2",
|
|
88
|
+
decimals: 6,
|
|
89
|
+
},
|
|
90
|
+
// Polygon
|
|
91
|
+
137: {
|
|
92
|
+
symbol: "USDC",
|
|
93
|
+
chainId: 137,
|
|
94
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
95
|
+
name: "USD Coin",
|
|
96
|
+
version: "2",
|
|
97
|
+
decimals: 6,
|
|
98
|
+
},
|
|
99
|
+
80002: {
|
|
100
|
+
symbol: "USDC",
|
|
101
|
+
chainId: 80002,
|
|
102
|
+
address: "0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582",
|
|
103
|
+
name: "USD Coin",
|
|
104
|
+
version: "2",
|
|
105
|
+
decimals: 6,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Get token information for a given chain
|
|
111
|
+
*/
|
|
112
|
+
export function getTokenInfo(tokenSymbol, chainId) {
|
|
113
|
+
const token = TOKENS[tokenSymbol.toUpperCase()];
|
|
114
|
+
if (!token)
|
|
115
|
+
return null;
|
|
116
|
+
const config = token[chainId];
|
|
117
|
+
return config || null;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Registry utilities
|
|
121
|
+
*/
|
|
122
|
+
export const registry = {
|
|
123
|
+
/**
|
|
124
|
+
* Get token configuration
|
|
125
|
+
*/
|
|
126
|
+
getToken: (symbol, chainId) => {
|
|
127
|
+
return getTokenInfo(symbol, chainId);
|
|
128
|
+
},
|
|
129
|
+
/**
|
|
130
|
+
* Get chain configuration
|
|
131
|
+
*/
|
|
132
|
+
getChain: (chainId) => {
|
|
133
|
+
return CHAINS[chainId] || null;
|
|
134
|
+
},
|
|
135
|
+
/**
|
|
136
|
+
* List all supported chains
|
|
137
|
+
*/
|
|
138
|
+
listChains: () => {
|
|
139
|
+
return Object.values(CHAINS);
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* List all supported chain IDs
|
|
143
|
+
*/
|
|
144
|
+
listChainIds: () => {
|
|
145
|
+
return Object.keys(CHAINS).map(Number);
|
|
146
|
+
},
|
|
147
|
+
/**
|
|
148
|
+
* Check if a token is supported on a chain
|
|
149
|
+
*/
|
|
150
|
+
isSupported: (symbol, chainId) => {
|
|
151
|
+
return getTokenInfo(symbol, chainId) !== null;
|
|
152
|
+
},
|
|
153
|
+
/**
|
|
154
|
+
* List all tokens on a chain
|
|
155
|
+
*/
|
|
156
|
+
listTokensOnChain: (chainId) => {
|
|
157
|
+
const tokens = [];
|
|
158
|
+
for (const symbol of Object.keys(TOKENS)) {
|
|
159
|
+
const config = TOKENS[symbol][chainId];
|
|
160
|
+
if (config) {
|
|
161
|
+
tokens.push(config);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return tokens;
|
|
165
|
+
},
|
|
166
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { TypedDataDomain, TypedDataField } from "ethers";
|
|
2
|
+
/**
|
|
3
|
+
* ERC-3009 TransferWithAuthorization message
|
|
4
|
+
*/
|
|
5
|
+
export interface TransferWithAuthorization {
|
|
6
|
+
from: string;
|
|
7
|
+
to: string;
|
|
8
|
+
value: bigint;
|
|
9
|
+
validAfter: bigint;
|
|
10
|
+
validBefore: bigint;
|
|
11
|
+
nonce: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* EIP-712 Domain
|
|
15
|
+
*/
|
|
16
|
+
export type EIP712Domain = TypedDataDomain;
|
|
17
|
+
/**
|
|
18
|
+
* Token configuration
|
|
19
|
+
*/
|
|
20
|
+
export interface TokenConfig {
|
|
21
|
+
symbol: string;
|
|
22
|
+
chainId: number;
|
|
23
|
+
address: string;
|
|
24
|
+
name: string;
|
|
25
|
+
version: string;
|
|
26
|
+
decimals: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Chain configuration
|
|
30
|
+
*/
|
|
31
|
+
export interface ChainConfig {
|
|
32
|
+
chainId: number;
|
|
33
|
+
name: string;
|
|
34
|
+
testnet: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Complete EIP-712 typed data structure
|
|
38
|
+
*/
|
|
39
|
+
export interface TypedData {
|
|
40
|
+
domain: EIP712Domain;
|
|
41
|
+
types: Record<string, TypedDataField[]>;
|
|
42
|
+
message: TransferWithAuthorization;
|
|
43
|
+
primaryType: string;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;IACxC,OAAO,EAAE,yBAAyB,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a random bytes32 nonce for TransferWithAuthorization
|
|
3
|
+
*/
|
|
4
|
+
export declare function randomNonce(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Get current Unix timestamp + N seconds (for validBefore)
|
|
7
|
+
*/
|
|
8
|
+
export declare function nowPlusSeconds(seconds: number): bigint;
|
|
9
|
+
/**
|
|
10
|
+
* Get current Unix timestamp in seconds
|
|
11
|
+
*/
|
|
12
|
+
export declare function nowSeconds(): bigint;
|
|
13
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { randomBytes, hexlify } from "ethers";
|
|
2
|
+
/**
|
|
3
|
+
* Generate a random bytes32 nonce for TransferWithAuthorization
|
|
4
|
+
*/
|
|
5
|
+
export function randomNonce() {
|
|
6
|
+
return hexlify(randomBytes(32));
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Get current Unix timestamp + N seconds (for validBefore)
|
|
10
|
+
*/
|
|
11
|
+
export function nowPlusSeconds(seconds) {
|
|
12
|
+
return BigInt(Math.floor(Date.now() / 1000) + seconds);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get current Unix timestamp in seconds
|
|
16
|
+
*/
|
|
17
|
+
export function nowSeconds() {
|
|
18
|
+
return BigInt(Math.floor(Date.now() / 1000));
|
|
19
|
+
}
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TransferWithAuthorization, EIP712Domain } from "./types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Recover the signer address from a signature
|
|
4
|
+
*/
|
|
5
|
+
export declare function recoverSigner(domain: EIP712Domain, message: TransferWithAuthorization, signature: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Verify that a signature was created by the expected signer
|
|
8
|
+
*/
|
|
9
|
+
export declare function verifySignature(domain: EIP712Domain, message: TransferWithAuthorization, signature: string, expectedSigner: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Get the EIP-712 hash for a message (useful for debugging)
|
|
12
|
+
*/
|
|
13
|
+
export declare function getMessageHash(domain: EIP712Domain, message: TransferWithAuthorization): string;
|
|
14
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,YAAY,EACb,MAAM,kBAAkB,CAAC;AAc1B;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,yBAAyB,EAClC,SAAS,EAAE,MAAM,GAChB,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,yBAAyB,EAClC,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACrB,OAAO,CAOT;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,yBAAyB,GACjC,MAAM,CAMR"}
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { TypedDataEncoder, verifyTypedData } from "ethers";
|
|
2
|
+
/**
|
|
3
|
+
* ERC-3009 type definition for signature recovery
|
|
4
|
+
*/
|
|
5
|
+
const TRANSFER_WITH_AUTHORIZATION_TYPE = [
|
|
6
|
+
{ name: "from", type: "address" },
|
|
7
|
+
{ name: "to", type: "address" },
|
|
8
|
+
{ name: "value", type: "uint256" },
|
|
9
|
+
{ name: "validAfter", type: "uint256" },
|
|
10
|
+
{ name: "validBefore", type: "uint256" },
|
|
11
|
+
{ name: "nonce", type: "bytes32" },
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Recover the signer address from a signature
|
|
15
|
+
*/
|
|
16
|
+
export function recoverSigner(domain, message, signature) {
|
|
17
|
+
return verifyTypedData(domain, { TransferWithAuthorization: TRANSFER_WITH_AUTHORIZATION_TYPE }, message, signature);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Verify that a signature was created by the expected signer
|
|
21
|
+
*/
|
|
22
|
+
export function verifySignature(domain, message, signature, expectedSigner) {
|
|
23
|
+
try {
|
|
24
|
+
const recovered = recoverSigner(domain, message, signature);
|
|
25
|
+
return recovered.toLowerCase() === expectedSigner.toLowerCase();
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the EIP-712 hash for a message (useful for debugging)
|
|
33
|
+
*/
|
|
34
|
+
export function getMessageHash(domain, message) {
|
|
35
|
+
return TypedDataEncoder.hash(domain, { TransferWithAuthorization: TRANSFER_WITH_AUTHORIZATION_TYPE }, message);
|
|
36
|
+
}
|