@reclaimprotocol/js-sdk
Version:
Designed to request proofs from the Reclaim protocol and manage the flow of claims and witness interactions.
1,593 lines (1,577 loc) • 52.3 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// package.json
var require_package = __commonJS({
"package.json"(exports2, module2) {
module2.exports = {
name: "@reclaimprotocol/js-sdk",
version: "2.3.3",
description: "Designed to request proofs from the Reclaim protocol and manage the flow of claims and witness interactions.",
main: "dist/index.js",
types: "dist/index.d.ts",
keywords: [
"reclaim",
"protocol",
"blockchain",
"proof",
"verification",
"identity",
"claims",
"witness",
"sdk",
"javascript",
"typescript",
"decentralized",
"web3"
],
files: [
"dist"
],
tsup: {
entry: [
"src/index.ts"
],
splitting: false,
sourcemap: true,
clean: true
},
scripts: {
build: "sh scripts/build.sh",
release: "release-it",
test: 'echo "Error: no test specified" && exit 1',
commitlint: "commitlint --edit"
},
repository: {
type: "git",
url: "https://github.com/reclaimprotocol/reclaim-js-sdk"
},
author: "ali <ali@creatoros.co>",
license: "See License in <https://github.com/reclaimprotocol/.github/blob/main/LICENSE>",
bugs: {
url: "https://github.com/reclaimprotocol/reclaim-js-sdk/issues"
},
homepage: "https://github.com/reclaimprotocol/reclaim-js-sdk/",
publishConfig: {
registry: "https://registry.npmjs.org/",
access: "public"
},
"release-it": {
git: {
commitMessage: "chore: release ${version}",
tagName: "v${version}"
},
npm: {
publish: true,
tag: "latest"
},
github: {
release: true
},
plugins: {
"@release-it/conventional-changelog": {
preset: "angular"
}
}
},
devDependencies: {
"@bconnorwhite/bob": "^2.9.5",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@release-it/conventional-changelog": "^5.0.0",
"@types/qs": "^6.9.11",
"@types/url-parse": "^1.4.11",
"@types/uuid": "^9.0.7",
"release-it": "^15.0.0",
tsup: "^8.0.1",
typescript: "^5.3.3"
},
dependencies: {
canonicalize: "^2.0.0",
ethers: "^6.9.1",
qs: "^6.11.2",
"url-parse": "^1.5.10",
uuid: "^9.0.1"
}
};
}
});
// src/index.ts
var index_exports = {};
__export(index_exports, {
ReclaimProofRequest: () => ReclaimProofRequest,
transformForOnchain: () => transformForOnchain,
verifyProof: () => verifyProof
});
module.exports = __toCommonJS(index_exports);
// src/witness.ts
var import_ethers = require("ethers");
function fetchWitnessListForClaim({ witnesses, witnessesRequiredForClaim, epoch }, params, timestampS) {
const identifier = typeof params === "string" ? params : getIdentifierFromClaimInfo(params);
const completeInput = [
identifier,
epoch.toString(),
witnessesRequiredForClaim.toString(),
timestampS.toString()
].join("\n");
const completeHashStr = import_ethers.ethers.keccak256(strToUint8Array(completeInput));
const completeHash = import_ethers.ethers.getBytes(completeHashStr);
const completeHashView = uint8ArrayToDataView(completeHash);
const witnessesLeft = [...witnesses];
const selectedWitnesses = [];
let byteOffset = 0;
for (let i = 0; i < witnessesRequiredForClaim; i++) {
const randomSeed = completeHashView.getUint32(byteOffset);
const witnessIndex = randomSeed % witnessesLeft.length;
const witness = witnessesLeft[witnessIndex];
selectedWitnesses.push(witness);
witnessesLeft[witnessIndex] = witnessesLeft[witnessesLeft.length - 1];
witnessesLeft.pop();
byteOffset = (byteOffset + 4) % completeHash.length;
}
return selectedWitnesses;
}
function getIdentifierFromClaimInfo(info) {
const str = `${info.provider}
${info.parameters}
${info.context || ""}`;
return import_ethers.ethers.keccak256(strToUint8Array(str)).toLowerCase();
}
function strToUint8Array(str) {
return new TextEncoder().encode(str);
}
function uint8ArrayToDataView(arr) {
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
}
function createSignDataForClaim(data) {
const identifier = "identifier" in data ? data.identifier : getIdentifierFromClaimInfo(data);
const lines = [
identifier,
data.owner.toLowerCase(),
data.timestampS.toString(),
data.epoch.toString()
];
return lines.join("\n");
}
// src/Reclaim.ts
var import_ethers6 = require("ethers");
var import_canonicalize2 = __toESM(require("canonicalize"));
// src/utils/errors.ts
function createErrorClass(name) {
return class extends Error {
constructor(message, innerError) {
const fullMessage = innerError ? `${message || ""} caused by ${innerError.name}: ${innerError.message}` : message;
super(fullMessage);
this.innerError = innerError;
this.name = name;
if (innerError) {
this.stack += `
Caused by: ${innerError.stack}`;
}
}
};
}
var TimeoutError = createErrorClass("TimeoutError");
var ProofNotVerifiedError = createErrorClass("ProofNotVerifiedError");
var SessionNotStartedError = createErrorClass("SessionNotStartedError");
var ProviderNotFoundError = createErrorClass("ProviderNotFoundError");
var SignatureGeneratingError = createErrorClass("SignatureGeneratingError");
var SignatureNotFoundError = createErrorClass("SignatureNotFoundError");
var InvalidSignatureError = createErrorClass("InvalidSignatureError");
var UpdateSessionError = createErrorClass("UpdateSessionError");
var InitSessionError = createErrorClass("InitSessionError");
var ProviderFailedError = createErrorClass("ProviderFailedError");
var InvalidParamError = createErrorClass("InvalidParamError");
var ApplicationError = createErrorClass("ApplicationError");
var InitError = createErrorClass("InitError");
var BackendServerError = createErrorClass("BackendServerError");
var GetStatusUrlError = createErrorClass("GetStatusUrlError");
var NoProviderParamsError = createErrorClass("NoProviderParamsError");
var SetParamsError = createErrorClass("SetParamsError");
var AddContextError = createErrorClass("AddContextError");
var SetSignatureError = createErrorClass("SetSignatureError");
var GetAppCallbackUrlError = createErrorClass("GetAppCallbackUrlError");
var StatusUrlError = createErrorClass("StatusUrlError");
var InavlidParametersError = createErrorClass("InavlidParametersError");
var ProofSubmissionFailedError = createErrorClass("ProofSubmissionFailedError");
// src/utils/logger.ts
var SimpleLogger = class {
constructor() {
this.level = "info";
}
setLevel(level) {
this.level = level;
}
shouldLog(messageLevel) {
const levels = ["error", "warn", "info", "silent"];
return levels.indexOf(this.level) >= levels.indexOf(messageLevel);
}
log(level, message, ...args) {
if (this.shouldLog(level) && this.level !== "silent") {
const logFunction = this.getLogFunction(level);
console.log("current level", this.level);
logFunction(`[${level.toUpperCase()}]`, message, ...args);
}
}
getLogFunction(level) {
switch (level) {
case "error":
return console.error;
case "warn":
return console.warn;
case "info":
return console.info;
default:
return () => {
};
}
}
info(message, ...args) {
this.log("info", message, ...args);
}
warn(message, ...args) {
this.log("warn", message, ...args);
}
error(message, ...args) {
this.log("error", message, ...args);
}
};
var logger = new SimpleLogger();
function setLogLevel(level) {
logger.setLevel(level);
}
var logger_default = {
logger,
setLogLevel
};
// src/utils/helper.ts
var logger2 = logger_default.logger;
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function replaceAll(str, find, replace) {
if (find === "") return str;
return str.replace(new RegExp(escapeRegExp(find), "g"), replace);
}
function scheduleIntervalEndingTask(sessionId, intervals, onFailureCallback, timeout = 1e3 * 60 * 10) {
setTimeout(() => {
if (intervals.has(sessionId)) {
const message = "Interval ended without receiving proofs";
onFailureCallback(new TimeoutError(message));
logger2.info(message);
clearInterval(intervals.get(sessionId));
intervals.delete(sessionId);
}
}, timeout);
}
// src/utils/constants.ts
var BACKEND_BASE_URL = "https://api.reclaimprotocol.org";
function setBackendBaseUrl(url) {
BACKEND_BASE_URL = url;
}
var constants = {
// Default callback URL for Reclaim protocol
get DEFAULT_RECLAIM_CALLBACK_URL() {
return `${BACKEND_BASE_URL}/api/sdk/callback?callbackId=`;
},
// Default status URL for Reclaim sessions
get DEFAULT_RECLAIM_STATUS_URL() {
return `${BACKEND_BASE_URL}/api/sdk/session/`;
},
// URL for sharing Reclaim templates
RECLAIM_SHARE_URL: "https://share.reclaimprotocol.org/verifier/?template="
};
// src/utils/validationUtils.ts
var import_ethers2 = require("ethers");
var import_canonicalize = __toESM(require("canonicalize"));
var logger3 = logger_default.logger;
function validateFunctionParams(params, functionName) {
params.forEach(({ input, paramName, isString }) => {
if (input == null) {
logger3.info(`Validation failed: ${paramName} in ${functionName} is null or undefined`);
throw new InvalidParamError(`${paramName} passed to ${functionName} must not be null or undefined.`);
}
if (isString && typeof input !== "string") {
logger3.info(`Validation failed: ${paramName} in ${functionName} is not a string`);
throw new InvalidParamError(`${paramName} passed to ${functionName} must be a string.`);
}
if (isString && input.trim() === "") {
logger3.info(`Validation failed: ${paramName} in ${functionName} is an empty string`);
throw new InvalidParamError(`${paramName} passed to ${functionName} must not be an empty string.`);
}
});
}
function validateParameters(parameters) {
try {
if (typeof parameters !== "object" || parameters === null) {
logger3.info(`Parameters validation failed: Provided parameters is not an object`);
throw new InavlidParametersError(`The provided parameters is not an object`);
}
for (const [key, value] of Object.entries(parameters)) {
if (typeof key !== "string" || typeof value !== "string") {
logger3.info(`Parameters validation failed: Provided parameters is not an object of key value pairs of string and string`);
throw new InavlidParametersError(`The provided parameters is not an object of key value pairs of string and string`);
}
}
} catch (e) {
logger3.info(`Parameters validation failed: ${e.message}`);
throw new InavlidParametersError(`Invalid parameters passed to validateParameters.`, e);
}
}
function validateURL(url, functionName) {
try {
new URL(url);
} catch (e) {
logger3.info(`URL validation failed for ${url} in ${functionName}: ${e.message}`);
throw new InvalidParamError(`Invalid URL format ${url} passed to ${functionName}.`, e);
}
}
function validateSignature(providerId, signature, applicationId, timestamp) {
try {
logger3.info(`Starting signature validation for providerId: ${providerId}, applicationId: ${applicationId}, timestamp: ${timestamp}`);
const message = (0, import_canonicalize.default)({ providerId, timestamp });
if (!message) {
logger3.info("Failed to canonicalize message for signature validation");
throw new Error("Failed to canonicalize message");
}
const messageHash = import_ethers2.ethers.keccak256(new TextEncoder().encode(message));
let appId = import_ethers2.ethers.verifyMessage(
import_ethers2.ethers.getBytes(messageHash),
import_ethers2.ethers.hexlify(signature)
).toLowerCase();
if (import_ethers2.ethers.getAddress(appId) !== import_ethers2.ethers.getAddress(applicationId)) {
logger3.info(`Signature validation failed: Mismatch between derived appId (${appId}) and provided applicationId (${applicationId})`);
throw new InvalidSignatureError(`Signature does not match the application id: ${appId}`);
}
logger3.info(`Signature validated successfully for applicationId: ${applicationId}`);
} catch (err) {
logger3.info(`Signature validation failed: ${err.message}`);
if (err instanceof InvalidSignatureError) {
throw err;
}
throw new InvalidSignatureError(`Failed to validate signature: ${err.message}`);
}
}
function validateContext(context) {
if (!context.contextAddress) {
logger3.info(`Context validation failed: Provided context address in context is not valid`);
throw new InvalidParamError(`The provided context address in context is not valid`);
}
if (!context.contextMessage) {
logger3.info(`Context validation failed: Provided context message in context is not valid`);
throw new InvalidParamError(`The provided context message in context is not valid`);
}
validateFunctionParams([
{ input: context.contextAddress, paramName: "contextAddress", isString: true },
{ input: context.contextMessage, paramName: "contextMessage", isString: true }
], "validateContext");
}
// src/utils/sessionUtils.ts
var logger4 = logger_default.logger;
function initSession(providerId, appId, timestamp, signature) {
return __async(this, null, function* () {
logger4.info(`Initializing session for providerId: ${providerId}, appId: ${appId}`);
try {
const response = yield fetch(`${BACKEND_BASE_URL}/api/sdk/init/session/`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ providerId, appId, timestamp, signature })
});
const res = yield response.json();
if (!response.ok) {
logger4.info(`Session initialization failed: ${res.message || "Unknown error"}`);
throw new InitSessionError(res.message || `Error initializing session with providerId: ${providerId}`);
}
return res;
} catch (err) {
logger4.info(`Failed to initialize session for providerId: ${providerId}, appId: ${appId}`, err);
throw err;
}
});
}
function updateSession(sessionId, status) {
return __async(this, null, function* () {
logger4.info(`Updating session status for sessionId: ${sessionId}, new status: ${status}`);
validateFunctionParams(
[{ input: sessionId, paramName: "sessionId", isString: true }],
"updateSession"
);
try {
const response = yield fetch(`${BACKEND_BASE_URL}/api/sdk/update/session/`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sessionId, status })
});
const res = yield response.json();
if (!response.ok) {
const errorMessage = `Error updating session with sessionId: ${sessionId}. Status Code: ${response.status}`;
logger4.info(errorMessage, res);
throw new UpdateSessionError(errorMessage);
}
logger4.info(`Session status updated successfully for sessionId: ${sessionId}`);
return res;
} catch (err) {
const errorMessage = `Failed to update session with sessionId: ${sessionId}`;
logger4.info(errorMessage, err);
throw new UpdateSessionError(`Error updating session with sessionId: ${sessionId}`);
}
});
}
function fetchStatusUrl(sessionId) {
return __async(this, null, function* () {
validateFunctionParams(
[{ input: sessionId, paramName: "sessionId", isString: true }],
"fetchStatusUrl"
);
try {
const response = yield fetch(`${constants.DEFAULT_RECLAIM_STATUS_URL}${sessionId}`, {
method: "GET",
headers: { "Content-Type": "application/json" }
});
const res = yield response.json();
if (!response.ok) {
const errorMessage = `Error fetching status URL for sessionId: ${sessionId}. Status Code: ${response.status}`;
logger4.info(errorMessage, res);
throw new StatusUrlError(errorMessage);
}
return res;
} catch (err) {
const errorMessage = `Failed to fetch status URL for sessionId: ${sessionId}`;
logger4.info(errorMessage, err);
throw new StatusUrlError(`Error fetching status URL for sessionId: ${sessionId}`);
}
});
}
// src/utils/proofUtils.ts
var import_ethers5 = require("ethers");
// src/contract-types/contracts/factories/Reclaim__factory.ts
var import_ethers3 = require("ethers");
var _abi = [
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "previousAdmin",
type: "address"
},
{
indexed: false,
internalType: "address",
name: "newAdmin",
type: "address"
}
],
name: "AdminChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "beacon",
type: "address"
}
],
name: "BeaconUpgraded",
type: "event"
},
{
anonymous: false,
inputs: [
{
components: [
{
internalType: "uint32",
name: "id",
type: "uint32"
},
{
internalType: "uint32",
name: "timestampStart",
type: "uint32"
},
{
internalType: "uint32",
name: "timestampEnd",
type: "uint32"
},
{
components: [
{
internalType: "address",
name: "addr",
type: "address"
},
{
internalType: "string",
name: "host",
type: "string"
}
],
internalType: "struct Reclaim.Witness[]",
name: "witnesses",
type: "tuple[]"
},
{
internalType: "uint8",
name: "minimumWitnessesForClaimCreation",
type: "uint8"
}
],
indexed: false,
internalType: "struct Reclaim.Epoch",
name: "epoch",
type: "tuple"
}
],
name: "EpochAdded",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint8",
name: "version",
type: "uint8"
}
],
name: "Initialized",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "previousOwner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "newOwner",
type: "address"
}
],
name: "OwnershipTransferred",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "implementation",
type: "address"
}
],
name: "Upgraded",
type: "event"
},
{
inputs: [
{
internalType: "address",
name: "witnessAddress",
type: "address"
},
{
internalType: "string",
name: "host",
type: "string"
}
],
name: "addAsWitness",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "addNewEpoch",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "uint32",
name: "epochNum",
type: "uint32"
},
{
components: [
{
internalType: "string",
name: "provider",
type: "string"
},
{
internalType: "string",
name: "parameters",
type: "string"
},
{
internalType: "string",
name: "context",
type: "string"
}
],
internalType: "struct Claims.ClaimInfo",
name: "claimInfo",
type: "tuple"
},
{
components: [
{
internalType: "bytes32",
name: "identifier",
type: "bytes32"
},
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "uint32",
name: "timestampS",
type: "uint32"
},
{
internalType: "uint256",
name: "epoch",
type: "uint256"
}
],
internalType: "struct Claims.CompleteClaimData",
name: "claimData",
type: "tuple"
},
{
internalType: "bytes[]",
name: "signatures",
type: "bytes[]"
}
],
name: "assertValidEpochAndSignedClaim",
outputs: [],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "currentEpoch",
outputs: [
{
internalType: "uint32",
name: "",
type: "uint32"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "epochDurationS",
outputs: [
{
internalType: "uint32",
name: "",
type: "uint32"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "epochs",
outputs: [
{
internalType: "uint32",
name: "id",
type: "uint32"
},
{
internalType: "uint32",
name: "timestampStart",
type: "uint32"
},
{
internalType: "uint32",
name: "timestampEnd",
type: "uint32"
},
{
internalType: "uint8",
name: "minimumWitnessesForClaimCreation",
type: "uint8"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "uint32",
name: "epoch",
type: "uint32"
}
],
name: "fetchEpoch",
outputs: [
{
components: [
{
internalType: "uint32",
name: "id",
type: "uint32"
},
{
internalType: "uint32",
name: "timestampStart",
type: "uint32"
},
{
internalType: "uint32",
name: "timestampEnd",
type: "uint32"
},
{
components: [
{
internalType: "address",
name: "addr",
type: "address"
},
{
internalType: "string",
name: "host",
type: "string"
}
],
internalType: "struct Reclaim.Witness[]",
name: "witnesses",
type: "tuple[]"
},
{
internalType: "uint8",
name: "minimumWitnessesForClaimCreation",
type: "uint8"
}
],
internalType: "struct Reclaim.Epoch",
name: "",
type: "tuple"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "uint32",
name: "epoch",
type: "uint32"
},
{
internalType: "bytes32",
name: "identifier",
type: "bytes32"
},
{
internalType: "uint32",
name: "timestampS",
type: "uint32"
}
],
name: "fetchWitnessesForClaim",
outputs: [
{
components: [
{
internalType: "address",
name: "addr",
type: "address"
},
{
internalType: "string",
name: "host",
type: "string"
}
],
internalType: "struct Reclaim.Witness[]",
name: "",
type: "tuple[]"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "initialize",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "minimumWitnessesForClaimCreation",
outputs: [
{
internalType: "uint8",
name: "",
type: "uint8"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "owner",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "proxiableUUID",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "witnessAddress",
type: "address"
}
],
name: "removeAsWitness",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "renounceOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "newOwner",
type: "address"
}
],
name: "transferOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "addr",
type: "address"
},
{
internalType: "bool",
name: "isWhitelisted",
type: "bool"
}
],
name: "updateWitnessWhitelist",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "newImplementation",
type: "address"
}
],
name: "upgradeTo",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "newImplementation",
type: "address"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
}
],
name: "upgradeToAndCall",
outputs: [],
stateMutability: "payable",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "witnesses",
outputs: [
{
internalType: "address",
name: "addr",
type: "address"
},
{
internalType: "string",
name: "host",
type: "string"
}
],
stateMutability: "view",
type: "function"
}
];
var Reclaim__factory = class {
static connect(address, signerOrProvider) {
return new import_ethers3.Contract(address, _abi, signerOrProvider);
}
};
Reclaim__factory.abi = _abi;
// src/contract-types/config.json
var config_default = {
"0x1a4": {
chainName: "opt-goerli",
address: "0xF93F605142Fb1Efad7Aa58253dDffF67775b4520",
rpcUrl: "https://opt-goerli.g.alchemy.com/v2/rksDkSUXd2dyk2ANy_zzODknx_AAokui"
},
"0xaa37dc": {
chainName: "opt-sepolia",
address: "0x6D0f81BDA11995f25921aAd5B43359630E65Ca96",
rpcUrl: "https://opt-sepolia.g.alchemy.com/v2/aO1-SfG4oFRLyAiLREqzyAUu0HTCwHgs"
}
};
// src/smart-contract.ts
var import_ethers4 = require("ethers");
var DEFAULT_CHAIN_ID = 11155420;
function makeBeacon(chainId) {
chainId = chainId || DEFAULT_CHAIN_ID;
const contract = getContract(chainId);
if (contract) {
let _a;
return makeBeaconCacheable({
getState(epochId) {
return __async(this, null, function* () {
const epoch = yield contract.fetchEpoch(epochId || 0);
if (!epoch.id) {
throw new Error(`Invalid epoch ID: ${epochId}`);
}
return {
epoch: epoch.id,
witnesses: epoch.witnesses.map((w) => ({
id: w.addr.toLowerCase(),
url: w.host
})),
witnessesRequiredForClaim: epoch.minimumWitnessesForClaimCreation,
nextEpochTimestampS: epoch.timestampEnd
};
});
}
});
} else {
return void 0;
}
}
function makeBeaconCacheable(beacon) {
const cache = {};
return __spreadProps(__spreadValues({}, beacon), {
getState(epochId) {
return __async(this, null, function* () {
if (!epochId) {
const state = yield beacon.getState();
return state;
}
const key = epochId;
if (!cache[key]) {
cache[key] = beacon.getState(epochId);
}
return cache[key];
});
}
});
}
var existingContractsMap = {};
function getContract(chainId) {
const chainKey = `0x${chainId.toString(16)}`;
if (!existingContractsMap[chainKey]) {
const contractData = config_default[chainKey];
if (!contractData) {
throw new Error(`Unsupported chain: "${chainKey}"`);
}
const rpcProvider = new import_ethers4.ethers.JsonRpcProvider(contractData.rpcUrl);
existingContractsMap[chainKey] = Reclaim__factory.connect(
contractData.address,
rpcProvider
);
}
return existingContractsMap[chainKey];
}
// src/utils/proofUtils.ts
var logger5 = logger_default.logger;
function getShortenedUrl(url) {
return __async(this, null, function* () {
logger5.info(`Attempting to shorten URL: ${url}`);
try {
validateURL(url, "getShortenedUrl");
const response = yield fetch(`${BACKEND_BASE_URL}/api/sdk/shortener`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ fullUrl: url })
});
const res = yield response.json();
if (!response.ok) {
logger5.info(`Failed to shorten URL: ${url}, Response: ${JSON.stringify(res)}`);
return url;
}
const shortenedVerificationUrl = res.result.shortUrl;
return shortenedVerificationUrl;
} catch (err) {
logger5.info(`Error shortening URL: ${url}, Error: ${err}`);
return url;
}
});
}
function createLinkWithTemplateData(templateData) {
return __async(this, null, function* () {
let template = encodeURIComponent(JSON.stringify(templateData));
template = replaceAll(template, "(", "%28");
template = replaceAll(template, ")", "%29");
const fullLink = `${constants.RECLAIM_SHARE_URL}${template}`;
try {
const shortenedLink = yield getShortenedUrl(fullLink);
return shortenedLink;
} catch (err) {
logger5.info(`Error creating link for sessionId: ${templateData.sessionId}, Error: ${err}`);
return fullLink;
}
});
}
function getWitnessesForClaim(epoch, identifier, timestampS) {
return __async(this, null, function* () {
const beacon = makeBeacon();
if (!beacon) {
logger5.info("No beacon available for getting witnesses");
throw new Error("No beacon available");
}
const state = yield beacon.getState(epoch);
const witnessList = fetchWitnessListForClaim(state, identifier, timestampS);
const witnesses = witnessList.map((w) => w.id.toLowerCase());
return witnesses;
});
}
function recoverSignersOfSignedClaim({
claim,
signatures
}) {
const dataStr = createSignDataForClaim(__spreadValues({}, claim));
const signers = signatures.map(
(signature) => import_ethers5.ethers.verifyMessage(dataStr, import_ethers5.ethers.hexlify(signature)).toLowerCase()
);
return signers;
}
function assertValidSignedClaim(claim, expectedWitnessAddresses) {
const witnessAddresses = recoverSignersOfSignedClaim(claim);
const witnessesNotSeen = new Set(expectedWitnessAddresses);
for (const witness of witnessAddresses) {
if (witnessesNotSeen.has(witness)) {
witnessesNotSeen.delete(witness);
}
}
if (witnessesNotSeen.size > 0) {
const missingWitnesses = Array.from(witnessesNotSeen).join(", ");
logger5.info(`Claim validation failed. Missing signatures from: ${missingWitnesses}`);
throw new ProofNotVerifiedError(
`Missing signatures from ${missingWitnesses}`
);
}
}
// src/Reclaim.ts
var logger6 = logger_default.logger;
var sdkVersion = require_package().version;
function verifyProof(proofOrProofs) {
return __async(this, null, function* () {
var _a;
if (Array.isArray(proofOrProofs)) {
for (const proof2 of proofOrProofs) {
const isVerified = yield verifyProof(proof2);
if (!isVerified) {
return false;
}
}
return true;
}
const proof = proofOrProofs;
if (!proof.signatures.length) {
throw new SignatureNotFoundError("No signatures");
}
try {
let witnesses = [];
if (proof.witnesses.length && ((_a = proof.witnesses[0]) == null ? void 0 : _a.url) === "manual-verify") {
witnesses.push(proof.witnesses[0].id);
} else {
witnesses = yield getWitnessesForClaim(
proof.claimData.epoch,
proof.identifier,
proof.claimData.timestampS
);
}
const calculatedIdentifier = getIdentifierFromClaimInfo({
parameters: JSON.parse(
(0, import_canonicalize2.default)(proof.claimData.parameters)
),
provider: proof.claimData.provider,
context: proof.claimData.context
});
proof.identifier = replaceAll(proof.identifier, '"', "");
if (calculatedIdentifier !== proof.identifier) {
throw new ProofNotVerifiedError("Identifier Mismatch");
}
const signedClaim = {
claim: __spreadValues({}, proof.claimData),
signatures: proof.signatures.map((signature) => {
return import_ethers6.ethers.getBytes(signature);
})
};
assertValidSignedClaim(signedClaim, witnesses);
} catch (e) {
logger6.info(`Error verifying proof: ${e instanceof Error ? e.message : String(e)}`);
return false;
}
return true;
});
}
function transformForOnchain(proof) {
const claimInfoBuilder = /* @__PURE__ */ new Map([
["context", proof.claimData.context],
["parameters", proof.claimData.parameters],
["provider", proof.claimData.provider]
]);
const claimInfo = Object.fromEntries(claimInfoBuilder);
const claimBuilder = /* @__PURE__ */ new Map([
["epoch", proof.claimData.epoch],
["identifier", proof.claimData.identifier],
["owner", proof.claimData.owner],
["timestampS", proof.claimData.timestampS]
]);
const signedClaim = {
claim: Object.fromEntries(claimBuilder),
signatures: proof.signatures
};
return { claimInfo, signedClaim };
}
var ReclaimProofRequest = class _ReclaimProofRequest {
// 30 seconds timeout, can be adjusted
// Private constructor
constructor(applicationId, providerId, options) {
this.context = { contextAddress: "0x0", contextMessage: "sample context" };
this.intervals = /* @__PURE__ */ new Map();
this.jsonProofResponse = false;
this.FAILURE_TIMEOUT = 3e4;
this.providerId = providerId;
this.timeStamp = Date.now().toString();
this.applicationId = applicationId;
this.sessionId = "";
this.parameters = {};
if (options == null ? void 0 : options.log) {
logger_default.setLogLevel("info");
} else {
logger_default.setLogLevel("silent");
}
if (options == null ? void 0 : options.envUrl) {
setBackendBaseUrl(options.envUrl);
}
this.options = options;
this.sdkVersion = "js-" + sdkVersion;
logger6.info(`Initializing client with applicationId: ${this.applicationId}`);
}
// Static initialization methods
static init(applicationId, appSecret, providerId, options) {
return __async(this, null, function* () {
try {
validateFunctionParams([
{ paramName: "applicationId", input: applicationId, isString: true },
{ paramName: "providerId", input: providerId, isString: true },
{ paramName: "appSecret", input: appSecret, isString: true }
], "the constructor");
if (options) {
if (options.acceptAiProviders) {
validateFunctionParams([
{ paramName: "acceptAiProviders", input: options.acceptAiProviders }
], "the constructor");
}
if (options.log) {
validateFunctionParams([
{ paramName: "log", input: options.log }
], "the constructor");
}
if (options.useAppClip) {
validateFunctionParams([
{ paramName: "useAppClip", input: options.useAppClip }
], "the constructor");
}
if (options.device) {
validateFunctionParams([
{ paramName: "device", input: options.device, isString: true }
], "the constructor");
}
if (options.envUrl) {
validateFunctionParams([
{ paramName: "envUrl", input: options.envUrl, isString: true }
], "the constructor");
}
}
const proofRequestInstance = new _ReclaimProofRequest(applicationId, providerId, options);
const signature = yield proofRequestInstance.generateSignature(appSecret);
proofRequestInstance.setSignature(signature);
const data = yield initSession(providerId, applicationId, proofRequestInstance.timeStamp, signature);
proofRequestInstance.sessionId = data.sessionId;
return proofRequestInstance;
} catch (error) {
logger6.info("Failed to initialize ReclaimProofRequest", error);
throw new InitError("Failed to initialize ReclaimProofRequest", error);
}
});
}
static fromJsonString(jsonString) {
return __async(this, null, function* () {
try {
const {
applicationId,
providerId,
sessionId,
context,
parameters,
signature,
redirectUrl,
timeStamp,
appCallbackUrl,
options,
sdkVersion: sdkVersion2,
jsonProofResponse
} = JSON.parse(jsonString);
validateFunctionParams([
{ input: applicationId, paramName: "applicationId", isString: true },
{ input: providerId, paramName: "providerId", isString: true },
{ input: signature, paramName: "signature", isString: true },
{ input: sessionId, paramName: "sessionId", isString: true },
{ input: timeStamp, paramName: "timeStamp", isString: true },
{ input: sdkVersion2, paramName: "sdkVersion", isString: true }
], "fromJsonString");
if (redirectUrl) {
validateURL(redirectUrl, "fromJsonString");
}
if (appCallbackUrl) {
validateURL(appCallbackUrl, "fromJsonString");
}
if (context) {
validateContext(context);
}
if (parameters) {
validateParameters(parameters);
}
if (jsonProofResponse !== void 0) {
validateFunctionParams([
{ input: jsonProofResponse, paramName: "jsonProofResponse" }
], "fromJsonString");
}
const proofRequestInstance = new _ReclaimProofRequest(applicationId, providerId, options);
proofRequestInstance.sessionId = sessionId;
proofRequestInstance.context = context;
proofRequestInstance.parameters = parameters;
proofRequestInstance.appCallbackUrl = appCallbackUrl;
proofRequestInstance.redirectUrl = redirectUrl;
proofRequestInstance.timeStamp = timeStamp;
proofRequestInstance.signature = signature;
proofRequestInstance.sdkVersion = sdkVersion2;
return proofRequestInstance;
} catch (error) {
logger6.info("Failed to parse JSON string in fromJsonString:", error);
throw new InvalidParamError("Invalid JSON string provided to fromJsonString");
}
});
}
// Setter methods
setAppCallbackUrl(url, jsonProofResponse) {
validateURL(url, "setAppCallbackUrl");
this.appCallbackUrl = url;
this.jsonProofResponse = jsonProofResponse != null ? jsonProofResponse : false;
}
setRedirectUrl(url) {
validateURL(url, "setRedirectUrl");
this.redirectUrl = url;
}
addContext(address, message) {
try {
validateFunctionParams([
{ input: address, paramName: "address", isString: true },
{ input: message, paramName: "message", isString: true }
], "addContext");
this.context = { contextAddress: address, contextMessage: message };
} catch (error) {
logger6.info("Error adding context", error);
throw new AddContextError("Error adding context", error);
}
}
setParams(params) {
try {
validateParameters(params);
this.parameters = __spreadValues(__spreadValues({}, this.parameters), params);
} catch (error) {
logger6.info("Error Setting Params:", error);
throw new SetParamsError("Error setting params", error);
}
}
// Getter methods
getAppCallbackUrl() {
try {
validateFunctionParams([{ input: this.sessionId, paramName: "sessionId", isString: true }], "getAppCallbackUrl");
return this.appCallbackUrl || `${constants.DEFAULT_RECLAIM_CALLBACK_URL}${this.sessionId}`;
} catch (error) {
logger6.info("Error getting app callback url", error);
throw new GetAppCallbackUrlError("Error getting app callback url", error);
}
}
getStatusUrl() {
try {
validateFunctionParams([{ input: this.sessionId, paramName: "sessionId", isString: true }], "getStatusUrl");
return `${constants.DEFAULT_RECLAIM_STATUS_URL}${this.sessionId}`;
} catch (error) {
logger6.info("Error fetching Status Url", error);
throw new GetStatusUrlError("Error fetching status url", error);
}
}
// Private helper methods
setSignature(signature) {
try {
validateFunctionParams([{ input: signature, paramName: "signature", isString: true }], "setSignature");
this.signature = signature;
logger6.info(`Signature set successfully for applicationId: ${this.applicationId}`);
} catch (error) {
logger6.info("Error setting signature", error);
throw new SetSignatureError("Error setting signature", error);
}
}
generateSignature(applicationSecret) {
return __async(this, null, function* () {
try {
const wallet = new import_ethers6.ethers.Wallet(applicationSecret);
const canonicalData = (0, import_canonicalize2.default)({ providerId: this.providerId, timestamp: this.timeStamp });
if (!canonicalData) {
throw new SignatureGeneratingError("Failed to canonicalize data for signing.");
}
const messageHash = import_ethers6.ethers.keccak256(new TextEncoder().encode(canonicalData));
return yield wallet.signMessage(import_ethers6.ethers.getBytes(messageHash));
} catch (err) {
logger6.info(`Error generating proof request for applicationId: ${this.applicationId}, providerId: ${this.providerId}, signature: ${this.signature}, timeStamp: ${this.timeStamp}`, err);
throw new SignatureGeneratingError(`Error generating signature for applicationSecret: ${applicationSecret}`);
}
});
}
clearInterval() {
if (this.sessionId && this.intervals.has(this.sessionId)) {
clearInterval(this.intervals.get(this.sessionId));
this.intervals.delete(this.sessionId);
}
}
// Public methods
toJsonString(options) {
return JSON.stringify({
applicationId: this.applicationId,
providerId: this.providerId,
sessionId: this.sessionId,
context: this.context,
appCallbackUrl: this.appCallbackUrl,
parameters: this.parameters,
signature: this.signature,
redirectUrl: this.redirectUrl,
timeStamp: this.timeStamp,
options: this.options,
sdkVersion: this.sdkVersion,
jsonProofResponse: this.jsonProofResponse
});
}
getRequestUrl() {
return __async(this, null, function* () {
var _a, _b, _c, _d, _e;
logger6.info("Creating Request Url");
if (!this.signature) {
throw new SignatureNotFoundError("Signature is not set.");
}
try {
validateSignature(this.providerId, this.signature, this.applicationId, this.timeStamp);
const templateData = {
sessionId: this.sessionId,
providerId: this.providerId,
applicationId: this.applicationId,
signature: this.signature,
timestamp: this.timeStamp,
callbackUrl: this.getAppCallbackUrl(),
context: JSON.stringify(this.context),
parameters: this.parameters,
redirectUrl: (_a = this.redirectUrl) != null ? _a : "",
acceptAiProviders: (_c = (_b = this.options) == null ? void 0 : _b.acceptAiProviders) != null ? _c : false,
sdkVersion: this.sdkVersion,
jsonProofResponse: this.jsonProofResponse
};
yield updateSession(this.sessionId, "SESSION_STARTED" /* SESSION_STARTED */);
if ((_d = this.options) == null ? void 0 : _d.useAppClip) {
let template = encodeURIComponent(JSON.stringify(templateData));
template = replaceAll(template, "(", "%28");
template = replaceAll(template, ")", "%29");
const isIos = ((_e = this.options) == null ? void 0 : _e.device) === "ios";
if (!isIos) {
const instantAppUrl = `https://share.reclaimprotocol.org/verify/?template=${template}`;
logger6.info("Instant App Url created successfully: " + instantAppUrl);
return instantAppUrl;
} else {
const appClipUrl = `https://appclip.apple.com/id?p=org.reclaimprotocol.app.clip&template=${template}`;
logger6.info("App Clip Url created successfully: " + appClipUrl);
return appClipUrl;
}
} else {
const link = yield createLinkWithTemplateData(templateData);
logger6.info("Request Url created successfully: " + link);
return link;
}
} catch (error) {
logger6.info("Error creating Request Url:", error);
throw error;
}
});
}
startSession(_0) {
return __async(this, arguments, function* ({ onSuccess, onError }) {
if (!this.sessionId) {
const message = "Session can't be started due to undefined value of sessionId";
logger6.info(message);
throw new SessionNotStartedError(message);
}
logger6.info("Starting session");
const interval = setInterval(() => __async(this, null, function* () {
try {
const statusUrlResponse = yield fetchStatusUrl(this.sessionId);
if (!statusUrlResponse.session) return;
if (statusUrlResponse.session.statusV2 !== "PROOF_GENERATION