From 88990296186dde8657cf132fc22e0fa3d2e1f8d1 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 10 Jun 2026 16:06:38 -0600 Subject: [PATCH 1/2] feat: wallet-bound proof signatures --- .changeset/wallet-bound-proof.md | 5 + src/tempo/Proof.conformance.test.ts | 141 ++++++++++++++++++++++++++++ src/tempo/Proof.test-d.ts | 20 ++++ src/tempo/Proof.ts | 53 ++++++++++- src/tempo/client/Charge.ts | 12 ++- src/tempo/internal/proof.test.ts | 16 +++- src/tempo/internal/proof.ts | 61 ++++++++++-- src/tempo/server/Charge.test.ts | 32 +++---- src/tempo/server/Charge.ts | 20 ++-- 9 files changed, 318 insertions(+), 42 deletions(-) create mode 100644 .changeset/wallet-bound-proof.md create mode 100644 src/tempo/Proof.conformance.test.ts diff --git a/.changeset/wallet-bound-proof.md b/.changeset/wallet-bound-proof.md new file mode 100644 index 00000000..b769bf72 --- /dev/null +++ b/.changeset/wallet-bound-proof.md @@ -0,0 +1,5 @@ +--- +'mppx': patch +--- + +Bound Tempo zero-amount proof credentials to the payer wallet. The EIP-712 `Proof` typed-data now includes an `account` field (domain version bumped to `3`), so a proof signature commits to a specific payer address and can no longer be replayed against a different account — including across an access key authorized for multiple accounts. Exposed the canonical proof contract via `tempo.Proof` (`types`, `domain`, `primaryType`, `message`, `typedData`, `hash`) and added deterministic conformance vectors covering the wallet-binding property. diff --git a/src/tempo/Proof.conformance.test.ts b/src/tempo/Proof.conformance.test.ts new file mode 100644 index 00000000..d88431bb --- /dev/null +++ b/src/tempo/Proof.conformance.test.ts @@ -0,0 +1,141 @@ +import { recoverTypedDataAddress } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { describe, expect, test } from 'vp/test' + +import * as Proof from './Proof.js' + +/** + * Deterministic conformance vector for the wallet-bound Tempo proof contract + * (EIP-712 domain `MPP` version `3`). These values pin the on-the-wire + * signing payload so any change to the proof ABI is caught here. + */ +const vector = { + account: '0x1a642f0E3c3aF545E7AcBD38b07251B3990914F1', + chainId: 42431, + challengeId: 'kM9xPqWvT2nJrHsY4aDfEb', + digest: '0x3860a700a55e02ad3c2dc047e92489feceecbdb0a801d948e1d9f0b61ea9bc3f', + privateKey: `0x${'01'.repeat(32)}`, + realm: 'api.example.com', + signature: + '0x53f5d64d9f995e841b4212639b2e17e508e96752e10316df3814a16443dcbdb626c082190a4c3ecc3148101eb443d15bd83b579380b1be735a9c99f0df36c9fe1b', +} as const + +const params = { + account: vector.account, + chainId: vector.chainId, + challengeId: vector.challengeId, + realm: vector.realm, +} as const + +describe('tempo.Proof conformance (wallet binding)', () => { + test('typedData is the canonical wallet-bound MPP v3 proof contract', () => { + expect(Proof.typedData(params)).toEqual({ + domain: { name: 'MPP', version: '3', chainId: vector.chainId }, + types: { + Proof: [ + { name: 'account', type: 'address' }, + { name: 'challengeId', type: 'string' }, + { name: 'realm', type: 'string' }, + ], + }, + primaryType: 'Proof', + message: { + account: vector.account, + challengeId: vector.challengeId, + realm: vector.realm, + }, + }) + }) + + test('hash matches the deterministic EIP-712 digest vector', () => { + expect(Proof.hash(params)).toBe(vector.digest) + }) + + test('the wallet produces the deterministic signature vector', async () => { + const account = privateKeyToAccount(vector.privateKey) + expect(account.address).toBe(vector.account) + const signature = await account.signTypedData(Proof.typedData(params)) + expect(signature).toBe(vector.signature) + }) + + test('the signature vector recovers to the bound wallet', async () => { + const recovered = await recoverTypedDataAddress({ + ...Proof.typedData(params), + signature: vector.signature, + }) + expect(recovered).toBe(vector.account) + }) + + test('the digest is bound to the wallet: a different account changes the digest', () => { + const other = '0x000000000000000000000000000000000000dEaD' + expect(Proof.hash({ ...params, account: other })).not.toBe(vector.digest) + }) + + test('a proof cannot be replayed against a different wallet for the same challenge', async () => { + const account = privateKeyToAccount(vector.privateKey) + const signature = await account.signTypedData(Proof.typedData(params)) + + // An attacker swaps the bound `account` to a wallet they want to impersonate + // while keeping the same challenge. Because `account` is a signed field, the + // recovered signer no longer matches the swapped wallet, so verification + // (and the access-key delegation check, which rebuilds the message from the + // claimed source) fails. + const swapped = '0x000000000000000000000000000000000000dEaD' + const recovered = await recoverTypedDataAddress({ + ...Proof.typedData({ ...params, account: swapped }), + signature, + }) + expect(recovered).not.toBe(swapped) + expect(recovered).not.toBe(vector.account) + }) + + test('models the access-key delegation path: swapping the source breaks signer recovery', async () => { + // An access key K signs a proof bound to root account A. The server's + // delegation check recovers the signer from the message it rebuilds using + // the *claimed* source, then requires `isActiveAccessKey(signer, source)`. + // Distinct access key (signer) and root account (the bound payer / source). + const accessKey = privateKeyToAccount(`0x${'02'.repeat(32)}`) + const rootA = vector.account // proof is signed bound to account = A + const signature = await accessKey.signTypedData(Proof.typedData({ ...params, account: rootA })) + expect(accessKey.address).not.toBe(rootA) + + // Honest submission (source = A): server recovers exactly K, so + // isActiveAccessKey(K, A) — the key actually authorized for A — is checked. + const recoveredForA = await recoverTypedDataAddress({ + ...Proof.typedData({ ...params, account: rootA }), + signature, + }) + expect(recoveredForA).toBe(accessKey.address) + + // Replay against a different root B (attacker swaps source to B): server + // rebuilds the message with account = B, recovering some K' != K. Even if K + // is an active access key of B, the server checks isActiveAccessKey(K', B), + // which cannot match the authorized key. Replay is rejected. + const rootB = '0x000000000000000000000000000000000000bEEF' + const recoveredForB = await recoverTypedDataAddress({ + ...Proof.typedData({ ...params, account: rootB }), + signature, + }) + expect(recoveredForB).not.toBe(accessKey.address) + }) + + test('a legacy v2 proof (no account field) does not verify under the v3 contract', async () => { + // The pre-binding contract: domain version "2", message without `account`. + const account = privateKeyToAccount(vector.privateKey) + const legacyTypedData = { + domain: { name: 'MPP', version: '2', chainId: vector.chainId }, + types: { Proof: [{ name: 'challengeId', type: 'string' }, { name: 'realm', type: 'string' }] }, + primaryType: 'Proof', + message: { challengeId: vector.challengeId, realm: vector.realm }, + } as const + const legacySignature = await account.signTypedData(legacyTypedData) + + // Verified against the current wallet-bound v3 contract, recovery yields a + // different address, so the server rejects stale v2 proofs. + const recovered = await recoverTypedDataAddress({ + ...Proof.typedData(params), + signature: legacySignature, + }) + expect(recovered).not.toBe(account.address) + }) +}) diff --git a/src/tempo/Proof.test-d.ts b/src/tempo/Proof.test-d.ts index 8aa06e08..e31dd168 100644 --- a/src/tempo/Proof.test-d.ts +++ b/src/tempo/Proof.test-d.ts @@ -1,7 +1,27 @@ +import type { Address, Hex } from 'viem' import { expectTypeOf, test } from 'vp/test' import { Proof } from './index.js' +test('Proof exports the wallet-bound typed-data contract helpers', () => { + expectTypeOf(Proof.message).toEqualTypeOf< + (parameters: { account: Address; challengeId: string; realm: string }) => { + readonly account: Address + readonly challengeId: string + readonly realm: string + } + >() + + expectTypeOf(Proof.hash).toEqualTypeOf< + (parameters: { + account: Address + chainId: number + challengeId: string + realm: string + }) => Hex + >() +}) + test('Proof exports public proof source helpers', () => { expectTypeOf(Proof.proofSource).toEqualTypeOf< (parameters: { address: string; chainId: number }) => string diff --git a/src/tempo/Proof.ts b/src/tempo/Proof.ts index c06bb4c8..402032de 100644 --- a/src/tempo/Proof.ts +++ b/src/tempo/Proof.ts @@ -1,7 +1,58 @@ -import type { Address } from 'viem' +import type { Address, Hex } from 'viem' import * as Proof_internal from './internal/proof.js' +/** EIP-712 primary type for Tempo proof credentials. */ +export const primaryType = Proof_internal.primaryType + +/** + * EIP-712 typed-data field definitions for Tempo zero-amount proof credentials. + * + * The `account` field cryptographically binds the signature to the payer + * wallet, so a proof signed for one account cannot be replayed against another. + */ +export const types = Proof_internal.types + +/** Constructs the EIP-712 domain for a Tempo proof credential. */ +export function domain(chainId: number) { + return Proof_internal.domain(chainId) +} + +/** + * Constructs the EIP-712 message for a Tempo proof credential. + * + * @param parameters - Proof message parameters. + * @param parameters.account - Payer wallet address the proof is bound to. + * @param parameters.challengeId - Challenge `id` being proven. + * @param parameters.realm - Challenge `realm` being proven. + */ +export function message(parameters: { account: Address; challengeId: string; realm: string }) { + return Proof_internal.message(parameters) +} + +/** + * Constructs the complete EIP-712 typed-data payload for a Tempo proof + * credential — the canonical, wallet-bound proof contract. + */ +export function typedData(parameters: { + account: Address + chainId: number + challengeId: string + realm: string +}) { + return Proof_internal.typedData(parameters) +} + +/** Computes the EIP-712 digest (signing payload) for a Tempo proof credential. */ +export function hash(parameters: { + account: Address + chainId: number + challengeId: string + realm: string +}): Hex { + return Proof_internal.hash(parameters) +} + /** Constructs the canonical `did:pkh:eip155` source DID for Tempo proof credentials. */ export function proofSource(parameters: { address: string; chainId: number }): string { return Proof_internal.proofSource(parameters) diff --git a/src/tempo/client/Charge.ts b/src/tempo/client/Charge.ts index 6c8ef589..8ab218dd 100644 --- a/src/tempo/client/Charge.ts +++ b/src/tempo/client/Charge.ts @@ -77,10 +77,14 @@ export function charge(parameters: charge.Parameters = {}) { if (BigInt(amount) === 0n) { const signature = await signTypedData(client, { account, - domain: Proof.domain(chainId), - types: Proof.types, - primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + // `account` here is the signing account; the proof's bound payer is + // `account.address` (echoed in the credential `source` below). + ...Proof.typedData({ + account: account.address, + chainId, + challengeId: challenge.id, + realm: challenge.realm, + }), }) return Credential.serialize({ challenge, diff --git a/src/tempo/internal/proof.test.ts b/src/tempo/internal/proof.test.ts index 157f3ec1..806acb26 100644 --- a/src/tempo/internal/proof.test.ts +++ b/src/tempo/internal/proof.test.ts @@ -44,9 +44,10 @@ const parsePkhSourceCases = [ ] as const describe('Proof', () => { - test('types has Proof with challengeId and realm fields', () => { + test('types has Proof with account, challengeId and realm fields', () => { expect(Proof.types).toEqual({ Proof: [ + { name: 'account', type: 'address' }, { name: 'challengeId', type: 'string' }, { name: 'realm', type: 'string' }, ], @@ -55,7 +56,7 @@ describe('Proof', () => { test('domain returns EIP-712 domain with name, version, chainId', () => { const d = Proof.domain(42431) - expect(d).toEqual({ name: 'MPP', version: '2', chainId: 42431 }) + expect(d).toEqual({ name: 'MPP', version: '3', chainId: 42431 }) }) test('domain uses provided chainId', () => { @@ -63,8 +64,15 @@ describe('Proof', () => { expect(Proof.domain(99999).chainId).toBe(99999) }) - test('message wraps challengeId and realm', () => { - expect(Proof.message('abc123', 'api.example.com')).toEqual({ + test('message wraps account, challengeId and realm', () => { + expect( + Proof.message({ + account: '0xAbCdEf1234567890AbCdEf1234567890AbCdEf12', + challengeId: 'abc123', + realm: 'api.example.com', + }), + ).toEqual({ + account: '0xAbCdEf1234567890AbCdEf1234567890AbCdEf12', challengeId: 'abc123', realm: 'api.example.com', }) diff --git a/src/tempo/internal/proof.ts b/src/tempo/internal/proof.ts index 3762eddb..6508834a 100644 --- a/src/tempo/internal/proof.ts +++ b/src/tempo/internal/proof.ts @@ -1,8 +1,18 @@ -import { isAddress, type Address } from 'viem' +import { hashTypedData, isAddress, type Address, type Hex } from 'viem' -/** EIP-712 typed data types for proof credentials. */ +/** EIP-712 primary type for proof credentials. */ +export const primaryType = 'Proof' as const + +/** + * EIP-712 typed-data field definitions for Tempo zero-amount proof credentials. + * + * The `account` field cryptographically binds the signature to the payer + * wallet, so a proof signed for one account cannot be replayed against another + * — including across an access key that is authorized for multiple accounts. + */ export const types = { Proof: [ + { name: 'account', type: 'address' }, { name: 'challengeId', type: 'string' }, { name: 'realm', type: 'string' }, ], @@ -10,12 +20,51 @@ export const types = { /** Constructs the EIP-712 domain for a proof credential. */ export function domain(chainId: number) { - return { name: 'MPP', version: '2', chainId } as const + return { name: 'MPP', version: '3', chainId } as const +} + +/** + * Constructs the EIP-712 message for a proof credential. + * + * @param parameters - Proof message parameters. + * @param parameters.account - Payer wallet address the proof is bound to. + * @param parameters.challengeId - Challenge `id` being proven. + * @param parameters.realm - Challenge `realm` being proven. + */ +export function message(parameters: { account: Address; challengeId: string; realm: string }) { + const { account, challengeId, realm } = parameters + return { account, challengeId, realm } as const +} + +/** + * Constructs the complete EIP-712 typed-data payload for a proof credential. + * + * This is the canonical, wallet-bound proof contract: signing this payload + * commits the signer to a specific `account`, `challengeId`, and `realm`. + */ +export function typedData(parameters: { + account: Address + chainId: number + challengeId: string + realm: string +}) { + const { account, chainId, challengeId, realm } = parameters + return { + domain: domain(chainId), + types, + primaryType, + message: message({ account, challengeId, realm }), + } as const } -/** Constructs the EIP-712 message for a proof credential. */ -export function message(challengeId: string, realm: string) { - return { challengeId, realm } as const +/** Computes the EIP-712 digest (signing payload) for a proof credential. */ +export function hash(parameters: { + account: Address + chainId: number + challengeId: string + realm: string +}): Hex { + return hashTypedData(typedData(parameters)) } /** Constructs the expected `did:pkh` source DID for a proof credential. */ diff --git a/src/tempo/server/Charge.test.ts b/src/tempo/server/Charge.test.ts index 83272666..992172a7 100644 --- a/src/tempo/server/Charge.test.ts +++ b/src/tempo/server/Charge.test.ts @@ -2430,7 +2430,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -2468,7 +2468,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -2583,7 +2583,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -2744,7 +2744,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -2805,7 +2805,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.serialize( @@ -2879,7 +2879,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -2942,7 +2942,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge1.id, challenge1.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge1.id, realm: challenge1.realm }), }) const credential1 = Credential.from({ @@ -2973,7 +2973,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge2.id, challenge2.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge2.id, realm: challenge2.realm }), }) const credential2 = Credential.from({ @@ -3010,7 +3010,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -3046,7 +3046,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -3145,7 +3145,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -3183,7 +3183,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -3222,7 +3222,7 @@ describe('tempo', () => { domain: Proof.domain(99999), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -3258,7 +3258,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, 'evil.example.com'), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: 'evil.example.com' }), }) const credential = Credential.from({ @@ -3294,7 +3294,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ @@ -3330,7 +3330,7 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), }) const credential = Credential.from({ diff --git a/src/tempo/server/Charge.ts b/src/tempo/server/Charge.ts index f1ef2113..da025284 100644 --- a/src/tempo/server/Charge.ts +++ b/src/tempo/server/Charge.ts @@ -2,7 +2,6 @@ import * as SignatureEnvelope from 'ox/tempo/SignatureEnvelope' import { decodeFunctionData, formatUnits, - hashTypedData, keccak256, parseEventLogs, type TransactionReceipt, @@ -292,11 +291,15 @@ export function charge( } const valid = await verifyTypedData(client, { + // Bind verification to the claimed payer (`source.address`): the + // signer may be the payer itself or an access key authorized for it. address: source.address, - domain: Proof.domain(resolvedChainId), - types: Proof.types, - primaryType: 'Proof', - message: Proof.message(challenge.id, challenge.realm), + ...Proof.typedData({ + account: source.address, + chainId: resolvedChainId, + challengeId: challenge.id, + realm: challenge.realm, + }), signature: payload.signature as `0x${string}`, }) if (!valid) { @@ -1005,12 +1008,7 @@ function recoverAuthorizedProofSigner(parameters: { try { const envelope = SignatureEnvelope.from(signature) - const proofHash = hashTypedData({ - domain: Proof.domain(chainId), - types: Proof.types, - primaryType: 'Proof', - message: Proof.message(challengeId, realm), - }) + const proofHash = Proof.hash({ account: sourceAddress, chainId, challengeId, realm }) if (envelope.type === 'keychain') { if (!TempoAddress.isEqual(envelope.userAddress, sourceAddress)) return null From 458a34654c1360676ad1c2517ebaf3cc041cbda3 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 10 Jun 2026 16:21:34 -0600 Subject: [PATCH 2/2] fix --- src/tempo/Proof.conformance.test.ts | 7 ++- src/tempo/Proof.test-d.ts | 7 +-- src/tempo/server/Charge.test.ts | 96 ++++++++++++++++++++++++----- 3 files changed, 87 insertions(+), 23 deletions(-) diff --git a/src/tempo/Proof.conformance.test.ts b/src/tempo/Proof.conformance.test.ts index d88431bb..dbddcfcb 100644 --- a/src/tempo/Proof.conformance.test.ts +++ b/src/tempo/Proof.conformance.test.ts @@ -124,7 +124,12 @@ describe('tempo.Proof conformance (wallet binding)', () => { const account = privateKeyToAccount(vector.privateKey) const legacyTypedData = { domain: { name: 'MPP', version: '2', chainId: vector.chainId }, - types: { Proof: [{ name: 'challengeId', type: 'string' }, { name: 'realm', type: 'string' }] }, + types: { + Proof: [ + { name: 'challengeId', type: 'string' }, + { name: 'realm', type: 'string' }, + ], + }, primaryType: 'Proof', message: { challengeId: vector.challengeId, realm: vector.realm }, } as const diff --git a/src/tempo/Proof.test-d.ts b/src/tempo/Proof.test-d.ts index e31dd168..fe75286b 100644 --- a/src/tempo/Proof.test-d.ts +++ b/src/tempo/Proof.test-d.ts @@ -13,12 +13,7 @@ test('Proof exports the wallet-bound typed-data contract helpers', () => { >() expectTypeOf(Proof.hash).toEqualTypeOf< - (parameters: { - account: Address - chainId: number - challengeId: string - realm: string - }) => Hex + (parameters: { account: Address; chainId: number; challengeId: string; realm: string }) => Hex >() }) diff --git a/src/tempo/server/Charge.test.ts b/src/tempo/server/Charge.test.ts index 992172a7..8fdb7dfa 100644 --- a/src/tempo/server/Charge.test.ts +++ b/src/tempo/server/Charge.test.ts @@ -2430,7 +2430,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -2468,7 +2472,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -2583,7 +2591,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -2744,7 +2756,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -2805,7 +2821,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.serialize( @@ -2879,7 +2899,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -2942,7 +2966,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge1.id, realm: challenge1.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge1.id, + realm: challenge1.realm, + }), }) const credential1 = Credential.from({ @@ -2973,7 +3001,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge2.id, realm: challenge2.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge2.id, + realm: challenge2.realm, + }), }) const credential2 = Credential.from({ @@ -3010,7 +3042,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -3046,7 +3082,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -3145,7 +3185,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -3183,7 +3227,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -3222,7 +3270,11 @@ describe('tempo', () => { domain: Proof.domain(99999), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -3258,7 +3310,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: 'evil.example.com' }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: 'evil.example.com', + }), }) const credential = Credential.from({ @@ -3294,7 +3350,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({ @@ -3330,7 +3390,11 @@ describe('tempo', () => { domain: Proof.domain(chain.id), types: Proof.types, primaryType: 'Proof', - message: Proof.message({ account: accounts[1].address, challengeId: challenge.id, realm: challenge.realm }), + message: Proof.message({ + account: accounts[1].address, + challengeId: challenge.id, + realm: challenge.realm, + }), }) const credential = Credential.from({