@seapay-ai/erc3009 0.1.1 → 0.2.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/README.md +171 -184
- 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 +8 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -31
- 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 +44 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -3
- 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 +8 -7
- package/dist/api/index.d.ts +0 -2
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/index.js +0 -1
- package/dist/api/prepare.d.ts +0 -47
- package/dist/api/prepare.d.ts.map +0 -1
- package/dist/api/prepare.js +0 -57
- package/dist/core.d.ts +0 -14
- package/dist/core.d.ts.map +0 -1
- package/dist/core.js +0 -13
- package/dist/domain/index.d.ts +0 -3
- package/dist/domain/index.d.ts.map +0 -1
- package/dist/domain/index.js +0 -2
- package/dist/domain/normalize.d.ts +0 -9
- package/dist/domain/normalize.d.ts.map +0 -1
- package/dist/domain/normalize.js +0 -25
- package/dist/domain/resolveDomain.d.ts +0 -15
- package/dist/domain/resolveDomain.d.ts.map +0 -1
- package/dist/domain/resolveDomain.js +0 -47
- package/dist/erc3009/buildTypes.d.ts +0 -6
- package/dist/erc3009/buildTypes.d.ts.map +0 -1
- package/dist/erc3009/buildTypes.js +0 -11
- package/dist/erc3009/constants.d.ts +0 -7
- package/dist/erc3009/constants.d.ts.map +0 -1
- package/dist/erc3009/constants.js +0 -6
- package/dist/erc3009/index.d.ts +0 -7
- package/dist/erc3009/index.d.ts.map +0 -1
- package/dist/erc3009/index.js +0 -6
- package/dist/erc3009/message.d.ts +0 -21
- package/dist/erc3009/message.d.ts.map +0 -1
- package/dist/erc3009/message.js +0 -29
- package/dist/erc3009/sign.d.ts +0 -7
- package/dist/erc3009/sign.d.ts.map +0 -1
- package/dist/erc3009/sign.js +0 -8
- package/dist/erc3009/typedData.d.ts +0 -15
- package/dist/erc3009/typedData.d.ts.map +0 -1
- package/dist/erc3009/typedData.js +0 -21
- package/dist/erc3009/verify.d.ts +0 -10
- package/dist/erc3009/verify.d.ts.map +0 -1
- package/dist/erc3009/verify.js +0 -17
- package/dist/registry/chains.d.ts +0 -22
- package/dist/registry/chains.d.ts.map +0 -1
- package/dist/registry/chains.js +0 -104
- package/dist/registry/index.d.ts +0 -4
- package/dist/registry/index.d.ts.map +0 -1
- package/dist/registry/index.js +0 -6
- package/dist/registry/registry.d.ts +0 -38
- package/dist/registry/registry.d.ts.map +0 -1
- package/dist/registry/registry.js +0 -73
- package/dist/registry/tokens/index.d.ts +0 -11
- package/dist/registry/tokens/index.d.ts.map +0 -1
- package/dist/registry/tokens/index.js +0 -14
- package/dist/registry/tokens/usdc.d.ts +0 -27
- package/dist/registry/tokens/usdc.d.ts.map +0 -1
- package/dist/registry/tokens/usdc.js +0 -104
- package/dist/types/domain.d.ts +0 -24
- package/dist/types/domain.d.ts.map +0 -1
- package/dist/types/domain.js +0 -1
- package/dist/types/erc3009.d.ts +0 -38
- package/dist/types/erc3009.d.ts.map +0 -1
- package/dist/types/erc3009.js +0 -15
- package/dist/types/registry.d.ts +0 -35
- package/dist/types/registry.d.ts.map +0 -1
- package/dist/types/registry.js +0 -1
- package/dist/utils/hex.d.ts +0 -13
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js +0 -19
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -3
- package/dist/utils/nonce.d.ts +0 -5
- package/dist/utils/nonce.d.ts.map +0 -1
- package/dist/utils/nonce.js +0 -7
- package/dist/utils/time.d.ts +0 -13
- package/dist/utils/time.d.ts.map +0 -1
- package/dist/utils/time.js +0 -18
package/README.md
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
# @seapay-ai/erc3009
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- ✅ **Multi-chain support**: Base, Ethereum, Arbitrum, Optimism, Polygon (mainnet + testnets)
|
|
8
|
-
- ✅ **Token registry**: Pre-configured USDC addresses and domain parameters for all chains
|
|
9
|
-
- ✅ **Type-safe**: Full TypeScript support with strict types
|
|
10
|
-
- ✅ **Ergonomic API**: One-liner `prepare()` for common use cases
|
|
11
|
-
- ✅ **Override support**: Customize domain parameters for custom tokens
|
|
12
|
-
- ✅ **ethers.js v6**: Built on ethers v6 for signing and verification
|
|
3
|
+
Simplified TypeScript library for ERC-3009 (TransferWithAuthorization) EIP-712 signing and verification.
|
|
13
4
|
|
|
14
5
|
## Installation
|
|
15
6
|
|
|
@@ -22,145 +13,161 @@ npm install @seapay-ai/erc3009
|
|
|
22
13
|
## Quick Start
|
|
23
14
|
|
|
24
15
|
```typescript
|
|
25
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
buildTypedData,
|
|
18
|
+
buildMessage,
|
|
19
|
+
resolveDomain,
|
|
20
|
+
nowPlusSeconds,
|
|
21
|
+
} from "@seapay-ai/erc3009";
|
|
26
22
|
import { Wallet } from "ethers";
|
|
27
23
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
chainId: 8453, // Base
|
|
31
|
-
token: "USDC",
|
|
24
|
+
// 1. Build the message
|
|
25
|
+
const message = buildMessage({
|
|
32
26
|
from: wallet.address,
|
|
33
27
|
to: "0xRecipient...",
|
|
34
28
|
value: 1000000n, // 1 USDC (6 decimals)
|
|
35
|
-
|
|
29
|
+
validBefore: nowPlusSeconds(300), // Valid for 5 minutes
|
|
36
30
|
});
|
|
37
31
|
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Usage Examples
|
|
47
|
-
|
|
48
|
-
### 1. Ergonomic API (Recommended)
|
|
49
|
-
|
|
50
|
-
The `prepare()` function resolves the domain, builds the message, and returns everything needed:
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
import { prepare } from "@seapay-ai/erc3009";
|
|
54
|
-
|
|
55
|
-
const { domain, message, typedData } = prepare({
|
|
32
|
+
// 2. Build typed data (includes domain resolution from registry)
|
|
33
|
+
const {
|
|
34
|
+
domain,
|
|
35
|
+
types,
|
|
36
|
+
message: msg,
|
|
37
|
+
} = buildTypedData({
|
|
56
38
|
chainId: 8453, // Base mainnet
|
|
57
39
|
token: "USDC",
|
|
58
|
-
|
|
59
|
-
to: "0xRecipient...",
|
|
60
|
-
value: 1000000n, // 1 USDC
|
|
61
|
-
ttlSeconds: 300, // optional, default: 300
|
|
40
|
+
message,
|
|
62
41
|
});
|
|
63
42
|
|
|
64
|
-
// Sign
|
|
65
|
-
const
|
|
66
|
-
typedData.domain,
|
|
67
|
-
typedData.types,
|
|
68
|
-
typedData.message
|
|
69
|
-
);
|
|
43
|
+
// 3. Sign with ethers wallet
|
|
44
|
+
const signature = await wallet.signTypedData(domain, types, msg);
|
|
70
45
|
```
|
|
71
46
|
|
|
72
|
-
|
|
47
|
+
## Core Functions
|
|
73
48
|
|
|
74
|
-
|
|
49
|
+
### 1. Build Message
|
|
75
50
|
|
|
76
51
|
```typescript
|
|
77
|
-
import {
|
|
78
|
-
resolveDomain,
|
|
79
|
-
buildMessage,
|
|
80
|
-
buildTypedData,
|
|
81
|
-
erc3009,
|
|
82
|
-
} from "@seapay-ai/erc3009";
|
|
83
|
-
|
|
84
|
-
// 1. Resolve domain from registry
|
|
85
|
-
const domain = resolveDomain({
|
|
86
|
-
chainId: 8453,
|
|
87
|
-
token: "USDC",
|
|
88
|
-
});
|
|
52
|
+
import { buildMessage, nowPlusSeconds, randomNonce } from "@seapay-ai/erc3009";
|
|
89
53
|
|
|
90
|
-
// 2. Build message
|
|
91
54
|
const message = buildMessage({
|
|
92
55
|
from: "0xSender...",
|
|
93
56
|
to: "0xRecipient...",
|
|
94
|
-
value: 1000000n,
|
|
95
|
-
validAfter: 0n,
|
|
96
|
-
validBefore:
|
|
97
|
-
nonce:
|
|
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
|
|
98
61
|
});
|
|
99
|
-
|
|
100
|
-
// 3. Build typed data
|
|
101
|
-
const typedData = buildTypedData({ domain, message });
|
|
102
|
-
|
|
103
|
-
// 4. Sign
|
|
104
|
-
const signature = await erc3009.sign(wallet, domain, message);
|
|
105
62
|
```
|
|
106
63
|
|
|
107
|
-
###
|
|
64
|
+
### 2. Build Typed Data
|
|
108
65
|
|
|
109
|
-
|
|
66
|
+
Automatically resolves token info from the registry:
|
|
110
67
|
|
|
111
68
|
```typescript
|
|
112
|
-
import {
|
|
113
|
-
|
|
114
|
-
// Get token config
|
|
115
|
-
const usdcBase = getToken("USDC", 8453);
|
|
116
|
-
// => { symbol: "USDC", chainId: 8453, verifyingContract: "0x...", ... }
|
|
117
|
-
|
|
118
|
-
// List all chains
|
|
119
|
-
const chains = registry.listChains();
|
|
69
|
+
import { buildTypedData } from "@seapay-ai/erc3009";
|
|
120
70
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
71
|
+
const typedData = buildTypedData({
|
|
72
|
+
chainId: 8453, // Base mainnet
|
|
73
|
+
token: "USDC", // Symbol from registry
|
|
74
|
+
message,
|
|
75
|
+
});
|
|
125
76
|
|
|
126
|
-
//
|
|
127
|
-
|
|
77
|
+
// Returns:
|
|
78
|
+
// {
|
|
79
|
+
// domain: { name, version, chainId, verifyingContract },
|
|
80
|
+
// types: { TransferWithAuthorization: [...] },
|
|
81
|
+
// message: { from, to, value, ... },
|
|
82
|
+
// primaryType: "TransferWithAuthorization"
|
|
83
|
+
// }
|
|
128
84
|
```
|
|
129
85
|
|
|
130
|
-
### 4. Custom Tokens / Domain Overrides
|
|
131
|
-
|
|
132
86
|
For custom tokens not in the registry:
|
|
133
87
|
|
|
134
88
|
```typescript
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const { typedData } = prepare({
|
|
89
|
+
const typedData = buildTypedData({
|
|
138
90
|
chainId: 8453,
|
|
139
91
|
token: "0xCustomTokenAddress",
|
|
140
|
-
|
|
141
|
-
|
|
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",
|
|
142
121
|
version: "1",
|
|
143
122
|
verifyingContract: "0xCustomTokenAddress",
|
|
144
|
-
from: "0xSender...",
|
|
145
|
-
to: "0xRecipient...",
|
|
146
|
-
value: 1000000n,
|
|
147
123
|
});
|
|
148
124
|
```
|
|
149
125
|
|
|
150
|
-
|
|
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
|
|
151
129
|
|
|
152
130
|
```typescript
|
|
153
131
|
import { verifySignature, recoverSigner } from "@seapay-ai/erc3009";
|
|
154
132
|
|
|
155
|
-
//
|
|
156
|
-
const recovered = recoverSigner(domain, message, signature);
|
|
157
|
-
console.log("Signed by:", recovered);
|
|
158
|
-
|
|
159
|
-
// Verify signature
|
|
133
|
+
// Verify signature matches expected signer
|
|
160
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);
|
|
161
139
|
```
|
|
162
140
|
|
|
163
|
-
##
|
|
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
|
|
164
171
|
|
|
165
172
|
| Chain | Chain ID | Testnet |
|
|
166
173
|
| ---------------- | -------- | ------- |
|
|
@@ -175,81 +182,93 @@ const isValid = verifySignature(domain, message, signature, expectedSigner);
|
|
|
175
182
|
| Polygon | 137 | - |
|
|
176
183
|
| Polygon Amoy | 80002 | ✅ |
|
|
177
184
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
Currently supports **USDC** on all chains above. The registry includes:
|
|
185
|
+
### Supported Tokens
|
|
181
186
|
|
|
182
|
-
|
|
183
|
-
- EIP-712 domain parameters (name, version)
|
|
184
|
-
- Token decimals
|
|
187
|
+
Currently includes **USDC** on all chains above.
|
|
185
188
|
|
|
186
189
|
### ⚠️ Important: Base Network Domain Names
|
|
187
190
|
|
|
188
|
-
USDC has **different domain names** on Base networks:
|
|
191
|
+
USDC has **different EIP-712 domain names** on Base networks:
|
|
189
192
|
|
|
190
193
|
| Network | Chain ID | Domain Name |
|
|
191
194
|
| ------------ | -------- | ------------ |
|
|
192
195
|
| Base Mainnet | 8453 | `"USD Coin"` |
|
|
193
196
|
| Base Sepolia | 84532 | `"USDC"` |
|
|
194
197
|
|
|
195
|
-
|
|
198
|
+
The registry handles this automatically. Always use the registry to ensure correct domain parameters.
|
|
199
|
+
|
|
200
|
+
## Complete Example
|
|
196
201
|
|
|
197
202
|
```typescript
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
import {
|
|
204
|
+
buildTypedData,
|
|
205
|
+
buildMessage,
|
|
206
|
+
nowPlusSeconds,
|
|
207
|
+
verifySignature,
|
|
208
|
+
} from "@seapay-ai/erc3009";
|
|
209
|
+
import { Wallet } from "ethers";
|
|
210
|
+
|
|
211
|
+
// Create wallet
|
|
212
|
+
const wallet = new Wallet("0x...");
|
|
206
213
|
|
|
207
|
-
//
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
value: 1000000n,
|
|
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
|
|
214
220
|
});
|
|
215
221
|
|
|
216
|
-
//
|
|
217
|
-
const {
|
|
218
|
-
|
|
222
|
+
// Build typed data
|
|
223
|
+
const {
|
|
224
|
+
domain,
|
|
225
|
+
types,
|
|
226
|
+
message: msg,
|
|
227
|
+
} = buildTypedData({
|
|
228
|
+
chainId: 84532, // Base Sepolia
|
|
219
229
|
token: "USDC",
|
|
220
|
-
|
|
221
|
-
// ...
|
|
230
|
+
message,
|
|
222
231
|
});
|
|
223
|
-
```
|
|
224
232
|
|
|
225
|
-
|
|
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);
|
|
239
|
+
```
|
|
226
240
|
|
|
227
241
|
## API Reference
|
|
228
242
|
|
|
229
|
-
###
|
|
243
|
+
### Build Functions
|
|
244
|
+
|
|
245
|
+
- **`buildMessage(params)`** - Create TransferWithAuthorization message
|
|
246
|
+
- **`buildTypedData(params)`** - Create complete EIP-712 typed data with domain resolution
|
|
247
|
+
|
|
248
|
+
### Domain Resolution
|
|
249
|
+
|
|
250
|
+
- **`resolveDomain(chainId, token, domainOverrides?)`** - Resolve EIP-712 domain from chain and token
|
|
251
|
+
|
|
252
|
+
### Verification Functions
|
|
230
253
|
|
|
231
|
-
-
|
|
232
|
-
-
|
|
233
|
-
- `buildTypedData({ domain, message })` - Build EIP-712 typed data
|
|
234
|
-
- `resolveDomain({ chainId, token, ...overrides })` - Resolve EIP-712 domain
|
|
235
|
-
- `erc3009.sign(wallet, domain, message)` - Sign with ethers wallet
|
|
236
|
-
- `verifySignature(domain, message, sig, signer)` - Verify signature
|
|
237
|
-
- `recoverSigner(domain, message, sig)` - Recover signer address
|
|
254
|
+
- **`verifySignature(domain, message, signature, expectedSigner)`** - Verify signature
|
|
255
|
+
- **`recoverSigner(domain, message, signature)`** - Recover signer address
|
|
238
256
|
|
|
239
|
-
### Registry
|
|
257
|
+
### Registry Functions
|
|
240
258
|
|
|
241
|
-
-
|
|
242
|
-
-
|
|
243
|
-
-
|
|
244
|
-
-
|
|
245
|
-
-
|
|
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
|
|
246
266
|
|
|
247
|
-
###
|
|
267
|
+
### Utility Functions
|
|
248
268
|
|
|
249
|
-
-
|
|
250
|
-
-
|
|
251
|
-
-
|
|
252
|
-
- `normalizeAddress(addr)` - Normalize to checksum address
|
|
269
|
+
- **`randomNonce()`** - Generate random bytes32 nonce
|
|
270
|
+
- **`nowPlusSeconds(seconds)`** - Get Unix timestamp N seconds from now
|
|
271
|
+
- **`nowSeconds()`** - Get current Unix timestamp
|
|
253
272
|
|
|
254
273
|
## TypeScript Types
|
|
255
274
|
|
|
@@ -257,44 +276,12 @@ The `prepare()` function automatically uses the correct domain name from the reg
|
|
|
257
276
|
import type {
|
|
258
277
|
TransferWithAuthorization,
|
|
259
278
|
EIP712Domain,
|
|
279
|
+
TypedData,
|
|
260
280
|
TokenConfig,
|
|
261
281
|
ChainConfig,
|
|
262
|
-
PrepareParams,
|
|
263
282
|
} from "@seapay-ai/erc3009";
|
|
264
283
|
```
|
|
265
284
|
|
|
266
|
-
## Advanced: Custom Registry
|
|
267
|
-
|
|
268
|
-
You can extend the registry by directly importing and modifying `TOKENS`:
|
|
269
|
-
|
|
270
|
-
```typescript
|
|
271
|
-
import { TOKENS } from "@seapay-ai/erc3009";
|
|
272
|
-
|
|
273
|
-
// Add your custom token
|
|
274
|
-
TOKENS["MYTOKEN"] = {
|
|
275
|
-
8453: {
|
|
276
|
-
symbol: "MYTOKEN",
|
|
277
|
-
chainId: 8453,
|
|
278
|
-
verifyingContract: "0x...",
|
|
279
|
-
name: "My Token",
|
|
280
|
-
version: "1",
|
|
281
|
-
decimals: 18,
|
|
282
|
-
},
|
|
283
|
-
};
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
## Examples
|
|
287
|
-
|
|
288
|
-
See the `/apps/erc3009-relay/src/signer-test.ts` in the monorepo for a complete working example.
|
|
289
|
-
|
|
290
|
-
## Contributing
|
|
291
|
-
|
|
292
|
-
Contributions welcome! To add support for a new token:
|
|
293
|
-
|
|
294
|
-
1. Add token config to `src/registry/tokens/{token}.ts`
|
|
295
|
-
2. Export from `src/registry/tokens/index.ts`
|
|
296
|
-
3. Add to `TOKENS` registry
|
|
297
|
-
|
|
298
285
|
## License
|
|
299
286
|
|
|
300
287
|
Apache-2.0
|
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,27 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @seapay/erc3009
|
|
2
|
+
* @seapay-ai/erc3009
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Simplified ERC-3009 (TransferWithAuthorization) library
|
|
5
5
|
* for building, signing, and verifying EIP-712 typed data.
|
|
6
6
|
*/
|
|
7
|
-
export type { TransferWithAuthorization, EIP712Domain,
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export { randomNonce,
|
|
13
|
-
export { prepare } from "./api/index.js";
|
|
14
|
-
export type { PrepareParams, PrepareResult } from "./api/prepare.js";
|
|
15
|
-
import * as registryFunctions from "./registry/registry.js";
|
|
16
|
-
export declare const registry: {
|
|
17
|
-
readonly getToken: typeof registryFunctions.getToken;
|
|
18
|
-
readonly getTokenByAddress: typeof registryFunctions.getTokenByAddress;
|
|
19
|
-
readonly getChain: typeof registryFunctions.getChain;
|
|
20
|
-
readonly listChains: typeof registryFunctions.listChains;
|
|
21
|
-
readonly listChainIds: typeof registryFunctions.listChainIds;
|
|
22
|
-
readonly listTokenSymbols: typeof registryFunctions.listTokenSymbols;
|
|
23
|
-
readonly listTokensOnChain: typeof registryFunctions.listTokensOnChain;
|
|
24
|
-
readonly isTokenSupported: typeof registryFunctions.isTokenSupported;
|
|
25
|
-
readonly isChainSupported: typeof registryFunctions.isChainSupported;
|
|
26
|
-
};
|
|
7
|
+
export type { TransferWithAuthorization, EIP712Domain, TypedData, 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";
|
|
27
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;;;;;GAKG;AAGH,YAAY,EACV,yBAAyB,EACzB,YAAY,EACZ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,yBAAyB,EACzB,YAAY,EACZ,SAAS,EACT,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"}
|