Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

counterfactual sig verification #49

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/agw-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
"types": "./dist/types/exports/index.d.ts",
"import": "./dist/esm/exports/index.js",
"require": "./dist/cjs/exports/index.js"
},
"./actions": {
"types": "./dist/types/exports/actions.d.ts",
"import": "./dist/esm/exports/actions.js",
"require": "./dist/cjs/exports/actions.js"
}
},
"files": [
Expand Down
28 changes: 28 additions & 0 deletions packages/agw-client/src/abis/UniversalSigValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const UniversalSignatureValidatorAbi = [
{
inputs: [
{
name: '_signer',
type: 'address',
},
{
name: '_hash',
type: 'bytes32',
},
{
name: '_signature',
type: 'bytes',
},
],
outputs: [
{
type: 'boolean',
},
],
name: 'isValidUniversalSig',
stateMutability: 'nonpayable',
type: 'function',
},
] as const;

Check warning on line 26 in packages/agw-client/src/abis/UniversalSigValidator.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/abis/UniversalSigValidator.ts#L2-L26

Added lines #L2 - L26 were not covered by tests

export default UniversalSignatureValidatorAbi;

Check warning on line 28 in packages/agw-client/src/abis/UniversalSigValidator.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/abis/UniversalSigValidator.ts#L28

Added line #L28 was not covered by tests
27 changes: 27 additions & 0 deletions packages/agw-client/src/abstractPublicActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
type Account,
type Chain,
type Client,
type PublicActions,
publicActions,
type Transport,
} from 'viem';

Check warning on line 8 in packages/agw-client/src/abstractPublicActions.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/abstractPublicActions.ts#L8

Added line #L8 was not covered by tests

import { verifyMessage } from './actions/verifyMessage';
import { verifySiweMessage } from './actions/verifySiweMessage';
import { verifyTypedData } from './actions/verifyTypedData';

Check warning on line 12 in packages/agw-client/src/abstractPublicActions.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/abstractPublicActions.ts#L10-L12

Added lines #L10 - L12 were not covered by tests

export function abstractPublicActions<

Check warning on line 14 in packages/agw-client/src/abstractPublicActions.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/abstractPublicActions.ts#L14

Added line #L14 was not covered by tests
transport extends Transport = Transport,
chain extends Chain = Chain,
account extends Account | undefined = Account | undefined,
>(
client: Client<transport, chain, account>,
): PublicActions<transport, chain, account> {
return {
...publicActions(client),
verifyMessage: (args) => verifyMessage(client, args),
verifySiweMessage: (args) => verifySiweMessage(client, args),
verifyTypedData: (args) => verifyTypedData(client, args),
};
}

Check warning on line 27 in packages/agw-client/src/abstractPublicActions.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/abstractPublicActions.ts#L19-L27

Added lines #L19 - L27 were not covered by tests
116 changes: 116 additions & 0 deletions packages/agw-client/src/actions/verifyHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import {
bytesToHex,
CallExecutionError,
type CallParameters,
type Chain,
type Client,
encodeFunctionData,
getAddress,
hexToBool,
isAddressEqual,
isErc6492Signature,
isHex,
recoverAddress,
serializeErc6492Signature,
serializeSignature,
type Transport,
} from 'viem';

Check warning on line 17 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L17

Added line #L17 was not covered by tests
import {
call,
verifyHash as viemVerifyHash,
type VerifyHashParameters,
type VerifyHashReturnType,
} from 'viem/actions';
import { abstractTestnet } from 'viem/chains';
import { getAction } from 'viem/utils';

Check warning on line 25 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L23-L25

Added lines #L23 - L25 were not covered by tests

import UniversalSignatureValidatorAbi from '../abis/UniversalSigValidator.js';
import { UNIVERSAL_SIGNATURE_VALIDATOR_ADDRESS } from '../constants.js';

Check warning on line 28 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L27-L28

Added lines #L27 - L28 were not covered by tests

const supportedChains: Record<number, Chain> = {
[abstractTestnet.id]: abstractTestnet,
};

Check warning on line 32 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L30-L32

Added lines #L30 - L32 were not covered by tests

/**
* Verifies a message hash onchain using ERC-6492.
*
* @param client - Client to use.
* @param parameters - {@link VerifyHashParameters}
* @returns Whether or not the signature is valid. {@link VerifyHashReturnType}
*/
export async function verifyHash<chain extends Chain>(
client: Client<Transport, chain>,
parameters: VerifyHashParameters,
): Promise<VerifyHashReturnType> {
const { address, factory, factoryData, hash, signature, ...rest } =
parameters;

Check warning on line 46 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L41-L46

Added lines #L41 - L46 were not covered by tests

if (!supportedChains[client.chain.id]) {
return await viemVerifyHash(client, parameters);
}

Check warning on line 50 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L48-L50

Added lines #L48 - L50 were not covered by tests

const signatureHex = (() => {
if (isHex(signature)) return signature;
if (typeof signature === 'object' && 'r' in signature && 's' in signature)
return serializeSignature(signature);
return bytesToHex(signature);
})();

Check warning on line 57 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L52-L57

Added lines #L52 - L57 were not covered by tests

const wrappedSignature = await (async () => {

Check warning on line 59 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L59

Added line #L59 was not covered by tests
// If no `factory` or `factoryData` is provided, it is assumed that the
// address is not a Smart Account, or the Smart Account is already deployed.
if (!factory && !factoryData) return signatureHex;

Check warning on line 62 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L62

Added line #L62 was not covered by tests

// If the signature is already wrapped, return the signature.
if (isErc6492Signature(signatureHex)) return signatureHex;

Check warning on line 65 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L65

Added line #L65 was not covered by tests

// If the Smart Account is not deployed, wrap the signature with a 6492 wrapper
// to perform counterfactual validation.
return serializeErc6492Signature({

Check warning on line 69 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L69

Added line #L69 was not covered by tests
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
address: factory!,

Check warning on line 71 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L71

Added line #L71 was not covered by tests
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
data: factoryData!,
signature: signatureHex,
});
})();

Check warning on line 76 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L73-L76

Added lines #L73 - L76 were not covered by tests

try {
const { data } = await getAction(
client,
call,
'call',
)({
to: UNIVERSAL_SIGNATURE_VALIDATOR_ADDRESS,
data: encodeFunctionData({
abi: UniversalSignatureValidatorAbi,
functionName: 'isValidUniversalSig',
args: [address, hash, wrappedSignature],
}),
...rest,
} as unknown as CallParameters);

Check warning on line 91 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L78-L91

Added lines #L78 - L91 were not covered by tests

const isValid = hexToBool(data ?? '0x0');

Check warning on line 93 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L93

Added line #L93 was not covered by tests

return isValid;
} catch (error) {

Check warning on line 96 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L95-L96

Added lines #L95 - L96 were not covered by tests
// Fallback attempt to verify the signature via ECDSA recovery.
try {
const verified = isAddressEqual(
getAddress(address),
await recoverAddress({ hash, signature }),
);
if (verified) return true;

Check warning on line 103 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L98-L103

Added lines #L98 - L103 were not covered by tests
// eslint-disable-next-line no-empty
} catch {}

Check warning on line 105 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L105

Added line #L105 was not covered by tests

if (error instanceof CallExecutionError) {

Check warning on line 107 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L107

Added line #L107 was not covered by tests
// if the execution fails, the signature was not valid and an internal method inside of the validator reverted
// this can happen for many reasons, for example if signer can not be recovered from the signature
// or if the signature has no valid format
return false;
}

Check warning on line 112 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L111-L112

Added lines #L111 - L112 were not covered by tests

throw error;
}
}

Check warning on line 116 in packages/agw-client/src/actions/verifyHash.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyHash.ts#L114-L116

Added lines #L114 - L116 were not covered by tests
42 changes: 42 additions & 0 deletions packages/agw-client/src/actions/verifyMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { type Chain, type Client, hashMessage, type Transport } from 'viem';
import type {
VerifyMessageParameters,
VerifyMessageReturnType,
} from 'viem/actions';

import { verifyHash } from './verifyHash';

Check warning on line 7 in packages/agw-client/src/actions/verifyMessage.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyMessage.ts#L7

Added line #L7 was not covered by tests

/**
* Verify that a message was signed by the provided address.
*
* Compatible with Smart Contract Accounts & Externally Owned Accounts via [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492).
*
* - Docs {@link https://viem.sh/docs/actions/public/verifyMessage}
*
* @param client - Client to use.
* @param parameters - {@link VerifyMessageParameters}
* @returns Whether or not the signature is valid. {@link VerifyMessageReturnType}
*/
export async function verifyMessage<chain extends Chain>(
client: Client<Transport, chain>,
{
address,
message,
factory,
factoryData,
signature,
...callRequest
}: VerifyMessageParameters,
): Promise<VerifyMessageReturnType> {
const hash = hashMessage(message);
return verifyHash(client, {
address,

Check warning on line 33 in packages/agw-client/src/actions/verifyMessage.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyMessage.ts#L20-L33

Added lines #L20 - L33 were not covered by tests
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
factory: factory!,

Check warning on line 35 in packages/agw-client/src/actions/verifyMessage.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyMessage.ts#L35

Added line #L35 was not covered by tests
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
factoryData: factoryData!,
hash,
signature,
...callRequest,
});
}

Check warning on line 42 in packages/agw-client/src/actions/verifyMessage.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyMessage.ts#L37-L42

Added lines #L37 - L42 were not covered by tests
57 changes: 57 additions & 0 deletions packages/agw-client/src/actions/verifySiweMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { type Chain, type Client, hashMessage, type Transport } from 'viem';
import type {
VerifySiweMessageParameters,
VerifySiweMessageReturnType,
} from 'viem/_types/actions/siwe/verifySiweMessage';

import { parseSiweMessage } from '../utils/siwe/parseSiweMessage';
import { validateSiweMessage } from '../utils/siwe/validateSiweMessage';
import { verifyHash } from './verifyHash';

Check warning on line 9 in packages/agw-client/src/actions/verifySiweMessage.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifySiweMessage.ts#L7-L9

Added lines #L7 - L9 were not covered by tests

/**
* Verifies [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) formatted message was signed.
*
* Compatible with Smart Contract Accounts & Externally Owned Accounts via [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492).
*
* - Docs {@link https://viem.sh/docs/siwe/actions/verifySiweMessage}
*
* @param client - Client to use.
* @param parameters - {@link VerifySiweMessageParameters}
* @returns Whether or not the signature is valid. {@link VerifySiweMessageReturnType}
*/
export async function verifySiweMessage<chain extends Chain>(
client: Client<Transport, chain>,
parameters: VerifySiweMessageParameters,
): Promise<VerifySiweMessageReturnType> {
const {
address,
domain,
message,
nonce,
scheme,
signature,
time = new Date(),
...callRequest
} = parameters;

Check warning on line 35 in packages/agw-client/src/actions/verifySiweMessage.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifySiweMessage.ts#L22-L35

Added lines #L22 - L35 were not covered by tests

const parsed = parseSiweMessage(message);
if (!parsed.address) return false;

Check warning on line 38 in packages/agw-client/src/actions/verifySiweMessage.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifySiweMessage.ts#L37-L38

Added lines #L37 - L38 were not covered by tests

const isValid = validateSiweMessage({
address,
domain,
message: parsed,
nonce,
scheme,
time,
});
if (!isValid) return false;

Check warning on line 48 in packages/agw-client/src/actions/verifySiweMessage.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifySiweMessage.ts#L40-L48

Added lines #L40 - L48 were not covered by tests

const hash = hashMessage(message);
return verifyHash(client, {
address: parsed.address,
hash,
signature,
...callRequest,
});
}

Check warning on line 57 in packages/agw-client/src/actions/verifySiweMessage.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifySiweMessage.ts#L50-L57

Added lines #L50 - L57 were not covered by tests
50 changes: 50 additions & 0 deletions packages/agw-client/src/actions/verifyTypedData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
type Chain,
type Client,
hashTypedData,
type Transport,
type TypedData,
type VerifyTypedDataReturnType,
} from 'viem';

Check warning on line 8 in packages/agw-client/src/actions/verifyTypedData.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyTypedData.ts#L8

Added line #L8 was not covered by tests
import type { VerifyTypedDataParameters } from 'viem/actions';

import { verifyHash } from './verifyHash';

Check warning on line 11 in packages/agw-client/src/actions/verifyTypedData.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyTypedData.ts#L11

Added line #L11 was not covered by tests

/**
* Verify that typed data was signed by the provided address.
*
* - Docs {@link https://viem.sh/docs/actions/public/verifyTypedData}
*
* @param client - Client to use.
* @param parameters - {@link VerifyTypedDataParameters}
* @returns Whether or not the signature is valid. {@link VerifyTypedDataReturnType}
*/
export async function verifyTypedData<

Check warning on line 22 in packages/agw-client/src/actions/verifyTypedData.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyTypedData.ts#L22

Added line #L22 was not covered by tests
const typedData extends TypedData | Record<string, unknown>,
primaryType extends keyof typedData | 'EIP712Domain',
chain extends Chain,
>(
client: Client<Transport, chain>,
parameters: VerifyTypedDataParameters<typedData, primaryType>,
): Promise<VerifyTypedDataReturnType> {
const {
address,
factory,
factoryData,
signature,
message,
primaryType,
types,
domain,
...callRequest
} = parameters as VerifyTypedDataParameters;
const hash = hashTypedData({ message, primaryType, types, domain });
return verifyHash(client, {
address,
factory: factory!,

Check warning on line 44 in packages/agw-client/src/actions/verifyTypedData.ts

View workflow job for this annotation

GitHub Actions / publish

Forbidden non-null assertion
factoryData: factoryData!,

Check warning on line 45 in packages/agw-client/src/actions/verifyTypedData.ts

View workflow job for this annotation

GitHub Actions / publish

Forbidden non-null assertion
hash,
signature,
...callRequest,
});
}

Check warning on line 50 in packages/agw-client/src/actions/verifyTypedData.ts

View check run for this annotation

Codecov / codecov/patch

packages/agw-client/src/actions/verifyTypedData.ts#L27-L50

Added lines #L27 - L50 were not covered by tests
4 changes: 4 additions & 0 deletions packages/agw-client/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ const VALIDATOR_ADDRESS = '0xC894DE2894e2F84C0C2944FDcce9490eC22A92b6';
const CONTRACT_DEPLOYER_ADDRESS =
'0x0000000000000000000000000000000000008006' as const;

const UNIVERSAL_SIGNATURE_VALIDATOR_ADDRESS =
'0x4d98aa5724ef4f638d326Eac4Ab032C67B08ac65' as const;

const INSUFFICIENT_BALANCE_SELECTOR = '0xe7931438' as const;

export {
BATCH_CALLER_ADDRESS,
CONTRACT_DEPLOYER_ADDRESS,
INSUFFICIENT_BALANCE_SELECTOR,
SMART_ACCOUNT_FACTORY_ADDRESS,
UNIVERSAL_SIGNATURE_VALIDATOR_ADDRESS,
VALIDATOR_ADDRESS,
};
Loading
Loading