Skip to content

Commit

Permalink
docs: refactor and update AA reference (#236)
Browse files Browse the repository at this point in the history
- closes #190
- reorders the pages in the AA developer reference to improve
information flow
- adds new details about using P256Verify & using smart accounts
  • Loading branch information
sarahschwartz authored Oct 11, 2024
1 parent 89b8073 commit 2a6fe0d
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,40 @@ For this purpose, use the `createAccount` or `create2Account` methods of the dep
system contract, rather than the general `create` or `create2` methods, which are
used for contracts not intended to act as accounts.

#### Example Using `zksync-ethers` SDK (v5)
#### Example Using `zksync-ethers` SDK (v6)

You can use the [`ECDSASmartAccount`](https://sdk.zksync.io/js/ethers/api/v6/accounts/smartaccount-factories#ecdsasmartaccount) or
[`MultisigECDSASmartAccount`](https://sdk.zksync.io/js/ethers/api/v6/accounts/smartaccount-factories#multisigecdsasmartaccount)
to create a standard smart account or multisig smart account.

```ts
import { ContractFactory } from "zksync-ethers";
import { types, ECDSASmartAccount, MultisigECDSASmartAccount, } from "zksync-ethers";

const account = ECDSASmartAccount.create(ADDRESS, PRIVATE_KEY, provider);
const multisigAccount = MultisigECDSASmartAccount.create(multisigAddress, [PRIVATE_KEY1, PRIVATE_KEY2], provider);
```

You can also deploy with custom factory and account contracts using the example below.

const contractFactory = new ContractFactory(abi, bytecode, initiator, "createAccount");
const aa = await contractFactory.deploy(...args);
await aa.deployed();
```ts
async function deployAAFactory(hre: HardhatRuntimeEnvironment) {
const deployer = hre.deployer;
// the factory contract
const factoryArtifact = await deployer.loadArtifact('AAFactory');
// the account contract
const aaArtifact = await deployer.loadArtifact('Account');

const factory = await deployer.deploy(
factoryArtifact,
[utils.hashBytecode(aaArtifact.bytecode)],
undefined,
undefined,
[aaArtifact.bytecode]
);
const factoryAddress = await factory.getAddress();
console.log(`AA factory address: ${factoryAddress}`);
return factory;
}
```

### Verification Step Limitations
Expand Down Expand Up @@ -65,19 +91,57 @@ Currently, only EIP712 formatted transactions are supported for sending from cus
accounts. Transactions must specify the `from` field as the account's address and
include a `customSignature` in the `customData`.

#### Example Transaction Submission
#### Example Transaction Submission With a Private Key

To send a transaction using the private key of a deployed smart account, you can use the
[`SmartAccount`](https://sdk.zksync.io/js/ethers/api/v6/accounts/smartaccount#sendtransaction) class from `zksync-ethers`:

```ts
import { SmartAccount, Provider, types } from "zksync-ethers";
import { parseEther } from "ethers";

const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);

const signedTx = await account.sendTransaction({
to: recipientAddress,
value: parseEther(amount),
});
```

#### Example Transaction Submission Without a Private Key

You can also manually add a custom signature to a transaction,
like in the example below.

```ts
import { utils } from "zksync-ethers";
import { types, Provider } from "zksync-ethers";
import { parseEther, AbiCoder } from "ethers";

const provider = new Provider(rpcUrl);
let tx: types.TransactionLike = {
to: recipientAddress,
value: parseEther(amount),
gasPrice: await provider.getGasPrice(),
gasLimit: BigInt(20000000),
chainId: (await provider.getNetwork()).chainId,
nonce: await provider.getTransactionCount(aaAddress),
};

const abiCoder = new AbiCoder();
const aaSignature = abiCoder.encode(
['string', 'string'],
['hello', 'world']
);

// Here, `tx` is a `TransactionRequest` object from `zksync-ethers` SDK.
// `zksyncProvider` is the `Provider` object from `zksync-ethers` SDK connected to the ZKSync network.
tx.from = aaAddress;
tx.type = 113;
tx.customData = {
...tx.customData,
customSignature: aaSignature,
};
const serializedTx = utils.serialize({ ...tx });

const sentTx = await zksyncProvider.sendTransaction(serializedTx);
const sentTx = await provider.broadcastTransaction(
types.Transaction.from(tx).serialized
);
await sentTx.wait();
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
title: Signature Validation
description: Recommended approaches to signature validation.
---

One of the most notable differences between various types of accounts to be built
is different signature schemes. We expect accounts to support the [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) standard.

## OpenZeppelin Libraries

The
[`@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5ed5a86d1d22f387ce69ab4e0ace405de8bc888d/contracts/utils/cryptography/SignatureChecker.sol#L22)
library provides a way to verify signatures for different
account implementations. We strongly encourage you to use this library whenever you need to check that a signature of an account is correct.

### Adding the library to your project

::code-group

```bash [npm]
npm add @openzeppelin/contracts
```

```bash [yarn]
yarn add @openzeppelin/contracts
```

```bash [pnpm]
pnpm add @openzeppelin/contracts
```

```bash [bun]
bun add @openzeppelin/contracts
```

::

### Example of using the library

```solidity
pragma solidity ^0.8.0;
import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
contract TestSignatureChecker {
using SignatureChecker for address;
function isValidSignature(
address _address,
bytes32 _hash,
bytes memory _signature
) public pure returns (bool) {
return _address.isValidSignatureNow(_hash, _signature);
}
}
```

## Verifying AA signatures

Our SDK provides two methods within `utils` to verify the signature of an account:
[`isMessageSignatureCorrect`](https://sdk.zksync.io/js/ethers/api/v6/utilities#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect`](https://sdk.zksync.io/js/ethers/api/v6/utilities#istypeddatasignaturecorrect).

```ts
import { utils, EIP712Signer } from "zksync-ethers";

const isValidMessageSignature = await utils.isMessageSignatureCorrect(provider, ADDRESS, message, messageSignature);

const isValidTypesSignature = await utils.isTypedDataSignatureCorrect(provider, ADDRESS, await eip712Signer.getDomain(), utils.EIP712_TYPES, EIP712Signer.getSignInput(tx), typedSignature);
```

Currently these methods only support verifying ECDSA signatures, but very soon they will support EIP1271 signature verification as well.

Both of these methods return `true` or `false` depending on whether the message signature is correct.

It is **not recommended** to use the `ethers.js` library to verify user signatures.

## Validating Secp256r1 Signatures

The [`P256Verify` precompile](https://docs.zksync.io/build/developer-reference/era-contracts/elliptic-curve-precompiles#p256)
is available to validate secp256r1 signatures.

```solidity
address constant P256 = 0x0000000000000000000000000000000000000100;
/**
* input[ 0: 32] = signed data hash
* input[ 32: 64] = signature r
* input[ 64: 96] = signature s
* input[ 96:128] = public key x
* input[128:160] = public key y
*/
bytes memory input = abi.encodePacked(
hash,
rs[0],
rs[1],
pubKey[0],
pubKey[1]
);
(bool __, bytes memory output) = P256.staticcall(input);
// if signature is valid:
// output = 0x0000000000000000000000000000000000000000000000000000000000000001
// if signature is NOT valid:
// output.length == 0
```

You can find a more in-depth example showing how
it can be used in the ["Signing Transactions with WebAuthn"](https://code.zksync.io/tutorials/signing-transactions-with-webauthn) tutorial.

This file was deleted.

0 comments on commit 2a6fe0d

Please sign in to comment.