Skip to content

Commit

Permalink
docs: test daily spend limit tutorial (#56)
Browse files Browse the repository at this point in the history
adds a test for the daily spend limit tutorial and minor fixes
  • Loading branch information
sarahschwartz authored Aug 29, 2024
1 parent 939b796 commit 3398dae
Show file tree
Hide file tree
Showing 11 changed files with 424 additions and 51 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/playwright.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
tutorial:
- "tests/erc20-paymaster.spec.ts"
- "tests/how-to-test-contracts.spec.ts"
- "tests/daily-spend-limit.spec.ts"

steps:
- uses: actions/checkout@v4
Expand All @@ -21,6 +22,11 @@ jobs:
- uses: actions/setup-node@v4
- name: Install Playwright Browsers
run: bun playwright install chromium --with-deps
- name: Install Era Test Node
run: |
curl --proto '=https' -sSf https://raw.githubusercontent.com/matter-labs/era-test-node/main/scripts/install.sh > install.sh
chmod +x install.sh
sudo ./install.sh
- name: Run test for ${{ matrix.tutorial }}
run: |
export TERM=xterm-256color
Expand Down
166 changes: 145 additions & 21 deletions content/tutorials/daily-spend-limit-account/10.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,32 +42,51 @@ with the smart contracts in this project.

1. Initiate a new project by running the command:

:test-action{actionId="initialize-project"}

```sh
npx zksync-cli create custom-spendlimit-tutorial --template hardhat_solidity
```

This creates a new ZKsync Era project called `custom-spendlimit-tutorial` with a with a few example contracts.
This creates a new ZKsync Era project called `custom-spendlimit-tutorial` with a few example contracts.

1. Navigate into the project directory:

:test-action{actionId="wait-for-init"}
:test-action{actionId="move-into-project"}

```sh
cd custom-spendlimit-tutorial
```

1. For the purposes of this tutorial, we don't need the example contracts related files. So, proceed by removing all the
files inside the `/contracts` and `/deploy` folders manually or by running the following commands:
:test-action{actionId="delete-template-files"}
```sh
rm -rf ./contracts/*
rm -rf ./deploy/*
```
1. Add the ZKsync and OpenZeppelin contract libraries:
```sh
yarn add -D @matterlabs/zksync-contracts @openzeppelin/[email protected]
:test-action{actionId="install-deps"}
::code-group
```bash [npm]
npm install -D @matterlabs/zksync-contracts @openzeppelin/[email protected] @matterlabs/[email protected]
```
```bash [yarn]
yarn add -D @matterlabs/zksync-contracts @openzeppelin/[email protected] @matterlabs/[email protected]
```
::
:test-action{actionId="wait-for-install"}
::callout{icon="i-heroicons-exclamation-triangle"}
This project does not use the latest version available of
`@openzeppelin/contracts`. Make sure you install the specific version mentioned above.
Expand All @@ -76,6 +95,8 @@ with the smart contracts in this project.
1. Include the `isSystem: true` setting in the `zksolc` section of the `hardhat.config.ts` configuration file to allow
interaction with system contracts:
:test-action{actionId="hardhat-config"}
```typescript [hardhat.config.ts]
import { HardhatUserConfig } from "hardhat/config";
Expand Down Expand Up @@ -117,6 +138,9 @@ with the smart contracts in this project.
export default config;
```

:test-action{actionId="start-local-node"}
:test-action{actionId="deploy-to-local-node"}

## Design

Now let’s dive into the design and implementation of the daily spending limit feature.
Expand Down Expand Up @@ -337,6 +361,12 @@ limit.available -= _amount;
1. In the folder `contracts`, add a file called `SpendLimit.sol`
:test-action{actionId="add-spend-limit-file"}
```sh
touch contracts/SpendLimit.sol
```
1. Copy/paste the complete code below.
::callout{icon="i-heroicons-exclamation-triangle"}
Expand All @@ -345,6 +375,9 @@ limit.available -= _amount;
forget to change the value before deploying the contract.
::
:test-action{actionId="open-spend-limit-code-panel"}
:test-action{actionId="spend-limit-contract"}
::drop-panel
::panel{label="SpendLimit.sol"}
Expand Down Expand Up @@ -481,6 +514,12 @@ The main difference is that our account has a single signer.
1. Create a file `Account.sol` in the `contracts` folder.
:test-action{actionId="account-contract-file"}
```sh
touch contracts/Account.sol
```
2. Copy/paste the code below.
The account implements the [IAccount](https://docs.zksync.io/build/developer-reference/account-abstraction/design#iaccount-interface) interface
Expand All @@ -491,6 +530,9 @@ Since we are building an account with signers, we should also implement
The `isValidSignature` method will take care of verifying the signature and making sure the extracted address matches
with the owner of the account.
:test-action{actionId="open-account-code-panel"}
:test-action{actionId="add-account-contract"}
::drop-panel
::panel{label="Account.sol"}
Expand Down Expand Up @@ -749,7 +791,17 @@ bytes from transaction calldata.
The `AAFactory.sol` contract is responsible for deploying instances of the `Account.sol` contract.
1. Create the `AAFactory.sol` file in the `contracts` folder and copy/paste the code below.
1. Create the `AAFactory.sol` file in the `contracts` folder.
:test-action{actionId="aa-factory-file"}
```sh
touch contracts/AAFactory.sol
```
1. Copy/paste the code below.
:test-action{actionId="aa-factory-contract"}
```solidity [AAFactory.sol]
// SPDX-License-Identifier: MIT
Expand Down Expand Up @@ -795,16 +847,35 @@ contract AAFactory {
1. Compile the contracts from the project root.
```sh
yarn hardhat compile
:test-action{actionId="compile"}
::code-group
```bash [npm]
npm run compile
```
```bash [yarn]
yarn compile
```
1. Create a file named `deploy/deployFactoryAccount.ts`. Then, copy and paste the following code into it. Remember to
::
1. Create a file named `deploy/deploy.ts`. Then, copy and paste the following code into it. Remember to
add your `DEPLOYER_PRIVATE_KEY` to the .env file.
:test-action{actionId="create-deploy-factory-script-file"}
```sh
touch deploy/deploy.ts
```
The script deploys the factory, creates a new smart contract account, and funds it with some ETH.
```typescript [deploy/deployFactoryAccount.ts]
:test-action{actionId="modify-env-file"}
:test-action{actionId="deploy-factory-script"}
```typescript [deploy/deploy.ts]
import { utils, Wallet, Provider } from "zksync-ethers";
import * as ethers from "ethers";
import { HardhatRuntimeEnvironment } from "hardhat/types";
Expand All @@ -818,7 +889,7 @@ contract AAFactory {
export default async function (hre: HardhatRuntimeEnvironment) {
// @ts-ignore target zkSyncSepoliaTestnet in config file which can be testnet or local
const provider = new Provider(hre.config.networks.zkSyncSepoliaTestnet.url);
const provider = new Provider(hre.network.config.url);
const wallet = new Wallet(DEPLOYER_PRIVATE_KEY, provider);
const deployer = new Deployer(hre, wallet);
const factoryArtifact = await deployer.loadArtifact("AAFactory");
Expand Down Expand Up @@ -864,10 +935,20 @@ contract AAFactory {
1. Run the script.
```sh
yarn hardhat deploy-zksync --script deployFactoryAccount.ts
:test-action{actionId="run-deploy-script"}
::code-group
```bash [npm]
npm run deploy
```
```bash [yarn]
yarn deploy
```
::
You should see the following:
```txt
Expand All @@ -886,12 +967,24 @@ address in order to track transactions and changes in the balance.
1. Create the file `setLimit.ts` in the `deploy` folder and copy/paste the example code below.
:test-action{actionId="wait-for-script"}
:test-action{actionId="create-set-limit-script"}
```sh
touch deploy/setLimit.ts
```
1. Replace `<DEPLOYED_ACCOUNT_ADDRESS>` and `<DEPLOYED_ACCOUNT_OWNER_PRIVATE_KEY>` with the output from the previous
section in your `.env` file.
:test-action{actionId="get-deployed-account-address"}
:test-action{actionId="get-private-key"}
To enable the daily spending limit, we execute the `setSpendingLimit` function with two parameters: token address and limit amount.
The token address is `ETH_ADDRESS` and the limit parameter is `0.0005` in the example below (and can be any amount).
:test-action{actionId="set-limit-script"}
```typescript [setLimit.ts]
import { utils, Wallet, Provider, Contract, EIP712Signer, types } from "zksync-ethers";
import * as ethers from "ethers";
Expand All @@ -908,7 +1001,7 @@ address in order to track transactions and changes in the balance.
export default async function (hre: HardhatRuntimeEnvironment) {
// @ts-ignore target zkSyncSepoliaTestnet in config file which can be testnet or local
const provider = new Provider(hre.config.networks.zkSyncSepoliaTestnet.url);
const provider = new Provider(hre.network.config.url);
const owner = new Wallet(DEPLOYED_ACCOUNT_OWNER_PRIVATE_KEY, provider);
Expand Down Expand Up @@ -956,10 +1049,20 @@ address in order to track transactions and changes in the balance.
1. Run the script.
```sh
:test-action{actionId="run-set-limit-script"}
::code-group
```bash [npx]
npx hardhat deploy-zksync --script setLimit.ts
```
```bash [yarn]
yarn hardhat deploy-zksync --script setLimit.ts
```
::
You should see the following output:
```text
Expand All @@ -977,10 +1080,19 @@ Let's test the `SpendLimit` contract works to make it refuse ETH transfers that
1. Create `transferETH.ts` and copy/paste the example code below, replacing the placeholder constants as before and
adding an account address for `RECEIVER_ACCOUNT`.
```typescript [transferETH.ts]
:test-action{actionId="add-receiver-account"}
:test-action{actionId="create-transfer-script"}
```sh
touch deploy/transferETH.ts
```
:test-action{actionId="add-transfer-script"}
```typescript [deploy/transferETH.ts]
import { utils, Wallet, Provider, Contract, EIP712Signer, types } from "zksync-ethers";
import * as ethers from "ethers";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import type { HardhatRuntimeEnvironment } from "hardhat/types";
// load env file
import dotenv from "dotenv";
Expand All @@ -994,7 +1106,7 @@ Let's test the `SpendLimit` contract works to make it refuse ETH transfers that
export default async function (hre: HardhatRuntimeEnvironment) {
// @ts-ignore target zkSyncSepoliaTestnet in config file which can be testnet or local
const provider = new Provider(hre.config.networks.zkSyncSepoliaTestnet.url);
const provider = new Provider(hre.network.config.url);
const owner = new Wallet(DEPLOYED_ACCOUNT_OWNER_PRIVATE_KEY, provider);
Expand Down Expand Up @@ -1034,11 +1146,13 @@ Let's test the `SpendLimit` contract works to make it refuse ETH transfers that
console.log("Available today: ", limitData.available.toString());
// L1 timestamp tends to be undefined in latest blocks. So it should find the latest L1 Batch first.
let l1BatchRange = await provider.getL1BatchBlockRange(await provider.getL1BatchNumber());
let l1TimeStamp = (await provider.getBlock(l1BatchRange[1])).l1BatchTimestamp;
if (hre.network.config.ethNetwork !== 'localhost') {
let l1BatchRange = await provider.getL1BatchBlockRange(await provider.getL1BatchNumber());
let l1TimeStamp = (await provider.getBlock(l1BatchRange[1])).l1BatchTimestamp;
console.log("L1 timestamp: ", l1TimeStamp);
console.log("Limit will reset on timestamp: ", limitData.resetTime.toString());
console.log('L1 timestamp: ', l1TimeStamp);
console.log('Limit will reset on timestamp: ', limitData.resetTime.toString());
}
// actually do the ETH transfer
console.log("Sending ETH transfer from smart contract account");
Expand All @@ -1064,10 +1178,20 @@ Let's test the `SpendLimit` contract works to make it refuse ETH transfers that
1. Run the script to attempt to make a transfer.
```shell
:test-action{actionId="run-transfer-script"}
::code-group
```bash [npx]
npx hardhat deploy-zksync --script transferETH.ts
```
```bash [yarn]
yarn hardhat deploy-zksync --script transferETH.ts
```
::
You should see an error message with the following content so we know it failed because the amount exceeded the limit.
```shell
Expand Down
4 changes: 4 additions & 0 deletions tests/configs/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { steps as erc20PaymasterSteps } from './erc20-paymaster';
import { steps as howToTestContractsSteps } from './how-to-test-contracts';
import { steps as dailySpendLimitSteps } from './daily-spend-limit';

export function getConfig(tutorialName: string) {
let steps;
Expand All @@ -10,6 +11,9 @@ export function getConfig(tutorialName: string) {
case 'how-to-test-contracts':
steps = howToTestContractsSteps;
break;
case 'daily-spend-limit':
steps = dailySpendLimitSteps;
break;
default:
break;
}
Expand Down
Loading

0 comments on commit 3398dae

Please sign in to comment.