@t402/extensions 2.5.0 → 2.6.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/dist/cjs/eip2612-gas-sponsoring/index.d.ts +337 -0
- package/dist/cjs/eip2612-gas-sponsoring/index.js +314 -0
- package/dist/cjs/eip2612-gas-sponsoring/index.js.map +1 -0
- package/dist/cjs/erc20-approval-gas-sponsoring/index.d.ts +316 -0
- package/dist/cjs/erc20-approval-gas-sponsoring/index.js +264 -0
- package/dist/cjs/erc20-approval-gas-sponsoring/index.js.map +1 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +650 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/payment-id/index.d.ts +142 -0
- package/dist/cjs/payment-id/index.js +101 -0
- package/dist/cjs/payment-id/index.js.map +1 -0
- package/dist/cjs/sign-in-with-x/index.d.ts +2 -1
- package/dist/cjs/sign-in-with-x/index.js +55 -0
- package/dist/cjs/sign-in-with-x/index.js.map +1 -1
- package/dist/esm/chunk-OAWKCEAR.mjs +226 -0
- package/dist/esm/chunk-OAWKCEAR.mjs.map +1 -0
- package/dist/esm/chunk-S36A7YLQ.mjs +70 -0
- package/dist/esm/chunk-S36A7YLQ.mjs.map +1 -0
- package/dist/esm/chunk-VINC22RD.mjs +278 -0
- package/dist/esm/chunk-VINC22RD.mjs.map +1 -0
- package/dist/esm/{chunk-J3ZMNCIA.mjs → chunk-YKZ5P2JW.mjs} +56 -1
- package/dist/esm/chunk-YKZ5P2JW.mjs.map +1 -0
- package/dist/esm/eip2612-gas-sponsoring/index.d.mts +337 -0
- package/dist/esm/eip2612-gas-sponsoring/index.mjs +27 -0
- package/dist/esm/eip2612-gas-sponsoring/index.mjs.map +1 -0
- package/dist/esm/erc20-approval-gas-sponsoring/index.d.mts +316 -0
- package/dist/esm/erc20-approval-gas-sponsoring/index.mjs +31 -0
- package/dist/esm/erc20-approval-gas-sponsoring/index.mjs.map +1 -0
- package/dist/esm/index.d.mts +3 -0
- package/dist/esm/index.mjs +67 -1
- package/dist/esm/payment-id/index.d.mts +142 -0
- package/dist/esm/payment-id/index.mjs +17 -0
- package/dist/esm/payment-id/index.mjs.map +1 -0
- package/dist/esm/sign-in-with-x/index.d.mts +2 -1
- package/dist/esm/sign-in-with-x/index.mjs +1 -1
- package/package.json +48 -11
- package/dist/esm/chunk-J3ZMNCIA.mjs.map +0 -1
package/dist/cjs/index.js
CHANGED
|
@@ -30,27 +30,57 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
APPROVE_FUNCTION_SELECTOR: () => APPROVE_FUNCTION_SELECTOR,
|
|
33
34
|
BAZAAR: () => BAZAAR,
|
|
35
|
+
EIP2612_GAS_SPONSOR_EXTENSION_KEY: () => EIP2612_GAS_SPONSOR_EXTENSION_KEY,
|
|
36
|
+
EIP2612_GAS_SPONSOR_HEADER_NAME: () => EIP2612_GAS_SPONSOR_HEADER_NAME,
|
|
37
|
+
ERC20_APPROVAL_GAS_SPONSOR_EXTENSION_KEY: () => ERC20_APPROVAL_GAS_SPONSOR_EXTENSION_KEY,
|
|
38
|
+
ERC20_APPROVAL_GAS_SPONSOR_HEADER_NAME: () => ERC20_APPROVAL_GAS_SPONSOR_HEADER_NAME,
|
|
39
|
+
PAYMENT_ID_EXTENSION_KEY: () => PAYMENT_ID_EXTENSION_KEY,
|
|
34
40
|
SIWX_EXTENSION_KEY: () => SIWX_EXTENSION_KEY,
|
|
35
41
|
SIWX_HEADER_NAME: () => SIWX_HEADER_NAME,
|
|
36
42
|
bazaarResourceServerExtension: () => bazaarResourceServerExtension,
|
|
43
|
+
buildPermitCallData: () => buildPermitCallData,
|
|
37
44
|
constructMessage: () => constructMessage,
|
|
45
|
+
createERC20ApprovalGasSponsorPayload: () => createERC20ApprovalGasSponsorPayload,
|
|
46
|
+
createEip2612GasSponsorPayload: () => createEip2612GasSponsorPayload,
|
|
47
|
+
createPaymentIdPayload: () => createPaymentIdPayload,
|
|
48
|
+
createPermitSignature: () => createPermitSignature,
|
|
38
49
|
createSIWxMessage: () => createSIWxMessage,
|
|
39
50
|
createSIWxPayload: () => createSIWxPayload,
|
|
40
51
|
createSIWxTypedData: () => createSIWxTypedData,
|
|
41
52
|
declareDiscoveryExtension: () => declareDiscoveryExtension,
|
|
53
|
+
declareERC20ApprovalGasSponsorExtension: () => declareERC20ApprovalGasSponsorExtension,
|
|
54
|
+
declareEip2612GasSponsorExtension: () => declareEip2612GasSponsorExtension,
|
|
55
|
+
declarePaymentIdExtension: () => declarePaymentIdExtension,
|
|
42
56
|
declareSIWxExtension: () => declareSIWxExtension,
|
|
57
|
+
decodeApproveCalldata: () => decodeApproveCalldata,
|
|
58
|
+
encodeApproveCalldata: () => encodeApproveCalldata,
|
|
59
|
+
encodeERC20ApprovalGasSponsorHeader: () => encodeERC20ApprovalGasSponsorHeader,
|
|
60
|
+
encodeEip2612GasSponsorHeader: () => encodeEip2612GasSponsorHeader,
|
|
61
|
+
encodePaymentIdHeader: () => encodePaymentIdHeader,
|
|
43
62
|
encodeSIWxHeader: () => encodeSIWxHeader,
|
|
44
63
|
extractDiscoveryInfo: () => extractDiscoveryInfo,
|
|
45
64
|
extractDiscoveryInfoFromExtension: () => extractDiscoveryInfoFromExtension,
|
|
46
65
|
extractDiscoveryInfoV1: () => extractDiscoveryInfoV1,
|
|
66
|
+
extractERC20ApprovalGasSponsorPayload: () => extractERC20ApprovalGasSponsorPayload,
|
|
67
|
+
extractEip2612GasSponsorPayload: () => extractEip2612GasSponsorPayload,
|
|
47
68
|
extractResourceMetadataV1: () => extractResourceMetadataV1,
|
|
48
69
|
hashMessage: () => hashMessage,
|
|
49
70
|
isDiscoverableV1: () => isDiscoverableV1,
|
|
71
|
+
parseERC20ApprovalGasSponsorHeader: () => parseERC20ApprovalGasSponsorHeader,
|
|
72
|
+
parseEip2612GasSponsorHeader: () => parseEip2612GasSponsorHeader,
|
|
73
|
+
parsePaymentIdPayload: () => parsePaymentIdPayload,
|
|
50
74
|
parseSIWxHeader: () => parseSIWxHeader,
|
|
75
|
+
processERC20ApprovalPayload: () => processERC20ApprovalPayload,
|
|
51
76
|
signSIWxMessage: () => signSIWxMessage,
|
|
52
77
|
validateAndExtract: () => validateAndExtract,
|
|
78
|
+
validateAndExtractApproval: () => validateAndExtractApproval,
|
|
79
|
+
validateAndExtractPermit: () => validateAndExtractPermit,
|
|
53
80
|
validateDiscoveryExtension: () => validateDiscoveryExtension,
|
|
81
|
+
validateERC20ApprovalGasSponsorPayload: () => validateERC20ApprovalGasSponsorPayload,
|
|
82
|
+
validateEip2612GasSponsorPayload: () => validateEip2612GasSponsorPayload,
|
|
83
|
+
validatePaymentId: () => validatePaymentId,
|
|
54
84
|
validateSIWxMessage: () => validateSIWxMessage,
|
|
55
85
|
verifyEIP6492Signature: () => verifyEIP6492Signature,
|
|
56
86
|
verifySIWxSignature: () => verifySIWxSignature,
|
|
@@ -492,6 +522,7 @@ function withBazaar(client) {
|
|
|
492
522
|
// src/sign-in-with-x/server.ts
|
|
493
523
|
var import_crypto = require("crypto");
|
|
494
524
|
var import_sha3 = require("@noble/hashes/sha3");
|
|
525
|
+
var import_sha2 = require("@noble/hashes/sha2");
|
|
495
526
|
var import_secp256k1 = require("@noble/curves/secp256k1");
|
|
496
527
|
var import_ed25519 = require("@noble/curves/ed25519");
|
|
497
528
|
var import_utils = require("@noble/hashes/utils");
|
|
@@ -622,7 +653,10 @@ function detectSignatureScheme(chainId) {
|
|
|
622
653
|
switch (namespace) {
|
|
623
654
|
case "solana":
|
|
624
655
|
case "stellar":
|
|
656
|
+
case "ton":
|
|
625
657
|
return "ed25519";
|
|
658
|
+
case "tron":
|
|
659
|
+
return "tron";
|
|
626
660
|
case "eip155":
|
|
627
661
|
default:
|
|
628
662
|
return "evm";
|
|
@@ -685,6 +719,44 @@ function decodeBase58(str) {
|
|
|
685
719
|
}
|
|
686
720
|
return new Uint8Array(bytes.reverse());
|
|
687
721
|
}
|
|
722
|
+
function encodeBase58(bytes) {
|
|
723
|
+
const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
724
|
+
if (bytes.length === 0) return "";
|
|
725
|
+
let leadingZeros = 0;
|
|
726
|
+
for (const b of bytes) {
|
|
727
|
+
if (b !== 0) break;
|
|
728
|
+
leadingZeros++;
|
|
729
|
+
}
|
|
730
|
+
const digits = [0];
|
|
731
|
+
for (const b of bytes) {
|
|
732
|
+
let carry = b;
|
|
733
|
+
for (let j = 0; j < digits.length; j++) {
|
|
734
|
+
carry += digits[j] * 256;
|
|
735
|
+
digits[j] = carry % 58;
|
|
736
|
+
carry = Math.floor(carry / 58);
|
|
737
|
+
}
|
|
738
|
+
while (carry > 0) {
|
|
739
|
+
digits.push(carry % 58);
|
|
740
|
+
carry = Math.floor(carry / 58);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
let result = ALPHABET[0].repeat(leadingZeros);
|
|
744
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
745
|
+
result += ALPHABET[digits[i]];
|
|
746
|
+
}
|
|
747
|
+
return result;
|
|
748
|
+
}
|
|
749
|
+
function evmAddressToTron(evmAddress) {
|
|
750
|
+
const addr = evmAddress.toLowerCase().replace("0x", "");
|
|
751
|
+
const addressBytes = (0, import_utils.hexToBytes)("41" + addr);
|
|
752
|
+
const hash1 = (0, import_sha2.sha256)(addressBytes);
|
|
753
|
+
const hash2 = (0, import_sha2.sha256)(hash1);
|
|
754
|
+
const checksum = hash2.slice(0, 4);
|
|
755
|
+
const fullAddress = new Uint8Array(addressBytes.length + 4);
|
|
756
|
+
fullAddress.set(addressBytes, 0);
|
|
757
|
+
fullAddress.set(checksum, addressBytes.length);
|
|
758
|
+
return encodeBase58(fullAddress);
|
|
759
|
+
}
|
|
688
760
|
async function verifySIWxSignature(message, signature, options = {}) {
|
|
689
761
|
const { checkSmartWallet = false } = options;
|
|
690
762
|
try {
|
|
@@ -700,6 +772,19 @@ async function verifySIWxSignature(message, signature, options = {}) {
|
|
|
700
772
|
error: "Ed25519 signature verification failed"
|
|
701
773
|
};
|
|
702
774
|
}
|
|
775
|
+
if (scheme === "tron") {
|
|
776
|
+
const messageHash2 = hashMessage(messageText);
|
|
777
|
+
const recoveredEvmAddress = recoverAddress(messageHash2, signature);
|
|
778
|
+
const recoveredTronAddress = evmAddressToTron(recoveredEvmAddress);
|
|
779
|
+
if (recoveredTronAddress === message.address) {
|
|
780
|
+
return { valid: true, address: message.address };
|
|
781
|
+
}
|
|
782
|
+
return {
|
|
783
|
+
valid: false,
|
|
784
|
+
address: recoveredTronAddress,
|
|
785
|
+
error: `Signature mismatch: expected ${message.address}, recovered ${recoveredTronAddress}`
|
|
786
|
+
};
|
|
787
|
+
}
|
|
703
788
|
const messageHash = hashMessage(messageText);
|
|
704
789
|
const recoveredAddress = recoverAddress(messageHash, signature);
|
|
705
790
|
if (recoveredAddress.toLowerCase() !== message.address.toLowerCase()) {
|
|
@@ -967,29 +1052,594 @@ async function createSIWxPayload(serverExtension, signer) {
|
|
|
967
1052
|
}
|
|
968
1053
|
var SIWX_EXTENSION_KEY = "siwx";
|
|
969
1054
|
var SIWX_HEADER_NAME = "X-T402-SIWx";
|
|
1055
|
+
|
|
1056
|
+
// src/payment-id/types.ts
|
|
1057
|
+
var PAYMENT_ID_EXTENSION_KEY = "paymentId";
|
|
1058
|
+
|
|
1059
|
+
// src/payment-id/server.ts
|
|
1060
|
+
var import_crypto2 = require("crypto");
|
|
1061
|
+
var PAYMENT_ID_SCHEMA = {
|
|
1062
|
+
type: "object",
|
|
1063
|
+
required: ["id"],
|
|
1064
|
+
properties: {
|
|
1065
|
+
id: { type: "string", format: "uuid" },
|
|
1066
|
+
clientId: { type: "string" }
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
function declarePaymentIdExtension(options = {}) {
|
|
1070
|
+
const info = {
|
|
1071
|
+
id: options.id || (0, import_crypto2.randomUUID)(),
|
|
1072
|
+
idempotencyKey: options.idempotencyKey,
|
|
1073
|
+
groupId: options.groupId,
|
|
1074
|
+
metadata: options.metadata
|
|
1075
|
+
};
|
|
1076
|
+
return {
|
|
1077
|
+
info,
|
|
1078
|
+
schema: PAYMENT_ID_SCHEMA
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
function parsePaymentIdPayload(extensions) {
|
|
1082
|
+
if (!extensions || !(PAYMENT_ID_EXTENSION_KEY in extensions)) {
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
const raw = extensions[PAYMENT_ID_EXTENSION_KEY];
|
|
1086
|
+
if (typeof raw !== "object" || raw === null) {
|
|
1087
|
+
throw new Error("Invalid paymentId extension: expected object");
|
|
1088
|
+
}
|
|
1089
|
+
const obj = raw;
|
|
1090
|
+
if (typeof obj.id !== "string" || obj.id.length === 0) {
|
|
1091
|
+
throw new Error("Invalid paymentId extension: missing or empty id");
|
|
1092
|
+
}
|
|
1093
|
+
return {
|
|
1094
|
+
id: obj.id,
|
|
1095
|
+
clientId: typeof obj.clientId === "string" ? obj.clientId : void 0
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
function validatePaymentId(payload, expected) {
|
|
1099
|
+
return payload.id === expected.id;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// src/payment-id/client.ts
|
|
1103
|
+
function createPaymentIdPayload(extension, clientId) {
|
|
1104
|
+
return {
|
|
1105
|
+
id: extension.info.id,
|
|
1106
|
+
clientId
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
function encodePaymentIdHeader(payload) {
|
|
1110
|
+
const json = JSON.stringify(payload);
|
|
1111
|
+
if (typeof Buffer !== "undefined") {
|
|
1112
|
+
return Buffer.from(json, "utf-8").toString("base64");
|
|
1113
|
+
}
|
|
1114
|
+
return btoa(json);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// src/eip2612-gas-sponsoring/server.ts
|
|
1118
|
+
var EIP2612_GAS_SPONSOR_SCHEMA = {
|
|
1119
|
+
type: "object",
|
|
1120
|
+
required: ["network", "permitSignature", "owner", "spender", "value", "deadline", "v", "r", "s"],
|
|
1121
|
+
properties: {
|
|
1122
|
+
network: { type: "string" },
|
|
1123
|
+
permitSignature: { type: "string" },
|
|
1124
|
+
owner: { type: "string" },
|
|
1125
|
+
spender: { type: "string" },
|
|
1126
|
+
value: { type: "string" },
|
|
1127
|
+
deadline: { type: "number" },
|
|
1128
|
+
v: { type: "number" },
|
|
1129
|
+
r: { type: "string" },
|
|
1130
|
+
s: { type: "string" }
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
function declareEip2612GasSponsorExtension(options) {
|
|
1134
|
+
const info = {
|
|
1135
|
+
sponsoredNetworks: options.sponsoredNetworks,
|
|
1136
|
+
maxAmount: options.maxAmount,
|
|
1137
|
+
permitDeadline: options.permitDeadline ?? 300,
|
|
1138
|
+
sponsorAddress: options.sponsorAddress
|
|
1139
|
+
};
|
|
1140
|
+
return {
|
|
1141
|
+
info,
|
|
1142
|
+
schema: EIP2612_GAS_SPONSOR_SCHEMA
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
function parseEip2612GasSponsorHeader(header) {
|
|
1146
|
+
if (!header) {
|
|
1147
|
+
throw new Error("Missing EIP-2612 gas sponsor header");
|
|
1148
|
+
}
|
|
1149
|
+
try {
|
|
1150
|
+
const decoded = Buffer.from(header, "base64").toString("utf-8");
|
|
1151
|
+
const payload = JSON.parse(decoded);
|
|
1152
|
+
const required = [
|
|
1153
|
+
"network",
|
|
1154
|
+
"permitSignature",
|
|
1155
|
+
"owner",
|
|
1156
|
+
"spender",
|
|
1157
|
+
"value",
|
|
1158
|
+
"deadline",
|
|
1159
|
+
"v",
|
|
1160
|
+
"r",
|
|
1161
|
+
"s"
|
|
1162
|
+
];
|
|
1163
|
+
for (const field of required) {
|
|
1164
|
+
if (!(field in payload)) {
|
|
1165
|
+
throw new Error(`Missing required field: ${field}`);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
return payload;
|
|
1169
|
+
} catch (error) {
|
|
1170
|
+
if (error instanceof SyntaxError) {
|
|
1171
|
+
throw new Error("Invalid EIP-2612 gas sponsor header: malformed JSON");
|
|
1172
|
+
}
|
|
1173
|
+
throw error;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
function validateEip2612GasSponsorPayload(payload, extensionInfo, options = {}) {
|
|
1177
|
+
const now = options.now ? options.now() : Date.now();
|
|
1178
|
+
const nowSeconds = Math.floor(now / 1e3);
|
|
1179
|
+
if (!extensionInfo.sponsoredNetworks.includes(payload.network)) {
|
|
1180
|
+
return {
|
|
1181
|
+
valid: false,
|
|
1182
|
+
error: `Network ${payload.network} is not in sponsored networks: ${extensionInfo.sponsoredNetworks.join(", ")}`
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
const payloadValue = BigInt(payload.value);
|
|
1186
|
+
const maxAmount = BigInt(extensionInfo.maxAmount);
|
|
1187
|
+
if (payloadValue > maxAmount) {
|
|
1188
|
+
return {
|
|
1189
|
+
valid: false,
|
|
1190
|
+
error: `Value ${payload.value} exceeds maximum amount ${extensionInfo.maxAmount}`
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
if (payload.deadline <= nowSeconds) {
|
|
1194
|
+
return {
|
|
1195
|
+
valid: false,
|
|
1196
|
+
error: "Permit deadline has expired"
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
const maxDeadline = nowSeconds + extensionInfo.permitDeadline;
|
|
1200
|
+
if (payload.deadline > maxDeadline) {
|
|
1201
|
+
return {
|
|
1202
|
+
valid: false,
|
|
1203
|
+
error: `Permit deadline ${payload.deadline} exceeds maximum allowed deadline ${maxDeadline}`
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
if (payload.spender.toLowerCase() !== extensionInfo.sponsorAddress.toLowerCase()) {
|
|
1207
|
+
return {
|
|
1208
|
+
valid: false,
|
|
1209
|
+
error: `Spender ${payload.spender} does not match sponsor address ${extensionInfo.sponsorAddress}`
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
const sigHex = payload.permitSignature.startsWith("0x") ? payload.permitSignature.slice(2) : payload.permitSignature;
|
|
1213
|
+
if (sigHex.length !== 130) {
|
|
1214
|
+
return {
|
|
1215
|
+
valid: false,
|
|
1216
|
+
error: `Invalid permit signature length: expected 130 hex chars, got ${sigHex.length}`
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
if (payload.v !== 27 && payload.v !== 28) {
|
|
1220
|
+
return {
|
|
1221
|
+
valid: false,
|
|
1222
|
+
error: `Invalid v value: expected 27 or 28, got ${payload.v}`
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
const rHex = payload.r.startsWith("0x") ? payload.r.slice(2) : payload.r;
|
|
1226
|
+
if (rHex.length !== 64) {
|
|
1227
|
+
return {
|
|
1228
|
+
valid: false,
|
|
1229
|
+
error: `Invalid r length: expected 64 hex chars, got ${rHex.length}`
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
const sHex = payload.s.startsWith("0x") ? payload.s.slice(2) : payload.s;
|
|
1233
|
+
if (sHex.length !== 64) {
|
|
1234
|
+
return {
|
|
1235
|
+
valid: false,
|
|
1236
|
+
error: `Invalid s length: expected 64 hex chars, got ${sHex.length}`
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
return { valid: true };
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// src/eip2612-gas-sponsoring/client.ts
|
|
1243
|
+
var EIP2612_GAS_SPONSOR_EXTENSION_KEY = "eip2612GasSponsoring";
|
|
1244
|
+
var EIP2612_GAS_SPONSOR_HEADER_NAME = "X-T402-EIP2612-Gas-Sponsoring";
|
|
1245
|
+
async function createPermitSignature(params) {
|
|
1246
|
+
const { signer, tokenAddress, tokenName, chainId, spender, value, deadline, nonce = 0 } = params;
|
|
1247
|
+
const domain = {
|
|
1248
|
+
name: tokenName,
|
|
1249
|
+
version: "1",
|
|
1250
|
+
chainId,
|
|
1251
|
+
verifyingContract: tokenAddress
|
|
1252
|
+
};
|
|
1253
|
+
const types = {
|
|
1254
|
+
Permit: [
|
|
1255
|
+
{ name: "owner", type: "address" },
|
|
1256
|
+
{ name: "spender", type: "address" },
|
|
1257
|
+
{ name: "value", type: "uint256" },
|
|
1258
|
+
{ name: "nonce", type: "uint256" },
|
|
1259
|
+
{ name: "deadline", type: "uint256" }
|
|
1260
|
+
]
|
|
1261
|
+
};
|
|
1262
|
+
const message = {
|
|
1263
|
+
owner: signer.address,
|
|
1264
|
+
spender,
|
|
1265
|
+
value,
|
|
1266
|
+
nonce,
|
|
1267
|
+
deadline
|
|
1268
|
+
};
|
|
1269
|
+
const signature = await signer.signTypedData({
|
|
1270
|
+
domain,
|
|
1271
|
+
types,
|
|
1272
|
+
primaryType: "Permit",
|
|
1273
|
+
message
|
|
1274
|
+
});
|
|
1275
|
+
const sigHex = signature.startsWith("0x") ? signature.slice(2) : signature;
|
|
1276
|
+
if (sigHex.length !== 130) {
|
|
1277
|
+
throw new Error(`Invalid signature length: expected 130 hex chars, got ${sigHex.length}`);
|
|
1278
|
+
}
|
|
1279
|
+
const r = "0x" + sigHex.slice(0, 64);
|
|
1280
|
+
const s = "0x" + sigHex.slice(64, 128);
|
|
1281
|
+
let v = parseInt(sigHex.slice(128, 130), 16);
|
|
1282
|
+
if (v < 27) {
|
|
1283
|
+
v += 27;
|
|
1284
|
+
}
|
|
1285
|
+
return {
|
|
1286
|
+
owner: signer.address,
|
|
1287
|
+
spender,
|
|
1288
|
+
value,
|
|
1289
|
+
deadline,
|
|
1290
|
+
v,
|
|
1291
|
+
r,
|
|
1292
|
+
s,
|
|
1293
|
+
permitSignature: signature.startsWith("0x") ? signature : "0x" + signature
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
function createEip2612GasSponsorPayload(permit, network) {
|
|
1297
|
+
return {
|
|
1298
|
+
network,
|
|
1299
|
+
permitSignature: permit.permitSignature,
|
|
1300
|
+
owner: permit.owner,
|
|
1301
|
+
spender: permit.spender,
|
|
1302
|
+
value: permit.value,
|
|
1303
|
+
deadline: permit.deadline,
|
|
1304
|
+
v: permit.v,
|
|
1305
|
+
r: permit.r,
|
|
1306
|
+
s: permit.s
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
function encodeEip2612GasSponsorHeader(payload) {
|
|
1310
|
+
const json = JSON.stringify(payload);
|
|
1311
|
+
if (typeof Buffer !== "undefined") {
|
|
1312
|
+
return Buffer.from(json, "utf-8").toString("base64");
|
|
1313
|
+
}
|
|
1314
|
+
return btoa(json);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// src/eip2612-gas-sponsoring/facilitator.ts
|
|
1318
|
+
function extractEip2612GasSponsorPayload(extensions) {
|
|
1319
|
+
if (!extensions) {
|
|
1320
|
+
return null;
|
|
1321
|
+
}
|
|
1322
|
+
const raw = extensions[EIP2612_GAS_SPONSOR_EXTENSION_KEY];
|
|
1323
|
+
if (!raw || typeof raw !== "object") {
|
|
1324
|
+
return null;
|
|
1325
|
+
}
|
|
1326
|
+
const payload = raw;
|
|
1327
|
+
const required = [
|
|
1328
|
+
"network",
|
|
1329
|
+
"permitSignature",
|
|
1330
|
+
"owner",
|
|
1331
|
+
"spender",
|
|
1332
|
+
"value",
|
|
1333
|
+
"deadline",
|
|
1334
|
+
"v",
|
|
1335
|
+
"r",
|
|
1336
|
+
"s"
|
|
1337
|
+
];
|
|
1338
|
+
for (const field of required) {
|
|
1339
|
+
if (!(field in payload)) {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
return {
|
|
1344
|
+
network: payload.network,
|
|
1345
|
+
permitSignature: payload.permitSignature,
|
|
1346
|
+
owner: payload.owner,
|
|
1347
|
+
spender: payload.spender,
|
|
1348
|
+
value: payload.value,
|
|
1349
|
+
deadline: payload.deadline,
|
|
1350
|
+
v: payload.v,
|
|
1351
|
+
r: payload.r,
|
|
1352
|
+
s: payload.s
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
function validateAndExtractPermit(extensions, extensionInfo) {
|
|
1356
|
+
const payload = extractEip2612GasSponsorPayload(extensions);
|
|
1357
|
+
if (!payload) {
|
|
1358
|
+
return {
|
|
1359
|
+
valid: false,
|
|
1360
|
+
error: `Missing or invalid ${EIP2612_GAS_SPONSOR_EXTENSION_KEY} extension in payment`
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
const result = validateEip2612GasSponsorPayload(payload, extensionInfo);
|
|
1364
|
+
if (!result.valid) {
|
|
1365
|
+
return result;
|
|
1366
|
+
}
|
|
1367
|
+
return { valid: true, payload };
|
|
1368
|
+
}
|
|
1369
|
+
function buildPermitCallData(payload) {
|
|
1370
|
+
return {
|
|
1371
|
+
owner: payload.owner,
|
|
1372
|
+
spender: payload.spender,
|
|
1373
|
+
value: payload.value,
|
|
1374
|
+
deadline: payload.deadline,
|
|
1375
|
+
v: payload.v,
|
|
1376
|
+
r: payload.r,
|
|
1377
|
+
s: payload.s
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// src/erc20-approval-gas-sponsoring/server.ts
|
|
1382
|
+
var ERC20_APPROVAL_GAS_SPONSOR_SCHEMA = {
|
|
1383
|
+
type: "object",
|
|
1384
|
+
required: ["network", "from", "asset", "amount", "signedApprovalTx", "chainId"],
|
|
1385
|
+
properties: {
|
|
1386
|
+
network: { type: "string" },
|
|
1387
|
+
from: { type: "string" },
|
|
1388
|
+
asset: { type: "string" },
|
|
1389
|
+
amount: { type: "string" },
|
|
1390
|
+
signedApprovalTx: { type: "string" },
|
|
1391
|
+
chainId: { type: "number" },
|
|
1392
|
+
nonce: { type: "number" }
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
function declareERC20ApprovalGasSponsorExtension(options) {
|
|
1396
|
+
const info = {
|
|
1397
|
+
sponsoredNetworks: options.sponsoredNetworks,
|
|
1398
|
+
maxAmount: options.maxAmount,
|
|
1399
|
+
sponsorAddress: options.sponsorAddress,
|
|
1400
|
+
requiresAtomicBatch: options.requiresAtomicBatch ?? false
|
|
1401
|
+
};
|
|
1402
|
+
if (options.permit2Address) {
|
|
1403
|
+
info.permit2Address = options.permit2Address;
|
|
1404
|
+
}
|
|
1405
|
+
return {
|
|
1406
|
+
info,
|
|
1407
|
+
schema: ERC20_APPROVAL_GAS_SPONSOR_SCHEMA
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
function parseERC20ApprovalGasSponsorHeader(header) {
|
|
1411
|
+
if (!header) {
|
|
1412
|
+
throw new Error("Missing ERC-20 approval gas sponsor header");
|
|
1413
|
+
}
|
|
1414
|
+
try {
|
|
1415
|
+
const decoded = Buffer.from(header, "base64").toString("utf-8");
|
|
1416
|
+
const payload = JSON.parse(decoded);
|
|
1417
|
+
const required = ["network", "from", "asset", "amount", "signedApprovalTx", "chainId"];
|
|
1418
|
+
for (const field of required) {
|
|
1419
|
+
if (!(field in payload)) {
|
|
1420
|
+
throw new Error(`Missing required field: ${field}`);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
return payload;
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
if (error instanceof SyntaxError) {
|
|
1426
|
+
throw new Error("Invalid ERC-20 approval gas sponsor header: malformed JSON");
|
|
1427
|
+
}
|
|
1428
|
+
throw error;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
function validateERC20ApprovalGasSponsorPayload(payload, extensionInfo, options = {}) {
|
|
1432
|
+
if (!extensionInfo.sponsoredNetworks.includes(payload.network)) {
|
|
1433
|
+
return {
|
|
1434
|
+
valid: false,
|
|
1435
|
+
error: `Network ${payload.network} is not in sponsored networks: ${extensionInfo.sponsoredNetworks.join(", ")}`
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
const payloadAmount = BigInt(payload.amount);
|
|
1439
|
+
const maxAmount = BigInt(extensionInfo.maxAmount);
|
|
1440
|
+
if (payloadAmount > maxAmount) {
|
|
1441
|
+
return {
|
|
1442
|
+
valid: false,
|
|
1443
|
+
error: `Amount ${payload.amount} exceeds maximum amount ${extensionInfo.maxAmount}`
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
if (options.expectedChainIds) {
|
|
1447
|
+
const expectedChainId = options.expectedChainIds[payload.network];
|
|
1448
|
+
if (expectedChainId !== void 0 && payload.chainId !== expectedChainId) {
|
|
1449
|
+
return {
|
|
1450
|
+
valid: false,
|
|
1451
|
+
error: `Chain ID ${payload.chainId} does not match expected chain ID ${expectedChainId} for network ${payload.network}`
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
const txHex = payload.signedApprovalTx.startsWith("0x") ? payload.signedApprovalTx.slice(2) : payload.signedApprovalTx;
|
|
1456
|
+
if (txHex.length === 0) {
|
|
1457
|
+
return {
|
|
1458
|
+
valid: false,
|
|
1459
|
+
error: "Signed approval transaction is empty"
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
if (!/^[0-9a-fA-F]+$/.test(txHex)) {
|
|
1463
|
+
return {
|
|
1464
|
+
valid: false,
|
|
1465
|
+
error: "Signed approval transaction is not valid hex"
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
const fromHex = payload.from.startsWith("0x") ? payload.from.slice(2) : payload.from;
|
|
1469
|
+
if (fromHex.length !== 40 || !/^[0-9a-fA-F]+$/.test(fromHex)) {
|
|
1470
|
+
return {
|
|
1471
|
+
valid: false,
|
|
1472
|
+
error: `Invalid from address: ${payload.from}`
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
const assetHex = payload.asset.startsWith("0x") ? payload.asset.slice(2) : payload.asset;
|
|
1476
|
+
if (assetHex.length !== 40 || !/^[0-9a-fA-F]+$/.test(assetHex)) {
|
|
1477
|
+
return {
|
|
1478
|
+
valid: false,
|
|
1479
|
+
error: `Invalid asset address: ${payload.asset}`
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
return { valid: true };
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// src/erc20-approval-gas-sponsoring/client.ts
|
|
1486
|
+
var ERC20_APPROVAL_GAS_SPONSOR_EXTENSION_KEY = "erc20ApprovalGasSponsoring";
|
|
1487
|
+
var ERC20_APPROVAL_GAS_SPONSOR_HEADER_NAME = "X-T402-ERC20-Approval-Gas-Sponsoring";
|
|
1488
|
+
var APPROVE_FUNCTION_SELECTOR = "0x095ea7b3";
|
|
1489
|
+
function encodeApproveCalldata(spender, amount) {
|
|
1490
|
+
const spenderHex = spender.startsWith("0x") ? spender.slice(2) : spender;
|
|
1491
|
+
const paddedSpender = spenderHex.toLowerCase().padStart(64, "0");
|
|
1492
|
+
const amountBigInt = BigInt(amount);
|
|
1493
|
+
const amountHex = amountBigInt.toString(16).padStart(64, "0");
|
|
1494
|
+
return APPROVE_FUNCTION_SELECTOR + paddedSpender + amountHex;
|
|
1495
|
+
}
|
|
1496
|
+
function createERC20ApprovalGasSponsorPayload(_info, params) {
|
|
1497
|
+
const payload = {
|
|
1498
|
+
network: params.network,
|
|
1499
|
+
from: params.from,
|
|
1500
|
+
asset: params.asset,
|
|
1501
|
+
amount: params.amount,
|
|
1502
|
+
signedApprovalTx: params.signedApprovalTx.startsWith("0x") ? params.signedApprovalTx : "0x" + params.signedApprovalTx,
|
|
1503
|
+
chainId: params.chainId
|
|
1504
|
+
};
|
|
1505
|
+
if (params.nonce !== void 0) {
|
|
1506
|
+
payload.nonce = params.nonce;
|
|
1507
|
+
}
|
|
1508
|
+
return payload;
|
|
1509
|
+
}
|
|
1510
|
+
function encodeERC20ApprovalGasSponsorHeader(payload) {
|
|
1511
|
+
const json = JSON.stringify(payload);
|
|
1512
|
+
if (typeof Buffer !== "undefined") {
|
|
1513
|
+
return Buffer.from(json, "utf-8").toString("base64");
|
|
1514
|
+
}
|
|
1515
|
+
return btoa(json);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
// src/erc20-approval-gas-sponsoring/facilitator.ts
|
|
1519
|
+
function extractERC20ApprovalGasSponsorPayload(extensions) {
|
|
1520
|
+
if (!extensions) {
|
|
1521
|
+
return null;
|
|
1522
|
+
}
|
|
1523
|
+
const raw = extensions[ERC20_APPROVAL_GAS_SPONSOR_EXTENSION_KEY];
|
|
1524
|
+
if (!raw || typeof raw !== "object") {
|
|
1525
|
+
return null;
|
|
1526
|
+
}
|
|
1527
|
+
const payload = raw;
|
|
1528
|
+
const required = ["network", "from", "asset", "amount", "signedApprovalTx", "chainId"];
|
|
1529
|
+
for (const field of required) {
|
|
1530
|
+
if (!(field in payload)) {
|
|
1531
|
+
return null;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
const result = {
|
|
1535
|
+
network: payload.network,
|
|
1536
|
+
from: payload.from,
|
|
1537
|
+
asset: payload.asset,
|
|
1538
|
+
amount: payload.amount,
|
|
1539
|
+
signedApprovalTx: payload.signedApprovalTx,
|
|
1540
|
+
chainId: payload.chainId
|
|
1541
|
+
};
|
|
1542
|
+
if ("nonce" in payload && payload.nonce !== void 0) {
|
|
1543
|
+
result.nonce = payload.nonce;
|
|
1544
|
+
}
|
|
1545
|
+
return result;
|
|
1546
|
+
}
|
|
1547
|
+
function processERC20ApprovalPayload(payload, extensionInfo) {
|
|
1548
|
+
const validationResult = validateERC20ApprovalGasSponsorPayload(payload, extensionInfo);
|
|
1549
|
+
if (!validationResult.valid) {
|
|
1550
|
+
return validationResult;
|
|
1551
|
+
}
|
|
1552
|
+
const txHex = payload.signedApprovalTx.startsWith("0x") ? payload.signedApprovalTx.slice(2) : payload.signedApprovalTx;
|
|
1553
|
+
if (txHex.length < 8) {
|
|
1554
|
+
return {
|
|
1555
|
+
valid: false,
|
|
1556
|
+
error: "Signed approval transaction is too short to contain function selector"
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
return { valid: true };
|
|
1560
|
+
}
|
|
1561
|
+
function validateAndExtractApproval(extensions, extensionInfo) {
|
|
1562
|
+
const payload = extractERC20ApprovalGasSponsorPayload(extensions);
|
|
1563
|
+
if (!payload) {
|
|
1564
|
+
return {
|
|
1565
|
+
valid: false,
|
|
1566
|
+
error: `Missing or invalid ${ERC20_APPROVAL_GAS_SPONSOR_EXTENSION_KEY} extension in payment`
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
const result = processERC20ApprovalPayload(payload, extensionInfo);
|
|
1570
|
+
if (!result.valid) {
|
|
1571
|
+
return result;
|
|
1572
|
+
}
|
|
1573
|
+
return { valid: true, payload };
|
|
1574
|
+
}
|
|
1575
|
+
function decodeApproveCalldata(calldata) {
|
|
1576
|
+
const hex = calldata.startsWith("0x") ? calldata.slice(2) : calldata;
|
|
1577
|
+
if (hex.length < 136) {
|
|
1578
|
+
return null;
|
|
1579
|
+
}
|
|
1580
|
+
const selector = "0x" + hex.slice(0, 8);
|
|
1581
|
+
if (selector !== APPROVE_FUNCTION_SELECTOR) {
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
const spenderWord = hex.slice(8, 72);
|
|
1585
|
+
const spender = "0x" + spenderWord.slice(24);
|
|
1586
|
+
const amountHex = hex.slice(72, 136);
|
|
1587
|
+
const amount = BigInt("0x" + amountHex).toString(10);
|
|
1588
|
+
return { spender, amount };
|
|
1589
|
+
}
|
|
970
1590
|
// Annotate the CommonJS export names for ESM import in node:
|
|
971
1591
|
0 && (module.exports = {
|
|
1592
|
+
APPROVE_FUNCTION_SELECTOR,
|
|
972
1593
|
BAZAAR,
|
|
1594
|
+
EIP2612_GAS_SPONSOR_EXTENSION_KEY,
|
|
1595
|
+
EIP2612_GAS_SPONSOR_HEADER_NAME,
|
|
1596
|
+
ERC20_APPROVAL_GAS_SPONSOR_EXTENSION_KEY,
|
|
1597
|
+
ERC20_APPROVAL_GAS_SPONSOR_HEADER_NAME,
|
|
1598
|
+
PAYMENT_ID_EXTENSION_KEY,
|
|
973
1599
|
SIWX_EXTENSION_KEY,
|
|
974
1600
|
SIWX_HEADER_NAME,
|
|
975
1601
|
bazaarResourceServerExtension,
|
|
1602
|
+
buildPermitCallData,
|
|
976
1603
|
constructMessage,
|
|
1604
|
+
createERC20ApprovalGasSponsorPayload,
|
|
1605
|
+
createEip2612GasSponsorPayload,
|
|
1606
|
+
createPaymentIdPayload,
|
|
1607
|
+
createPermitSignature,
|
|
977
1608
|
createSIWxMessage,
|
|
978
1609
|
createSIWxPayload,
|
|
979
1610
|
createSIWxTypedData,
|
|
980
1611
|
declareDiscoveryExtension,
|
|
1612
|
+
declareERC20ApprovalGasSponsorExtension,
|
|
1613
|
+
declareEip2612GasSponsorExtension,
|
|
1614
|
+
declarePaymentIdExtension,
|
|
981
1615
|
declareSIWxExtension,
|
|
1616
|
+
decodeApproveCalldata,
|
|
1617
|
+
encodeApproveCalldata,
|
|
1618
|
+
encodeERC20ApprovalGasSponsorHeader,
|
|
1619
|
+
encodeEip2612GasSponsorHeader,
|
|
1620
|
+
encodePaymentIdHeader,
|
|
982
1621
|
encodeSIWxHeader,
|
|
983
1622
|
extractDiscoveryInfo,
|
|
984
1623
|
extractDiscoveryInfoFromExtension,
|
|
985
1624
|
extractDiscoveryInfoV1,
|
|
1625
|
+
extractERC20ApprovalGasSponsorPayload,
|
|
1626
|
+
extractEip2612GasSponsorPayload,
|
|
986
1627
|
extractResourceMetadataV1,
|
|
987
1628
|
hashMessage,
|
|
988
1629
|
isDiscoverableV1,
|
|
1630
|
+
parseERC20ApprovalGasSponsorHeader,
|
|
1631
|
+
parseEip2612GasSponsorHeader,
|
|
1632
|
+
parsePaymentIdPayload,
|
|
989
1633
|
parseSIWxHeader,
|
|
1634
|
+
processERC20ApprovalPayload,
|
|
990
1635
|
signSIWxMessage,
|
|
991
1636
|
validateAndExtract,
|
|
1637
|
+
validateAndExtractApproval,
|
|
1638
|
+
validateAndExtractPermit,
|
|
992
1639
|
validateDiscoveryExtension,
|
|
1640
|
+
validateERC20ApprovalGasSponsorPayload,
|
|
1641
|
+
validateEip2612GasSponsorPayload,
|
|
1642
|
+
validatePaymentId,
|
|
993
1643
|
validateSIWxMessage,
|
|
994
1644
|
verifyEIP6492Signature,
|
|
995
1645
|
verifySIWxSignature,
|