@opendatalabs/vana-sdk
Version:
A TypeScript library for interacting with Vana Network smart contracts.
1 lines • 17 kB
Source Map (JSON)
{"version":3,"sources":["../../src/account/personal-server-registration.ts"],"sourcesContent":["/**\n * Optional first-party Account integration for Personal Server registration.\n *\n * The protocol helper lives in `protocol/personal-server-registration`.\n * This module is only for callers that want to use an Account deployment's\n * constrained silent-sign endpoint.\n *\n * @category Account\n */\n\nimport { isAddress, type Address, type Hex } from \"viem\";\nimport {\n buildPersonalServerRegistrationTypedData,\n type BuildPersonalServerRegistrationTypedDataInput,\n type PersonalServerRegistrationSignature,\n type PersonalServerRegistrationSigner,\n type PersonalServerRegistrationTypedData,\n personalServerRegistrationDomain,\n} from \"../protocol/personal-server-registration\";\nimport { SERVER_REGISTRATION_TYPES } from \"../protocol/eip712\";\n\nexport const ACCOUNT_PERSONAL_SERVER_REGISTRATION_INTENT =\n \"personal_server.server_registration.v1\" as const;\n\nexport type AccountPersonalServerRegistrationIntent =\n typeof ACCOUNT_PERSONAL_SERVER_REGISTRATION_INTENT;\n\nexport type AccountPersonalServerRegistrationStatus =\n | \"signed\"\n | \"confirmation_required\"\n | \"fallback_required\";\n\nexport interface AccountPersonalServerRegistrationRequest extends Omit<\n BuildPersonalServerRegistrationTypedDataInput,\n \"ownerAddress\"\n> {}\n\nexport interface AccountPersonalServerRegistrationConfig {\n /**\n * Origin for the Account deployment to call, e.g. an app-dev Account origin.\n * No production origin is assumed by the SDK.\n */\n accountOrigin: string;\n /**\n * Path for Account's constrained PS registration silent-sign endpoint.\n */\n endpointPath?: string;\n /**\n * Optional fetch implementation for tests and non-default runtimes.\n */\n fetchImpl?: typeof fetch;\n /**\n * Optional signer used when Account says user confirmation is required and\n * returns typed data for the caller to sign interactively.\n */\n fallbackSigner?: PersonalServerRegistrationSigner;\n}\n\nexport type AccountPersonalServerRegistrationSignature =\n PersonalServerRegistrationSignature & {\n intent: AccountPersonalServerRegistrationIntent;\n };\n\nexport interface AccountSignedPersonalServerRegistration {\n status: \"signed\";\n result: AccountPersonalServerRegistrationSignature;\n}\n\nexport interface AccountConfirmationRequiredPersonalServerRegistration {\n status: \"confirmation_required\";\n typedData: PersonalServerRegistrationTypedData;\n signerAddress?: Address;\n}\n\nexport interface AccountFallbackSignedPersonalServerRegistration {\n status: \"fallback_signed\";\n accountStatus: \"confirmation_required\";\n result: AccountPersonalServerRegistrationSignature;\n}\n\nexport type AccountPersonalServerRegistrationResult =\n | AccountSignedPersonalServerRegistration\n | AccountConfirmationRequiredPersonalServerRegistration\n | AccountFallbackSignedPersonalServerRegistration;\n\nexport class AccountPersonalServerRegistrationError extends Error {\n status: number;\n code?: string;\n details?: unknown;\n\n constructor(input: {\n status: number;\n message: string;\n code?: string;\n details?: unknown;\n }) {\n super(input.message);\n this.name = \"AccountPersonalServerRegistrationError\";\n this.status = input.status;\n this.code = input.code;\n this.details = input.details;\n }\n}\n\ninterface AccountSilentSignResponse {\n status: AccountPersonalServerRegistrationStatus;\n signature?: Hex;\n signerAddress?: Address;\n signer?: { address?: Address };\n typedData?: PersonalServerRegistrationTypedData;\n typed_data?: PersonalServerRegistrationTypedData;\n error?: unknown;\n}\n\n// Account-owned route policy. Protocol signing primitives deliberately do not\n// define Account intent names or API paths.\nconst DEFAULT_ACCOUNT_PS_REGISTRATION_PATH =\n \"/api/v1/intents/personal-server-registration/sign\";\n\nfunction trimTrailingSlash(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\nfunction assertAddress(value: Address, name: string): void {\n if (!isAddress(value)) {\n throw new Error(`${name} must be a valid EVM address`);\n }\n}\n\nasync function parseAccountResponse(\n response: Response,\n): Promise<AccountSilentSignResponse> {\n const body = (await response.json().catch(() => undefined)) as unknown;\n\n if (!response.ok) {\n throw new AccountPersonalServerRegistrationError({\n status: response.status,\n code: accountErrorCode(body),\n message: accountErrorMessage(response.status, body),\n details: body,\n });\n }\n\n return body as AccountSilentSignResponse;\n}\n\nfunction accountErrorMessage(status: number, body: unknown): string {\n const nestedMessage = nestedAccountErrorField(body, \"message\");\n if (nestedMessage) {\n return nestedMessage;\n }\n\n if (isRecord(body) && typeof body.message === \"string\") {\n return body.message;\n }\n\n const code = accountErrorCode(body);\n if (code) {\n return `Account PS registration signing failed: ${code}`;\n }\n\n return `Account PS registration signing failed: ${status}`;\n}\n\nfunction accountErrorCode(body: unknown): string | undefined {\n const nestedCode = nestedAccountErrorField(body, \"code\");\n if (nestedCode) {\n return nestedCode;\n }\n\n if (isRecord(body)) {\n if (typeof body.code === \"string\") {\n return body.code;\n }\n if (typeof body.error === \"string\") {\n return body.error;\n }\n }\n\n return undefined;\n}\n\nfunction nestedAccountErrorField(\n body: unknown,\n field: \"code\" | \"message\",\n): string | undefined {\n if (!isRecord(body) || !isRecord(body.error)) {\n return undefined;\n }\n\n const value = body.error[field];\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction normalizeAccountResponse(\n response: AccountSilentSignResponse,\n): AccountSilentSignResponse {\n return {\n ...response,\n status:\n response.status === \"fallback_required\"\n ? \"confirmation_required\"\n : response.status,\n signerAddress: response.signerAddress ?? response.signer?.address,\n typedData: response.typedData ?? response.typed_data,\n };\n}\n\nfunction buildSignedResult(\n response: Required<\n Pick<AccountSilentSignResponse, \"signature\" | \"signerAddress\">\n > &\n Pick<AccountSilentSignResponse, \"typedData\">,\n request: AccountPersonalServerRegistrationRequest,\n): AccountPersonalServerRegistrationSignature {\n assertAddress(response.signerAddress, \"signerAddress\");\n if (response.typedData) {\n assertTypedDataMatchesRequest(\n response.typedData,\n request,\n response.signerAddress,\n );\n }\n\n return {\n signature: response.signature,\n signerAddress: response.signerAddress,\n typedData:\n response.typedData ??\n buildPersonalServerRegistrationTypedData({\n ownerAddress: response.signerAddress,\n ...request,\n }),\n intent: ACCOUNT_PERSONAL_SERVER_REGISTRATION_INTENT,\n };\n}\n\nfunction assertTypedDataMatchesRequest(\n typedData: PersonalServerRegistrationTypedData,\n request: AccountPersonalServerRegistrationRequest,\n expectedSignerAddress?: Address,\n): void {\n assertAddress(\n typedData.message.ownerAddress,\n \"typedData.message.ownerAddress\",\n );\n assertAddress(\n typedData.message.serverAddress,\n \"typedData.message.serverAddress\",\n );\n\n if (\n expectedSignerAddress &&\n !sameAddress(typedData.message.ownerAddress, expectedSignerAddress)\n ) {\n throw new Error(\n \"Account typedData ownerAddress must match the expected signer address\",\n );\n }\n\n if (!sameAddress(typedData.message.serverAddress, request.serverAddress)) {\n throw new Error(\n \"Account typedData serverAddress must match the requested serverAddress\",\n );\n }\n\n if (typedData.message.publicKey !== request.serverPublicKey) {\n throw new Error(\n \"Account typedData publicKey must match the requested serverPublicKey\",\n );\n }\n\n if (typedData.message.serverUrl !== request.serverUrl) {\n throw new Error(\n \"Account typedData serverUrl must match the requested serverUrl\",\n );\n }\n\n if (typedData.primaryType !== \"ServerRegistration\") {\n throw new Error(\"Account typedData primaryType must be ServerRegistration\");\n }\n\n if (\n JSON.stringify(typedData.types) !==\n JSON.stringify(SERVER_REGISTRATION_TYPES)\n ) {\n throw new Error(\"Account typedData types must be ServerRegistration types\");\n }\n\n const expectedDomain = personalServerRegistrationDomain({\n config: request.config,\n chainId: request.chainId,\n verifyingContract: request.verifyingContract,\n });\n if (!domainsEqual(typedData.domain, expectedDomain)) {\n throw new Error(\"Account typedData domain must match the requested domain\");\n }\n}\n\nfunction sameAddress(a: Address, b: Address): boolean {\n return a.toLowerCase() === b.toLowerCase();\n}\n\nfunction domainsEqual(\n a: PersonalServerRegistrationTypedData[\"domain\"],\n b: PersonalServerRegistrationTypedData[\"domain\"],\n): boolean {\n if (!a || !b) {\n return false;\n }\n\n return (\n a.name === b.name &&\n a.version === b.version &&\n Number(a.chainId) === Number(b.chainId) &&\n String(a.verifyingContract ?? \"\").toLowerCase() ===\n String(b.verifyingContract ?? \"\").toLowerCase() &&\n a.salt === b.salt\n );\n}\n\nexport async function signPersonalServerRegistrationWithAccount(\n config: AccountPersonalServerRegistrationConfig,\n request: AccountPersonalServerRegistrationRequest,\n): Promise<AccountPersonalServerRegistrationResult> {\n assertAddress(request.serverAddress, \"serverAddress\");\n\n const fetchImpl = config.fetchImpl ?? globalThis.fetch.bind(globalThis);\n const endpoint = new URL(\n config.endpointPath ?? DEFAULT_ACCOUNT_PS_REGISTRATION_PATH,\n `${trimTrailingSlash(config.accountOrigin)}/`,\n );\n\n const response = await fetchImpl(endpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify({\n intent: ACCOUNT_PERSONAL_SERVER_REGISTRATION_INTENT,\n serverAddress: request.serverAddress,\n serverPublicKey: request.serverPublicKey,\n serverUrl: request.serverUrl,\n config: request.config,\n chainId: request.chainId,\n verifyingContract: request.verifyingContract,\n }),\n });\n const body = normalizeAccountResponse(await parseAccountResponse(response));\n\n if (body.status === \"signed\") {\n if (!body.signature || !body.signerAddress) {\n throw new Error(\n \"Account signed response must include signature and signerAddress\",\n );\n }\n\n return {\n status: \"signed\",\n result: buildSignedResult(\n {\n signature: body.signature,\n signerAddress: body.signerAddress,\n typedData: body.typedData,\n },\n request,\n ),\n };\n }\n\n if (body.status === \"confirmation_required\") {\n if (!body.typedData) {\n throw new Error(\n \"Account confirmation_required response must include typedData\",\n );\n }\n assertTypedDataMatchesRequest(body.typedData, request, body.signerAddress);\n\n if (!config.fallbackSigner) {\n return {\n status: \"confirmation_required\",\n typedData: body.typedData,\n signerAddress: body.signerAddress,\n };\n }\n\n assertTypedDataMatchesRequest(\n body.typedData,\n request,\n config.fallbackSigner.address,\n );\n const signature = await config.fallbackSigner.signTypedData(body.typedData);\n\n return {\n status: \"fallback_signed\",\n accountStatus: \"confirmation_required\",\n result: {\n signature,\n signerAddress: config.fallbackSigner.address,\n typedData: body.typedData,\n intent: ACCOUNT_PERSONAL_SERVER_REGISTRATION_INTENT,\n },\n };\n }\n\n throw new Error(\n `Unsupported Account PS registration signing status: ${String(body.status)}`,\n );\n}\n"],"mappings":"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","names":[]}