@stablechain/sdk 1.0.3 → 1.0.5
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 +757 -0
- package/dist/chunk-QQ26UI3F.cjs +665 -0
- package/dist/chunk-VS6GBRDF.js +665 -0
- package/dist/erc3009.cjs +26 -0
- package/dist/erc3009.d.cts +429 -0
- package/dist/erc3009.d.ts +429 -0
- package/dist/erc3009.js +26 -0
- package/dist/index.cjs +709 -180
- package/dist/index.d.cts +109 -4
- package/dist/index.d.ts +109 -4
- package/dist/index.js +635 -106
- package/package.json +6 -1
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
// src/services/erc3009/abi.ts
|
|
2
|
+
var ERC3009_ABI = [
|
|
3
|
+
{
|
|
4
|
+
type: "function",
|
|
5
|
+
name: "transferWithAuthorization",
|
|
6
|
+
stateMutability: "nonpayable",
|
|
7
|
+
inputs: [
|
|
8
|
+
{ name: "from", type: "address" },
|
|
9
|
+
{ name: "to", type: "address" },
|
|
10
|
+
{ name: "value", type: "uint256" },
|
|
11
|
+
{ name: "validAfter", type: "uint256" },
|
|
12
|
+
{ name: "validBefore", type: "uint256" },
|
|
13
|
+
{ name: "nonce", type: "bytes32" },
|
|
14
|
+
{ name: "v", type: "uint8" },
|
|
15
|
+
{ name: "r", type: "bytes32" },
|
|
16
|
+
{ name: "s", type: "bytes32" }
|
|
17
|
+
],
|
|
18
|
+
outputs: []
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
type: "function",
|
|
22
|
+
name: "receiveWithAuthorization",
|
|
23
|
+
stateMutability: "nonpayable",
|
|
24
|
+
inputs: [
|
|
25
|
+
{ name: "from", type: "address" },
|
|
26
|
+
{ name: "to", type: "address" },
|
|
27
|
+
{ name: "value", type: "uint256" },
|
|
28
|
+
{ name: "validAfter", type: "uint256" },
|
|
29
|
+
{ name: "validBefore", type: "uint256" },
|
|
30
|
+
{ name: "nonce", type: "bytes32" },
|
|
31
|
+
{ name: "v", type: "uint8" },
|
|
32
|
+
{ name: "r", type: "bytes32" },
|
|
33
|
+
{ name: "s", type: "bytes32" }
|
|
34
|
+
],
|
|
35
|
+
outputs: []
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: "function",
|
|
39
|
+
name: "cancelAuthorization",
|
|
40
|
+
stateMutability: "nonpayable",
|
|
41
|
+
inputs: [
|
|
42
|
+
{ name: "authorizer", type: "address" },
|
|
43
|
+
{ name: "nonce", type: "bytes32" },
|
|
44
|
+
{ name: "v", type: "uint8" },
|
|
45
|
+
{ name: "r", type: "bytes32" },
|
|
46
|
+
{ name: "s", type: "bytes32" }
|
|
47
|
+
],
|
|
48
|
+
outputs: []
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: "function",
|
|
52
|
+
name: "authorizationState",
|
|
53
|
+
stateMutability: "view",
|
|
54
|
+
inputs: [
|
|
55
|
+
{ name: "authorizer", type: "address" },
|
|
56
|
+
{ name: "nonce", type: "bytes32" }
|
|
57
|
+
],
|
|
58
|
+
outputs: [{ name: "", type: "bool" }]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: "function",
|
|
62
|
+
name: "balanceOf",
|
|
63
|
+
stateMutability: "view",
|
|
64
|
+
inputs: [{ name: "account", type: "address" }],
|
|
65
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: "event",
|
|
69
|
+
name: "AuthorizationUsed",
|
|
70
|
+
anonymous: false,
|
|
71
|
+
inputs: [
|
|
72
|
+
{ name: "authorizer", type: "address", indexed: true },
|
|
73
|
+
{ name: "nonce", type: "bytes32", indexed: true }
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: "function",
|
|
78
|
+
name: "eip712Domain",
|
|
79
|
+
stateMutability: "view",
|
|
80
|
+
inputs: [],
|
|
81
|
+
outputs: [
|
|
82
|
+
{ name: "fields", type: "bytes1" },
|
|
83
|
+
{ name: "name", type: "string" },
|
|
84
|
+
{ name: "version", type: "string" },
|
|
85
|
+
{ name: "chainId", type: "uint256" },
|
|
86
|
+
{ name: "verifyingContract", type: "address" },
|
|
87
|
+
{ name: "salt", type: "bytes32" },
|
|
88
|
+
{ name: "extensions", type: "uint256[]" }
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: "function",
|
|
93
|
+
name: "name",
|
|
94
|
+
stateMutability: "view",
|
|
95
|
+
inputs: [],
|
|
96
|
+
outputs: [{ name: "", type: "string" }]
|
|
97
|
+
}
|
|
98
|
+
];
|
|
99
|
+
var AUTHORIZATION_FIELDS = [
|
|
100
|
+
{ name: "from", type: "address" },
|
|
101
|
+
{ name: "to", type: "address" },
|
|
102
|
+
{ name: "value", type: "uint256" },
|
|
103
|
+
{ name: "validAfter", type: "uint256" },
|
|
104
|
+
{ name: "validBefore", type: "uint256" },
|
|
105
|
+
{ name: "nonce", type: "bytes32" }
|
|
106
|
+
];
|
|
107
|
+
var TRANSFER_WITH_AUTHORIZATION_TYPES = {
|
|
108
|
+
TransferWithAuthorization: AUTHORIZATION_FIELDS
|
|
109
|
+
};
|
|
110
|
+
var RECEIVE_WITH_AUTHORIZATION_TYPES = {
|
|
111
|
+
ReceiveWithAuthorization: AUTHORIZATION_FIELDS
|
|
112
|
+
};
|
|
113
|
+
var CANCEL_AUTHORIZATION_TYPES = {
|
|
114
|
+
CancelAuthorization: [
|
|
115
|
+
{ name: "authorizer", type: "address" },
|
|
116
|
+
{ name: "nonce", type: "bytes32" }
|
|
117
|
+
]
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/services/erc3009/signAuthorization.ts
|
|
121
|
+
import { signTypedData } from "viem/actions";
|
|
122
|
+
|
|
123
|
+
// src/constants.ts
|
|
124
|
+
var STABLE_USDT_ADDRESS = "0x779ded0c9e1022225f8e0630b35a9b54be713736";
|
|
125
|
+
var STABLE_VAULT_ADDRESS = "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08";
|
|
126
|
+
|
|
127
|
+
// src/errors.ts
|
|
128
|
+
import { BaseError } from "viem";
|
|
129
|
+
var StableError = class extends BaseError {
|
|
130
|
+
};
|
|
131
|
+
var StableValidationError = class extends StableError {
|
|
132
|
+
constructor(field, value, reason, opts) {
|
|
133
|
+
super(reason, {
|
|
134
|
+
name: "StableValidationError",
|
|
135
|
+
cause: opts?.cause,
|
|
136
|
+
metaMessages: [`Field: ${field}`, `Value: ${formatValue(value)}`]
|
|
137
|
+
});
|
|
138
|
+
this.field = field;
|
|
139
|
+
this.value = value;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
var StableQuoteError = class extends StableError {
|
|
143
|
+
constructor(args) {
|
|
144
|
+
const meta = [`Provider: ${args.provider}`];
|
|
145
|
+
if (args.httpStatus !== void 0)
|
|
146
|
+
meta.push(`HTTP status: ${args.httpStatus}`);
|
|
147
|
+
if (args.providerCode) meta.push(`Provider code: ${args.providerCode}`);
|
|
148
|
+
super(args.shortMessage, {
|
|
149
|
+
name: "StableQuoteError",
|
|
150
|
+
cause: args.cause,
|
|
151
|
+
metaMessages: meta
|
|
152
|
+
});
|
|
153
|
+
this.provider = args.provider;
|
|
154
|
+
this.httpStatus = args.httpStatus;
|
|
155
|
+
this.providerCode = args.providerCode;
|
|
156
|
+
this.body = args.body;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var StableTransactionError = class extends StableError {
|
|
160
|
+
constructor(args) {
|
|
161
|
+
const meta = [`Phase: ${args.phase}`];
|
|
162
|
+
if (args.chainId !== void 0) meta.push(`Chain ID: ${args.chainId}`);
|
|
163
|
+
if (args.txHash) meta.push(`Tx hash: ${args.txHash}`);
|
|
164
|
+
if (args.revertReason) meta.push(`Revert reason: ${args.revertReason}`);
|
|
165
|
+
super(args.shortMessage, {
|
|
166
|
+
name: "StableTransactionError",
|
|
167
|
+
cause: args.cause,
|
|
168
|
+
metaMessages: meta
|
|
169
|
+
});
|
|
170
|
+
this.phase = args.phase;
|
|
171
|
+
this.txHash = args.txHash;
|
|
172
|
+
this.chainId = args.chainId;
|
|
173
|
+
this.revertReason = args.revertReason;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var StableNetworkError = class extends StableError {
|
|
177
|
+
constructor(args) {
|
|
178
|
+
super(args.shortMessage, {
|
|
179
|
+
name: "StableNetworkError",
|
|
180
|
+
cause: args.cause,
|
|
181
|
+
metaMessages: args.url ? [`URL: ${args.url}`] : void 0
|
|
182
|
+
});
|
|
183
|
+
this.url = args.url;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
var StableBulkError = class extends StableError {
|
|
187
|
+
constructor(args) {
|
|
188
|
+
super(args.shortMessage, { name: "StableBulkError" });
|
|
189
|
+
this.results = args.results;
|
|
190
|
+
this.succeeded = args.succeeded;
|
|
191
|
+
this.failed = args.failed;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
function formatValue(v) {
|
|
195
|
+
if (typeof v === "bigint") return `${v}n`;
|
|
196
|
+
if (typeof v === "string") return JSON.stringify(v);
|
|
197
|
+
if (v === null) return "null";
|
|
198
|
+
if (v === void 0) return "undefined";
|
|
199
|
+
try {
|
|
200
|
+
return JSON.stringify(v);
|
|
201
|
+
} catch {
|
|
202
|
+
return String(v);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/services/erc3009/shared.ts
|
|
207
|
+
import {
|
|
208
|
+
isAddress,
|
|
209
|
+
parseSignature,
|
|
210
|
+
size
|
|
211
|
+
} from "viem";
|
|
212
|
+
import { generatePrivateKey } from "viem/accounts";
|
|
213
|
+
import { getChainId, readContract } from "viem/actions";
|
|
214
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
215
|
+
function randomNonce() {
|
|
216
|
+
return generatePrivateKey();
|
|
217
|
+
}
|
|
218
|
+
function splitSignature(signature) {
|
|
219
|
+
const { r, s, v, yParity } = parseSignature(signature);
|
|
220
|
+
return { r, s, v: v !== void 0 ? Number(v) : (yParity ?? 0) + 27 };
|
|
221
|
+
}
|
|
222
|
+
function resolveSigner(client, account) {
|
|
223
|
+
const signer = account ?? client.account;
|
|
224
|
+
if (!signer) {
|
|
225
|
+
throw new StableValidationError(
|
|
226
|
+
"signer",
|
|
227
|
+
void 0,
|
|
228
|
+
"erc3009: requires a signer \u2014 pass `account` or configure one on the client"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
return signer;
|
|
232
|
+
}
|
|
233
|
+
function signerAddress(signer) {
|
|
234
|
+
return typeof signer === "string" ? signer : signer.address;
|
|
235
|
+
}
|
|
236
|
+
async function resolveDomain(client, token, overrides) {
|
|
237
|
+
const chainId = overrides?.chainId ?? client.chain?.id ?? await getChainId(client);
|
|
238
|
+
const verifyingContract = overrides?.verifyingContract ?? token;
|
|
239
|
+
if (overrides?.name !== void 0 && overrides?.version !== void 0) {
|
|
240
|
+
return {
|
|
241
|
+
name: overrides.name,
|
|
242
|
+
version: overrides.version,
|
|
243
|
+
chainId,
|
|
244
|
+
verifyingContract
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
const res = await readContract(client, {
|
|
249
|
+
address: token,
|
|
250
|
+
abi: ERC3009_ABI,
|
|
251
|
+
functionName: "eip712Domain"
|
|
252
|
+
});
|
|
253
|
+
return {
|
|
254
|
+
name: overrides?.name ?? res[1],
|
|
255
|
+
version: overrides?.version ?? res[2],
|
|
256
|
+
chainId,
|
|
257
|
+
verifyingContract
|
|
258
|
+
};
|
|
259
|
+
} catch {
|
|
260
|
+
const name = overrides?.name ?? await readContract(client, {
|
|
261
|
+
address: token,
|
|
262
|
+
abi: ERC3009_ABI,
|
|
263
|
+
functionName: "name"
|
|
264
|
+
});
|
|
265
|
+
return {
|
|
266
|
+
name,
|
|
267
|
+
version: overrides?.version ?? "1",
|
|
268
|
+
chainId,
|
|
269
|
+
verifyingContract
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function validateAuthorizationParams(params) {
|
|
274
|
+
if (!isAddress(params.from)) {
|
|
275
|
+
throw new StableValidationError(
|
|
276
|
+
"from",
|
|
277
|
+
params.from,
|
|
278
|
+
`erc3009: invalid sender address "${params.from}"`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
if (!isAddress(params.to) || params.to === ZERO_ADDRESS) {
|
|
282
|
+
throw new StableValidationError(
|
|
283
|
+
"to",
|
|
284
|
+
params.to,
|
|
285
|
+
`erc3009: invalid recipient address "${params.to}"`
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
if (params.value <= 0n) {
|
|
289
|
+
throw new StableValidationError(
|
|
290
|
+
"value",
|
|
291
|
+
params.value,
|
|
292
|
+
"erc3009: value must be a positive bigint (token smallest unit)"
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
if (params.validBefore <= params.validAfter) {
|
|
296
|
+
throw new StableValidationError(
|
|
297
|
+
"validBefore",
|
|
298
|
+
params.validBefore,
|
|
299
|
+
"erc3009: validBefore must be greater than validAfter"
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
validateNonce(params.nonce);
|
|
303
|
+
}
|
|
304
|
+
function validateNonce(nonce) {
|
|
305
|
+
if (!/^0x[0-9a-fA-F]*$/.test(nonce) || size(nonce) !== 32) {
|
|
306
|
+
throw new StableValidationError(
|
|
307
|
+
"nonce",
|
|
308
|
+
nonce,
|
|
309
|
+
"erc3009: nonce must be a 32-byte hex value"
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// src/services/erc3009/signAuthorization.ts
|
|
315
|
+
async function signAuthorization(client, parameters, primaryType, types) {
|
|
316
|
+
const token = parameters.token ?? STABLE_USDT_ADDRESS;
|
|
317
|
+
const signer = resolveSigner(client, parameters.account);
|
|
318
|
+
const authorization = {
|
|
319
|
+
from: parameters.from ?? signerAddress(signer),
|
|
320
|
+
to: parameters.to,
|
|
321
|
+
value: parameters.value,
|
|
322
|
+
validAfter: parameters.validAfter ?? 0n,
|
|
323
|
+
validBefore: parameters.validBefore,
|
|
324
|
+
nonce: parameters.nonce ?? randomNonce()
|
|
325
|
+
};
|
|
326
|
+
validateAuthorizationParams(authorization);
|
|
327
|
+
const domain = await resolveDomain(client, token, parameters.domain);
|
|
328
|
+
let signature;
|
|
329
|
+
try {
|
|
330
|
+
signature = await signTypedData(client, {
|
|
331
|
+
account: signer,
|
|
332
|
+
domain,
|
|
333
|
+
types,
|
|
334
|
+
primaryType,
|
|
335
|
+
message: authorization
|
|
336
|
+
});
|
|
337
|
+
} catch (err) {
|
|
338
|
+
throw new StableTransactionError({
|
|
339
|
+
phase: "sign",
|
|
340
|
+
shortMessage: "erc3009: wallet rejected or failed to sign the authorization",
|
|
341
|
+
chainId: domain.chainId === void 0 ? void 0 : Number(domain.chainId),
|
|
342
|
+
cause: err
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return { authorization, signature, ...splitSignature(signature) };
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/services/erc3009/signTransferAuthorization.ts
|
|
349
|
+
function signTransferAuthorization(client, parameters) {
|
|
350
|
+
return signAuthorization(
|
|
351
|
+
client,
|
|
352
|
+
parameters,
|
|
353
|
+
"TransferWithAuthorization",
|
|
354
|
+
TRANSFER_WITH_AUTHORIZATION_TYPES
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/services/erc3009/signReceiveAuthorization.ts
|
|
359
|
+
function signReceiveAuthorization(client, parameters) {
|
|
360
|
+
return signAuthorization(
|
|
361
|
+
client,
|
|
362
|
+
parameters,
|
|
363
|
+
"ReceiveWithAuthorization",
|
|
364
|
+
RECEIVE_WITH_AUTHORIZATION_TYPES
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/services/erc3009/submit.ts
|
|
369
|
+
import { writeContract } from "viem/actions";
|
|
370
|
+
async function submitAuthorizationTx(client, token, functionName, signed) {
|
|
371
|
+
const signer = resolveSigner(client, void 0);
|
|
372
|
+
const { authorization: a, v, r, s } = signed;
|
|
373
|
+
try {
|
|
374
|
+
return await writeContract(client, {
|
|
375
|
+
address: token,
|
|
376
|
+
abi: ERC3009_ABI,
|
|
377
|
+
functionName,
|
|
378
|
+
args: [
|
|
379
|
+
a.from,
|
|
380
|
+
a.to,
|
|
381
|
+
a.value,
|
|
382
|
+
a.validAfter,
|
|
383
|
+
a.validBefore,
|
|
384
|
+
a.nonce,
|
|
385
|
+
v,
|
|
386
|
+
r,
|
|
387
|
+
s
|
|
388
|
+
],
|
|
389
|
+
account: signer,
|
|
390
|
+
chain: client.chain
|
|
391
|
+
});
|
|
392
|
+
} catch (err) {
|
|
393
|
+
throw new StableTransactionError({
|
|
394
|
+
phase: "send",
|
|
395
|
+
shortMessage: `erc3009: ${functionName} transaction failed`,
|
|
396
|
+
chainId: client.chain?.id,
|
|
397
|
+
cause: err
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/services/erc3009/transferWithAuthorization.ts
|
|
403
|
+
async function transferWithAuthorization(client, parameters) {
|
|
404
|
+
const token = parameters.token ?? STABLE_USDT_ADDRESS;
|
|
405
|
+
const signed = "signed" in parameters ? parameters.signed : await signTransferAuthorization(client, parameters);
|
|
406
|
+
return submitAuthorizationTx(
|
|
407
|
+
client,
|
|
408
|
+
token,
|
|
409
|
+
"transferWithAuthorization",
|
|
410
|
+
signed
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// src/services/erc3009/receiveWithAuthorization.ts
|
|
415
|
+
async function receiveWithAuthorization(client, parameters) {
|
|
416
|
+
const token = parameters.token ?? STABLE_USDT_ADDRESS;
|
|
417
|
+
const signed = "signed" in parameters ? parameters.signed : await signReceiveAuthorization(client, parameters);
|
|
418
|
+
const caller = signerAddress(resolveSigner(client, void 0));
|
|
419
|
+
if (caller.toLowerCase() !== signed.authorization.to.toLowerCase()) {
|
|
420
|
+
throw new StableValidationError(
|
|
421
|
+
"caller",
|
|
422
|
+
caller,
|
|
423
|
+
`erc3009: receiveWithAuthorization must be submitted by the recipient (to=${signed.authorization.to})`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
return submitAuthorizationTx(
|
|
427
|
+
client,
|
|
428
|
+
token,
|
|
429
|
+
"receiveWithAuthorization",
|
|
430
|
+
signed
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// src/services/erc3009/cancelAuthorization.ts
|
|
435
|
+
import { signTypedData as signTypedData2, writeContract as writeContract2 } from "viem/actions";
|
|
436
|
+
async function cancelAuthorization(client, parameters) {
|
|
437
|
+
const token = parameters.token ?? STABLE_USDT_ADDRESS;
|
|
438
|
+
const signer = resolveSigner(client, parameters.account);
|
|
439
|
+
const authorizer = parameters.authorizer ?? signerAddress(signer);
|
|
440
|
+
validateNonce(parameters.nonce);
|
|
441
|
+
const domain = await resolveDomain(client, token, parameters.domain);
|
|
442
|
+
const message = { authorizer, nonce: parameters.nonce };
|
|
443
|
+
let signature;
|
|
444
|
+
try {
|
|
445
|
+
signature = await signTypedData2(client, {
|
|
446
|
+
account: signer,
|
|
447
|
+
domain,
|
|
448
|
+
types: CANCEL_AUTHORIZATION_TYPES,
|
|
449
|
+
primaryType: "CancelAuthorization",
|
|
450
|
+
message
|
|
451
|
+
});
|
|
452
|
+
} catch (err) {
|
|
453
|
+
throw new StableTransactionError({
|
|
454
|
+
phase: "sign",
|
|
455
|
+
shortMessage: "erc3009: wallet rejected or failed to sign the cancellation",
|
|
456
|
+
chainId: domain.chainId === void 0 ? void 0 : Number(domain.chainId),
|
|
457
|
+
cause: err
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
const { v, r, s } = splitSignature(signature);
|
|
461
|
+
try {
|
|
462
|
+
return await writeContract2(client, {
|
|
463
|
+
address: token,
|
|
464
|
+
abi: ERC3009_ABI,
|
|
465
|
+
functionName: "cancelAuthorization",
|
|
466
|
+
args: [authorizer, parameters.nonce, v, r, s],
|
|
467
|
+
account: signer,
|
|
468
|
+
chain: client.chain
|
|
469
|
+
});
|
|
470
|
+
} catch (err) {
|
|
471
|
+
throw new StableTransactionError({
|
|
472
|
+
phase: "send",
|
|
473
|
+
shortMessage: "erc3009: cancelAuthorization transaction failed",
|
|
474
|
+
chainId: client.chain?.id,
|
|
475
|
+
cause: err
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/services/erc3009/prepareTransferWithAuthorization.ts
|
|
481
|
+
import { encodeFunctionData } from "viem";
|
|
482
|
+
import { getChainId as getChainId2 } from "viem/actions";
|
|
483
|
+
async function prepareTransferWithAuthorization(client, parameters) {
|
|
484
|
+
const token = parameters.token ?? STABLE_USDT_ADDRESS;
|
|
485
|
+
const signed = await signTransferAuthorization(client, parameters);
|
|
486
|
+
const { authorization: a, v, r, s } = signed;
|
|
487
|
+
const data = encodeFunctionData({
|
|
488
|
+
abi: ERC3009_ABI,
|
|
489
|
+
functionName: "transferWithAuthorization",
|
|
490
|
+
args: [
|
|
491
|
+
a.from,
|
|
492
|
+
a.to,
|
|
493
|
+
a.value,
|
|
494
|
+
a.validAfter,
|
|
495
|
+
a.validBefore,
|
|
496
|
+
a.nonce,
|
|
497
|
+
v,
|
|
498
|
+
r,
|
|
499
|
+
s
|
|
500
|
+
]
|
|
501
|
+
});
|
|
502
|
+
const chainId = client.chain?.id ?? await getChainId2(client);
|
|
503
|
+
return { to: token, data, value: 0n, chainId, signed };
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/services/erc3009/verifyAuthorization.ts
|
|
507
|
+
import { recoverTypedDataAddress } from "viem";
|
|
508
|
+
import { getBlock, readContract as readContract2, simulateContract } from "viem/actions";
|
|
509
|
+
async function verifyAuthorization(client, parameters) {
|
|
510
|
+
const token = parameters.token ?? STABLE_USDT_ADDRESS;
|
|
511
|
+
const primaryType = parameters.primaryType ?? "TransferWithAuthorization";
|
|
512
|
+
const isReceive = primaryType === "ReceiveWithAuthorization";
|
|
513
|
+
const types = isReceive ? RECEIVE_WITH_AUTHORIZATION_TYPES : TRANSFER_WITH_AUTHORIZATION_TYPES;
|
|
514
|
+
const { authorization: a, signature, v, r, s } = parameters.signed;
|
|
515
|
+
const reasons = [];
|
|
516
|
+
const domain = await resolveDomain(client, token, parameters.domain);
|
|
517
|
+
let signer;
|
|
518
|
+
try {
|
|
519
|
+
signer = await recoverTypedDataAddress({
|
|
520
|
+
domain,
|
|
521
|
+
types,
|
|
522
|
+
primaryType,
|
|
523
|
+
message: a,
|
|
524
|
+
signature
|
|
525
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic primaryType/types
|
|
526
|
+
});
|
|
527
|
+
if (signer.toLowerCase() !== a.from.toLowerCase()) {
|
|
528
|
+
reasons.push("signer_mismatch");
|
|
529
|
+
}
|
|
530
|
+
} catch {
|
|
531
|
+
reasons.push("invalid_signature");
|
|
532
|
+
}
|
|
533
|
+
const used = await readContract2(client, {
|
|
534
|
+
address: token,
|
|
535
|
+
abi: ERC3009_ABI,
|
|
536
|
+
functionName: "authorizationState",
|
|
537
|
+
args: [a.from, a.nonce]
|
|
538
|
+
});
|
|
539
|
+
if (used) reasons.push("already_used");
|
|
540
|
+
const now = parameters.now ?? (await getBlock(client, { blockTag: "latest" })).timestamp;
|
|
541
|
+
const margin = BigInt(parameters.expiryMarginSeconds ?? 0);
|
|
542
|
+
if (a.validAfter > now) reasons.push("not_yet_valid");
|
|
543
|
+
if (a.validBefore <= now) reasons.push("expired");
|
|
544
|
+
else if (a.validBefore <= now + margin) reasons.push("expiring_soon");
|
|
545
|
+
const balance = await readContract2(client, {
|
|
546
|
+
address: token,
|
|
547
|
+
abi: ERC3009_ABI,
|
|
548
|
+
functionName: "balanceOf",
|
|
549
|
+
args: [a.from]
|
|
550
|
+
});
|
|
551
|
+
let shortfall;
|
|
552
|
+
if (balance < a.value) {
|
|
553
|
+
reasons.push("insufficient_funds");
|
|
554
|
+
shortfall = a.value - balance;
|
|
555
|
+
}
|
|
556
|
+
let revertReason;
|
|
557
|
+
try {
|
|
558
|
+
await simulateContract(client, {
|
|
559
|
+
address: token,
|
|
560
|
+
abi: ERC3009_ABI,
|
|
561
|
+
functionName: isReceive ? "receiveWithAuthorization" : "transferWithAuthorization",
|
|
562
|
+
args: [
|
|
563
|
+
a.from,
|
|
564
|
+
a.to,
|
|
565
|
+
a.value,
|
|
566
|
+
a.validAfter,
|
|
567
|
+
a.validBefore,
|
|
568
|
+
a.nonce,
|
|
569
|
+
v,
|
|
570
|
+
r,
|
|
571
|
+
s
|
|
572
|
+
],
|
|
573
|
+
account: a.to
|
|
574
|
+
});
|
|
575
|
+
} catch (err) {
|
|
576
|
+
reasons.push("would_revert");
|
|
577
|
+
revertReason = err.shortMessage ?? err.message;
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
ok: reasons.length === 0,
|
|
581
|
+
reasons,
|
|
582
|
+
signer,
|
|
583
|
+
balance,
|
|
584
|
+
shortfall,
|
|
585
|
+
revertReason
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// src/services/erc3009/getAuthorizationState.ts
|
|
590
|
+
import { readContract as readContract3 } from "viem/actions";
|
|
591
|
+
function getAuthorizationState(client, parameters) {
|
|
592
|
+
validateNonce(parameters.nonce);
|
|
593
|
+
return readContract3(client, {
|
|
594
|
+
address: parameters.token ?? STABLE_USDT_ADDRESS,
|
|
595
|
+
abi: ERC3009_ABI,
|
|
596
|
+
functionName: "authorizationState",
|
|
597
|
+
args: [parameters.authorizer, parameters.nonce]
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/services/erc3009/getAuthorizationUsage.ts
|
|
602
|
+
import { getLogs } from "viem/actions";
|
|
603
|
+
var AUTHORIZATION_USED_EVENT = {
|
|
604
|
+
type: "event",
|
|
605
|
+
name: "AuthorizationUsed",
|
|
606
|
+
anonymous: false,
|
|
607
|
+
inputs: [
|
|
608
|
+
{ name: "authorizer", type: "address", indexed: true },
|
|
609
|
+
{ name: "nonce", type: "bytes32", indexed: true }
|
|
610
|
+
]
|
|
611
|
+
};
|
|
612
|
+
async function getAuthorizationUsage(client, parameters) {
|
|
613
|
+
validateNonce(parameters.nonce);
|
|
614
|
+
const logs = await getLogs(client, {
|
|
615
|
+
address: parameters.token ?? STABLE_USDT_ADDRESS,
|
|
616
|
+
event: AUTHORIZATION_USED_EVENT,
|
|
617
|
+
args: { authorizer: parameters.authorizer, nonce: parameters.nonce },
|
|
618
|
+
fromBlock: parameters.fromBlock ?? "earliest",
|
|
619
|
+
toBlock: parameters.toBlock ?? "latest"
|
|
620
|
+
});
|
|
621
|
+
const hit = logs.at(-1);
|
|
622
|
+
if (!hit) return { used: false };
|
|
623
|
+
return {
|
|
624
|
+
used: true,
|
|
625
|
+
txHash: hit.transactionHash ?? void 0,
|
|
626
|
+
blockNumber: hit.blockNumber ?? void 0
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// src/services/erc3009/decorator.ts
|
|
631
|
+
function erc3009Actions(client) {
|
|
632
|
+
return {
|
|
633
|
+
signTransferAuthorization: (p) => signTransferAuthorization(client, p),
|
|
634
|
+
signReceiveAuthorization: (p) => signReceiveAuthorization(client, p),
|
|
635
|
+
transferWithAuthorization: (p) => transferWithAuthorization(client, p),
|
|
636
|
+
receiveWithAuthorization: (p) => receiveWithAuthorization(client, p),
|
|
637
|
+
cancelAuthorization: (p) => cancelAuthorization(client, p),
|
|
638
|
+
prepareTransferWithAuthorization: (p) => prepareTransferWithAuthorization(client, p),
|
|
639
|
+
verifyAuthorization: (p) => verifyAuthorization(client, p),
|
|
640
|
+
getAuthorizationState: (p) => getAuthorizationState(client, p),
|
|
641
|
+
getAuthorizationUsage: (p) => getAuthorizationUsage(client, p)
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export {
|
|
646
|
+
StableError,
|
|
647
|
+
StableValidationError,
|
|
648
|
+
StableQuoteError,
|
|
649
|
+
StableTransactionError,
|
|
650
|
+
StableNetworkError,
|
|
651
|
+
StableBulkError,
|
|
652
|
+
ERC3009_ABI,
|
|
653
|
+
STABLE_USDT_ADDRESS,
|
|
654
|
+
STABLE_VAULT_ADDRESS,
|
|
655
|
+
signTransferAuthorization,
|
|
656
|
+
signReceiveAuthorization,
|
|
657
|
+
transferWithAuthorization,
|
|
658
|
+
receiveWithAuthorization,
|
|
659
|
+
cancelAuthorization,
|
|
660
|
+
prepareTransferWithAuthorization,
|
|
661
|
+
verifyAuthorization,
|
|
662
|
+
getAuthorizationState,
|
|
663
|
+
getAuthorizationUsage,
|
|
664
|
+
erc3009Actions
|
|
665
|
+
};
|
package/dist/erc3009.cjs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
var _chunkQQ26UI3Fcjs = require('./chunk-QQ26UI3F.cjs');
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
exports.ERC3009_ABI = _chunkQQ26UI3Fcjs.ERC3009_ABI; exports.cancelAuthorization = _chunkQQ26UI3Fcjs.cancelAuthorization; exports.erc3009Actions = _chunkQQ26UI3Fcjs.erc3009Actions; exports.getAuthorizationState = _chunkQQ26UI3Fcjs.getAuthorizationState; exports.getAuthorizationUsage = _chunkQQ26UI3Fcjs.getAuthorizationUsage; exports.prepareTransferWithAuthorization = _chunkQQ26UI3Fcjs.prepareTransferWithAuthorization; exports.receiveWithAuthorization = _chunkQQ26UI3Fcjs.receiveWithAuthorization; exports.signReceiveAuthorization = _chunkQQ26UI3Fcjs.signReceiveAuthorization; exports.signTransferAuthorization = _chunkQQ26UI3Fcjs.signTransferAuthorization; exports.transferWithAuthorization = _chunkQQ26UI3Fcjs.transferWithAuthorization; exports.verifyAuthorization = _chunkQQ26UI3Fcjs.verifyAuthorization;
|