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

use credential ID to track signers in Account contract #20

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 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
8 changes: 4 additions & 4 deletions packages/common/src/encoding/encodeChallenge.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { ECDSASigValue } from '@peculiar/asn1-ecc';
import { AsnParser } from '@peculiar/asn1-schema';
import { ethers } from 'ethers';
import { BytesLike, ethers } from 'ethers';
import { uint8ArrayToUint256 } from './numbers';

export function encodeChallenge(pubKey: {x: string, y: string}, assertionResponse: AuthenticatorAssertionResponse) {
export function encodeChallenge(credentialId: BytesLike, assertionResponse: AuthenticatorAssertionResponse) {
const decodedClientDataJson = new TextDecoder().decode(assertionResponse.clientDataJSON);
const responseTypeLocation = decodedClientDataJson.indexOf('"type":');
const challengeLocation = decodedClientDataJson.indexOf('"challenge":');
const parsedSignature = AsnParser.parse(assertionResponse.signature, ECDSASigValue);

return ethers.AbiCoder.defaultAbiCoder().encode(
[
'tuple(bytes authenticatorData, string clientDataJSON, uint256 challengeLocation, uint256 responseTypeLocation, uint256 r, uint256 s,tuple(uint256 x, uint256 y))',
'tuple(bytes credentialId, bytes authenticatorData, string clientDataJSON, uint256 challengeLocation, uint256 responseTypeLocation, uint256 r, uint256 s)',
],
[
[
credentialId,
new Uint8Array(assertionResponse.authenticatorData),
decodedClientDataJson,
challengeLocation,
responseTypeLocation,
uint8ArrayToUint256(parsedSignature.r),
uint8ArrayToUint256(parsedSignature.s),
[pubKey.x, pubKey.y],
],
],
);
Expand Down
13 changes: 7 additions & 6 deletions packages/contracts/contracts/AbstractAccountFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import {Types} from './Types.sol';
*/
abstract contract AbstractAccountFactory {
/**
* @notice Deploys an account contract with the given public key and registry address
* @dev This function must be implemented by concrete factory contracts
* @param publicKey The public key to associate with the account as the admin key
* @param registry The address of the registry contract that will manage the account
* @return The address of the deployed account contract
* @notice Deploys a new Account contract
* @dev Must be implemented by derived contracts
* @param credentialId The credential identifier for the admin credential
* @param publicKey The public key for the admin credential
* @param registry The address of the AccountRegistry contract
* @return The address of the deployed Account contract
*/
function deployAccount(Types.PublicKey calldata publicKey, address registry) external virtual returns (address);
function deployAccount(bytes calldata credentialId, Types.PublicKey calldata publicKey, address registry) public virtual returns (address);
}
329 changes: 152 additions & 177 deletions packages/contracts/contracts/Account.sol

Large diffs are not rendered by default.

34 changes: 16 additions & 18 deletions packages/contracts/contracts/AccountFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,48 @@ contract AccountFactory is AbstractAccountFactory {
/**
* @notice Emitted when a new account is deployed
* @param account The address of the deployed account
* @param publicKeyX The X coordinate of the initial admin key
* @param publicKeyY The Y coordinate of the initial admin key
* @param registry The address of the registry
* @param credentialId The credential identifier for the admin credential
*/
event AccountDeployed(address indexed account, bytes32 indexed publicKeyX, bytes32 publicKeyY, address indexed registry);
event AccountDeployed(address indexed account, bytes credentialId);

/**
* @notice Deploys a new Account contract with the given public key and registry address
* @dev Creates a new Account instance using CREATE2 for predictable addresses and returns its address
* @param publicKey The public key to associate with the account as the admin key
* @param credentialId The credential identifier for the admin credential
* @param publicKey The public key to associate with the account as the admin credential
* @param registry The address of the registry contract that will manage the account
* @return accountAddress The address of the deployed Account contract
*/
function deployAccount(Types.PublicKey calldata publicKey, address registry) external override returns (address accountAddress) {
// Compute salt from public key and registry
bytes32 salt = keccak256(abi.encode(publicKey.x, publicKey.y, registry));
function deployAccount(bytes calldata credentialId, Types.PublicKey calldata publicKey, address registry) public override returns (address accountAddress) {
// Compute the salt using all parameters to ensure deterministic addresses
bytes32 salt = keccak256(abi.encode(publicKey, credentialId, registry));

// Deploy using CREATE2 for deterministic addresses
accountAddress = address(new Account{salt: salt}(publicKey, registry));
// Deploy the account contract
accountAddress = address(new Account{salt: salt}(publicKey, credentialId, registry));

emit AccountDeployed(accountAddress, publicKey.x, publicKey.y, registry);
emit AccountDeployed(accountAddress, credentialId);

return accountAddress;
}

/**
* @notice Computes the address where an account would be deployed
* @dev Uses the same logic as deployAccount to predict the address without deploying
* @param credentialId The credential identifier for the admin credential
* @param publicKey The public key that would be associated with the account
* @param registry The address of the registry that would manage the account
* @return The predicted address where the account would be deployed
*/
function computeAccountAddress(Types.PublicKey calldata publicKey, address registry) external view returns (address) {
bytes32 salt = keccak256(abi.encode(publicKey.x, publicKey.y, registry));
function computeAccountAddress(bytes calldata credentialId, Types.PublicKey calldata publicKey, address registry) public view returns (address) {
// Compute the salt using all parameters to ensure deterministic addresses
bytes32 salt = keccak256(abi.encode(publicKey, credentialId, registry));

// Compute the CREATE2 address
// Compute the address using CREATE2
return address(uint160(uint256(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(abi.encodePacked(
type(Account).creationCode,
abi.encode(publicKey, registry)
))
keccak256(abi.encodePacked(type(Account).creationCode, abi.encode(publicKey, credentialId, registry)))
)))));
}
}
Loading