@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.
Files changed (98) hide show
  1. package/README.md +171 -184
  2. package/dist/build.d.ts +27 -0
  3. package/dist/build.d.ts.map +1 -0
  4. package/dist/build.js +47 -0
  5. package/dist/domain.d.ts +29 -0
  6. package/dist/domain.d.ts.map +1 -0
  7. package/dist/domain.js +57 -0
  8. package/dist/index.d.ts +8 -22
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +10 -31
  11. package/dist/registry.d.ts +44 -0
  12. package/dist/registry.d.ts.map +1 -0
  13. package/dist/registry.js +166 -0
  14. package/dist/types/index.d.ts +44 -3
  15. package/dist/types/index.d.ts.map +1 -1
  16. package/dist/types/index.js +1 -3
  17. package/dist/utils.d.ts +13 -0
  18. package/dist/utils.d.ts.map +1 -0
  19. package/dist/utils.js +19 -0
  20. package/dist/verify.d.ts +14 -0
  21. package/dist/verify.d.ts.map +1 -0
  22. package/dist/verify.js +36 -0
  23. package/package.json +8 -7
  24. package/dist/api/index.d.ts +0 -2
  25. package/dist/api/index.d.ts.map +0 -1
  26. package/dist/api/index.js +0 -1
  27. package/dist/api/prepare.d.ts +0 -47
  28. package/dist/api/prepare.d.ts.map +0 -1
  29. package/dist/api/prepare.js +0 -57
  30. package/dist/core.d.ts +0 -14
  31. package/dist/core.d.ts.map +0 -1
  32. package/dist/core.js +0 -13
  33. package/dist/domain/index.d.ts +0 -3
  34. package/dist/domain/index.d.ts.map +0 -1
  35. package/dist/domain/index.js +0 -2
  36. package/dist/domain/normalize.d.ts +0 -9
  37. package/dist/domain/normalize.d.ts.map +0 -1
  38. package/dist/domain/normalize.js +0 -25
  39. package/dist/domain/resolveDomain.d.ts +0 -15
  40. package/dist/domain/resolveDomain.d.ts.map +0 -1
  41. package/dist/domain/resolveDomain.js +0 -47
  42. package/dist/erc3009/buildTypes.d.ts +0 -6
  43. package/dist/erc3009/buildTypes.d.ts.map +0 -1
  44. package/dist/erc3009/buildTypes.js +0 -11
  45. package/dist/erc3009/constants.d.ts +0 -7
  46. package/dist/erc3009/constants.d.ts.map +0 -1
  47. package/dist/erc3009/constants.js +0 -6
  48. package/dist/erc3009/index.d.ts +0 -7
  49. package/dist/erc3009/index.d.ts.map +0 -1
  50. package/dist/erc3009/index.js +0 -6
  51. package/dist/erc3009/message.d.ts +0 -21
  52. package/dist/erc3009/message.d.ts.map +0 -1
  53. package/dist/erc3009/message.js +0 -29
  54. package/dist/erc3009/sign.d.ts +0 -7
  55. package/dist/erc3009/sign.d.ts.map +0 -1
  56. package/dist/erc3009/sign.js +0 -8
  57. package/dist/erc3009/typedData.d.ts +0 -15
  58. package/dist/erc3009/typedData.d.ts.map +0 -1
  59. package/dist/erc3009/typedData.js +0 -21
  60. package/dist/erc3009/verify.d.ts +0 -10
  61. package/dist/erc3009/verify.d.ts.map +0 -1
  62. package/dist/erc3009/verify.js +0 -17
  63. package/dist/registry/chains.d.ts +0 -22
  64. package/dist/registry/chains.d.ts.map +0 -1
  65. package/dist/registry/chains.js +0 -104
  66. package/dist/registry/index.d.ts +0 -4
  67. package/dist/registry/index.d.ts.map +0 -1
  68. package/dist/registry/index.js +0 -6
  69. package/dist/registry/registry.d.ts +0 -38
  70. package/dist/registry/registry.d.ts.map +0 -1
  71. package/dist/registry/registry.js +0 -73
  72. package/dist/registry/tokens/index.d.ts +0 -11
  73. package/dist/registry/tokens/index.d.ts.map +0 -1
  74. package/dist/registry/tokens/index.js +0 -14
  75. package/dist/registry/tokens/usdc.d.ts +0 -27
  76. package/dist/registry/tokens/usdc.d.ts.map +0 -1
  77. package/dist/registry/tokens/usdc.js +0 -104
  78. package/dist/types/domain.d.ts +0 -24
  79. package/dist/types/domain.d.ts.map +0 -1
  80. package/dist/types/domain.js +0 -1
  81. package/dist/types/erc3009.d.ts +0 -38
  82. package/dist/types/erc3009.d.ts.map +0 -1
  83. package/dist/types/erc3009.js +0 -15
  84. package/dist/types/registry.d.ts +0 -35
  85. package/dist/types/registry.d.ts.map +0 -1
  86. package/dist/types/registry.js +0 -1
  87. package/dist/utils/hex.d.ts +0 -13
  88. package/dist/utils/hex.d.ts.map +0 -1
  89. package/dist/utils/hex.js +0 -19
  90. package/dist/utils/index.d.ts +0 -4
  91. package/dist/utils/index.d.ts.map +0 -1
  92. package/dist/utils/index.js +0 -3
  93. package/dist/utils/nonce.d.ts +0 -5
  94. package/dist/utils/nonce.d.ts.map +0 -1
  95. package/dist/utils/nonce.js +0 -7
  96. package/dist/utils/time.d.ts +0 -13
  97. package/dist/utils/time.d.ts.map +0 -1
  98. package/dist/utils/time.js +0 -18
package/dist/index.js CHANGED
@@ -1,37 +1,16 @@
1
1
  /**
2
- * @seapay/erc3009
2
+ * @seapay-ai/erc3009
3
3
  *
4
- * Multi-chain ERC-3009 (TransferWithAuthorization) helper library
4
+ * Simplified ERC-3009 (TransferWithAuthorization) library
5
5
  * for building, signing, and verifying EIP-712 typed data.
6
6
  */
7
- // === Core ERC-3009 Namespace ===
8
- export { erc3009 } from "./core.js";
9
- // === Core Functions (individual exports) ===
10
- export { buildTypes, buildMessage, buildMessageWithTTL, buildTypedData, signTransferWithAuthorization, recoverSigner, verifySignature, TRANSFER_WITH_AUTHORIZATION_TYPE, PRIMARY_TYPE, } from "./erc3009/index.js";
11
- // === Domain Resolution ===
12
- export { resolveDomain, resolveDomainFromToken, normalizeAddress, normalizeChainId, } from "./domain/index.js";
13
7
  // === Registry ===
14
- export {
15
- // Chains
16
- CHAINS, getChain, listChains, listChainIds, isChainSupported,
17
- // Tokens
18
- TOKENS, USDC, getUSDC, isUSDCSupported, listUSDCChains, listTokenSymbols,
19
- // Registry lookup
20
- getToken, getTokenByAddress, listTokensOnChain, isTokenSupported, } from "./registry/index.js";
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";
21
15
  // === Utils ===
22
- export { randomNonce, nowSeconds, nowPlusSeconds, toBigInt, ensureHex, isBytes32Hex, } from "./utils/index.js";
23
- // === Ergonomic API ===
24
- export { prepare } from "./api/index.js";
25
- // === Registry object (for namespaced access) ===
26
- import * as registryFunctions from "./registry/registry.js";
27
- export const registry = {
28
- getToken: registryFunctions.getToken,
29
- getTokenByAddress: registryFunctions.getTokenByAddress,
30
- getChain: registryFunctions.getChain,
31
- listChains: registryFunctions.listChains,
32
- listChainIds: registryFunctions.listChainIds,
33
- listTokenSymbols: registryFunctions.listTokenSymbols,
34
- listTokensOnChain: registryFunctions.listTokensOnChain,
35
- isTokenSupported: registryFunctions.isTokenSupported,
36
- isChainSupported: registryFunctions.isChainSupported,
37
- };
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"}
@@ -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
+ };
@@ -1,4 +1,45 @@
1
- export * from "./erc3009.js";
2
- export * from "./domain.js";
3
- export * from "./registry.js";
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
+ }
4
45
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
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"}
@@ -1,3 +1 @@
1
- export * from "./erc3009.js";
2
- export * from "./domain.js";
3
- export * from "./registry.js";
1
+ export {};
@@ -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
+ }
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seapay-ai/erc3009",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "TypeScript utilities for ERC-3009 (Transfer With Authorization) EIP-712 signing",
5
5
  "keywords": [
6
6
  "erc3009",
@@ -30,6 +30,12 @@
30
30
  "README.md",
31
31
  "LICENSE"
32
32
  ],
33
+ "scripts": {
34
+ "build": "tsc -b",
35
+ "prepublishOnly": "pnpm build",
36
+ "lint": "echo \"no lint configured\"",
37
+ "typecheck": "tsc --noEmit"
38
+ },
33
39
  "dependencies": {
34
40
  "ethers": "^6.16.0"
35
41
  },
@@ -43,10 +49,5 @@
43
49
  },
44
50
  "publishConfig": {
45
51
  "access": "public"
46
- },
47
- "scripts": {
48
- "build": "tsc -b",
49
- "lint": "echo \"no lint configured\"",
50
- "typecheck": "tsc --noEmit"
51
52
  }
52
- }
53
+ }
@@ -1,2 +0,0 @@
1
- export * from "./prepare.js";
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
package/dist/api/index.js DELETED
@@ -1 +0,0 @@
1
- export * from "./prepare.js";
@@ -1,47 +0,0 @@
1
- import type { EIP712Domain, TransferWithAuthorization } from "../types/index.js";
2
- import type { TypedData } from "../erc3009/typedData.js";
3
- export type PrepareParams = {
4
- chainId: number;
5
- token: string;
6
- name?: string;
7
- version?: string;
8
- verifyingContract?: string;
9
- from: string;
10
- to: string;
11
- value: bigint | number | string;
12
- ttlSeconds?: number;
13
- nonce?: string;
14
- validAfter?: bigint | number | string;
15
- validBefore?: bigint | number | string;
16
- };
17
- export type PrepareResult = {
18
- domain: EIP712Domain;
19
- message: TransferWithAuthorization;
20
- typedData: TypedData;
21
- };
22
- /**
23
- * One-call convenience helper to prepare complete ERC-3009 typed data
24
- *
25
- * This resolves the domain from registry, builds the message with time window,
26
- * and returns everything needed for signing.
27
- *
28
- * @example
29
- * ```ts
30
- * const { domain, message, typedData } = prepare({
31
- * chainId: 8453,
32
- * token: "USDC",
33
- * from: "0x...",
34
- * to: "0x...",
35
- * value: 1000000n, // 1 USDC (6 decimals)
36
- * ttlSeconds: 300, // 5 minutes
37
- * });
38
- *
39
- * const signature = await wallet.signTypedData(
40
- * typedData.domain,
41
- * typedData.types,
42
- * typedData.message
43
- * );
44
- * ```
45
- */
46
- export declare function prepare(params: PrepareParams): PrepareResult;
47
- //# sourceMappingURL=prepare.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prepare.d.ts","sourceRoot":"","sources":["../../src/api/prepare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,yBAAyB,EAE1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAMzD,MAAM,MAAM,aAAa,GAAG;IAE1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAG3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,yBAAyB,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAgC5D"}
@@ -1,57 +0,0 @@
1
- import { resolveDomain } from "../domain/resolveDomain.js";
2
- import { buildMessageWithTTL } from "../erc3009/message.js";
3
- import { buildTypedData } from "../erc3009/typedData.js";
4
- import { randomNonce } from "../utils/nonce.js";
5
- /**
6
- * One-call convenience helper to prepare complete ERC-3009 typed data
7
- *
8
- * This resolves the domain from registry, builds the message with time window,
9
- * and returns everything needed for signing.
10
- *
11
- * @example
12
- * ```ts
13
- * const { domain, message, typedData } = prepare({
14
- * chainId: 8453,
15
- * token: "USDC",
16
- * from: "0x...",
17
- * to: "0x...",
18
- * value: 1000000n, // 1 USDC (6 decimals)
19
- * ttlSeconds: 300, // 5 minutes
20
- * });
21
- *
22
- * const signature = await wallet.signTypedData(
23
- * typedData.domain,
24
- * typedData.types,
25
- * typedData.message
26
- * );
27
- * ```
28
- */
29
- export function prepare(params) {
30
- // Resolve domain
31
- const domainParams = {
32
- chainId: params.chainId,
33
- token: params.token,
34
- name: params.name,
35
- version: params.version,
36
- verifyingContract: params.verifyingContract,
37
- };
38
- const domain = resolveDomain(domainParams);
39
- // Build message
40
- const message = buildMessageWithTTL({
41
- from: params.from,
42
- to: params.to,
43
- value: params.value,
44
- ttlSeconds: params.ttlSeconds,
45
- nonce: params.nonce ?? randomNonce(),
46
- });
47
- // Override time window if explicitly provided
48
- if (params.validAfter !== undefined) {
49
- message.validAfter = BigInt(params.validAfter);
50
- }
51
- if (params.validBefore !== undefined) {
52
- message.validBefore = BigInt(params.validBefore);
53
- }
54
- // Build typed data
55
- const typedData = buildTypedData({ domain, message });
56
- return { domain, message, typedData };
57
- }
package/dist/core.d.ts DELETED
@@ -1,14 +0,0 @@
1
- /**
2
- * Core ERC-3009 namespace with all builder functions
3
- */
4
- import { buildTypes, buildMessage, buildMessageWithTTL, buildTypedData, signTransferWithAuthorization, recoverSigner, verifySignature } from "./erc3009/index.js";
5
- export declare const erc3009: {
6
- readonly buildTypes: typeof buildTypes;
7
- readonly buildMessage: typeof buildMessage;
8
- readonly buildMessageWithTTL: typeof buildMessageWithTTL;
9
- readonly buildTypedData: typeof buildTypedData;
10
- readonly sign: typeof signTransferWithAuthorization;
11
- readonly recoverSigner: typeof recoverSigner;
12
- readonly verifySignature: typeof verifySignature;
13
- };
14
- //# sourceMappingURL=core.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,UAAU,EACV,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,6BAA6B,EAC7B,aAAa,EACb,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,OAAO;;;;;;;;CAQV,CAAC"}
package/dist/core.js DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * Core ERC-3009 namespace with all builder functions
3
- */
4
- import { buildTypes, buildMessage, buildMessageWithTTL, buildTypedData, signTransferWithAuthorization, recoverSigner, verifySignature, } from "./erc3009/index.js";
5
- export const erc3009 = {
6
- buildTypes,
7
- buildMessage,
8
- buildMessageWithTTL,
9
- buildTypedData,
10
- sign: signTransferWithAuthorization,
11
- recoverSigner,
12
- verifySignature,
13
- };
@@ -1,3 +0,0 @@
1
- export * from "./normalize.js";
2
- export * from "./resolveDomain.js";
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/domain/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC"}
@@ -1,2 +0,0 @@
1
- export * from "./normalize.js";
2
- export * from "./resolveDomain.js";
@@ -1,9 +0,0 @@
1
- /**
2
- * Normalize address to checksum format
3
- */
4
- export declare function normalizeAddress(address: string): string;
5
- /**
6
- * Normalize chainId to number
7
- */
8
- export declare function normalizeChainId(chainId: number | bigint | string): number;
9
- //# sourceMappingURL=normalize.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/domain/normalize.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASxD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAI1E"}
@@ -1,25 +0,0 @@
1
- import { getAddress } from "ethers";
2
- /**
3
- * Normalize address to checksum format
4
- */
5
- export function normalizeAddress(address) {
6
- try {
7
- return getAddress(address);
8
- }
9
- catch {
10
- // If invalid address, return lowercase with 0x
11
- return address.toLowerCase().startsWith("0x")
12
- ? address.toLowerCase()
13
- : `0x${address.toLowerCase()}`;
14
- }
15
- }
16
- /**
17
- * Normalize chainId to number
18
- */
19
- export function normalizeChainId(chainId) {
20
- if (typeof chainId === "number")
21
- return chainId;
22
- if (typeof chainId === "bigint")
23
- return Number(chainId);
24
- return parseInt(String(chainId), 10);
25
- }