Skip to content

Commit

Permalink
fix: remove hardcoded gas, fix estimation, sdk improvements (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
JackHamer09 authored Nov 20, 2024
1 parent 087b3ac commit d90a94d
Show file tree
Hide file tree
Showing 19 changed files with 416 additions and 77 deletions.
2 changes: 1 addition & 1 deletion cspell-config/cspell-misc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Nuxt
nuxtjs
testid
vueuse
ethereum

// examples/bank-demo
ctap
Expand All @@ -25,4 +26,3 @@ usdc
// examples/nft-quest
Fren
fren
trxn
8 changes: 5 additions & 3 deletions examples/demo-app/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</template>

<script lang="ts" setup>
import { disconnect, getBalance, watchAccount, sendTransaction, createConfig, connect, reconnect, type GetBalanceReturnType } from "@wagmi/core";
import { disconnect, getBalance, watchAccount, sendTransaction, createConfig, connect, reconnect, waitForTransactionReceipt, type GetBalanceReturnType } from "@wagmi/core";
import { zksyncSsoConnector } from "zksync-sso/connector";
import { zksyncInMemoryNode } from "@wagmi/core/chains";
import { createWalletClient, http, parseEther, type Address } from "viem";
Expand Down Expand Up @@ -142,15 +142,17 @@ const sendTokens = async () => {
errorMessage.value = "";
isSendingEth.value = true;
try {
await sendTransaction(wagmiConfig, {
const transactionHash = await sendTransaction(wagmiConfig, {
to: testTransferTarget,
value: parseEther("0.1"),
gas: 100_000_000n,
});
balance.value = await getBalance(wagmiConfig, {
address: address.value,
});
const receipt = await waitForTransactionReceipt(wagmiConfig, { hash: transactionHash });
if (receipt.status === "reverted") throw new Error("Transaction reverted");
} catch (error) {
// eslint-disable-next-line no-console
console.error("Transaction failed:", error);
Expand Down
17 changes: 2 additions & 15 deletions examples/nft-quest/composables/useMintNft.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { estimateGas, waitForTransactionReceipt, writeContract } from "@wagmi/core";
import { type Address, encodeFunctionData } from "viem";
import { waitForTransactionReceipt, writeContract } from "@wagmi/core";
import type { Address } from "viem";
import { getGeneralPaymasterInput } from "viem/zksync";

export const useMintNft = async (_address: MaybeRef<Address>) => {
Expand All @@ -10,24 +10,11 @@ export const useMintNft = async (_address: MaybeRef<Address>) => {
const { wagmiConfig } = storeToRefs(useConnectorStore());

const mintingForAddress = address.value;
const data = encodeFunctionData({
abi: nftAbi,
functionName: "mint",
args: [address.value],
});

const estimatedGas = await estimateGas(wagmiConfig.value, {
to: runtimeConfig.public.contracts.nft as Address,
chainId: runtimeConfig.public.chain.id,
data,
});

const transactionHash = await writeContract(wagmiConfig.value, {
address: runtimeConfig.public.contracts.nft as Address,
abi: nftAbi,
functionName: "mint",
args: [mintingForAddress],
gas: estimatedGas + (estimatedGas / 100n * 25n), // gas may be underestimated
paymaster: runtimeConfig.public.contracts.paymaster as Address,
paymasterInput: getGeneralPaymasterInput({ innerInput: "0x" }),
});
Expand Down
10 changes: 10 additions & 0 deletions examples/nft-quest/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,14 @@ export default defineNuxtConfig({
},
},
},

vite: {
css: {
preprocessorOptions: {
scss: {
api: "modern", // Fix warning: "The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0"
},
},
},
},
});
10 changes: 10 additions & 0 deletions packages/auth-server/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,14 @@ export default defineNuxtConfig({
chainId: parseInt(process.env.NUXT_PUBLIC_DEFAULT_CHAIN_ID || "") || zksyncInMemoryNode.id,
},
},

vite: {
css: {
preprocessorOptions: {
scss: {
api: "modern", // Fix warning: "The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0"
},
},
},
},
});
3 changes: 0 additions & 3 deletions packages/contracts/test/PasskeyModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,6 @@ async function rawVerify(
const hashedData = await toHash(concat([authDataBuffer, clientDataHash]));
const rs = unwrapEC2Signature(toBuffer(b64SignedChallange));
const publicKeys = await getPublicKey(publicKeyEs256Bytes);
/* console.log("externalSignature", ethers.hexlify(hashedData));
console.log("rs", ethers.hexlify(rs[0]), ethers.hexlify(rs[1]));
console.log("pubkey xy", publicKeys); */
return await passkeyValidator.rawVerify(hashedData, rs, publicKeys);
}

Expand Down
1 change: 0 additions & 1 deletion packages/contracts/test/SessionKeyTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,6 @@ describe("SessionKeyModule tests", function () {
await tester.sendTxSuccess({
to: sessionTarget,
value: parseEther("0.01"),
gasLimit: 10_000_000n,
});
expect(await provider.getBalance(sessionTarget))
.to.equal(parseEther("0.01"), "session target should have received the funds");
Expand Down
108 changes: 106 additions & 2 deletions packages/sdk/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,107 @@
# zksync-sso
# zksync-sso SDK

ZKsync SSO SDK
[![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE-MIT)
[![CI](https://github.com/matter-labs/zksync-account-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/matter-labs/zksync-account-sdk/actions/workflows/ci.yml)

A user & developer friendly modular smart account implementation on ZKsync;
simplifying user authentication, session management, and transaction processing.

## Features and Goals

<!-- prettier-ignore -->
> [!CAUTION]
> ZKsync SSO is under active development and is not yet feature
> complete. Use it to improve your development applications and tooling. Please
> do not use it in production environments.
- 🧩 Modular smart accounts based on
[ERC-7579](https://eips.ethereum.org/EIPS/eip-7579#modules)
- 🔑 Passkey authentication (no seed phrases)
- ⏰ Sessions w/ easy configuration and management
- 💰 Integrated paymaster support
- ❤️‍🩹 Account recovery _(Coming Soon)_
- 💻 Simple SDKs : JavaScript, iOS/Android _(Coming Soon)_
- 🤝 Open-source authentication server
- 🎓 Examples to get started quickly

## Getting started

Install the ZKsync SSO SDK package:

```sh
npm i zksync-sso
```

Add ZKsync SSO connector to your app (using `wagmi`):

```ts
import { zksyncSepoliaTestnet } from "viem/chains";
import { createConfig, connect } from "@wagmi/core";
import { zksyncSsoConnector } from "zksync-sso/connector";

const ssoConnector = zksyncSsoConnector({
// Optional session configuration,
// if omitted user will have to sign every transaction via Auth Server
session: {
// Allow up to 0.1 ETH to be spend in gas fees
feeLimit: parseEther("0.1"),

transfers: [
// Allow ETH transfers of up to 0.1 ETH to specific address
{
to: "0x188bd99cd7D4d78d4E605Aeea12C17B32CC3135A",
valueLimit: parseEther("0.1"),
},

// Allow ETH transfers to specific address with a limit of 0.1 ETH per hour
// until the session expires
{
to: "0x188bd99cd7D4d78d4E605Aeea12C17B32CC3135A",
valueLimit: {
limit: parseEther("0.1"),
period: BigInt(60 * 60), // 1 hour in seconds
},
},
],

// Allow calling specific smart contracts (e.g. ERC20 transfer):
contractCalls: [
{
address: "0xa1cf087DB965Ab02Fb3CFaCe1f5c63935815f044",
function: "transfer(address,uint256)",

// Optional call constraints (unconstrained otherwise):
constraints: [

// Only allow transfers to this address
{
index: 0,
condition: "Equal",
refValue: pad("0x6cC8cf7f6b488C58AA909B77E6e65c631c204784"),
},

// Transfer up to 0.2 tokens
{
index: 1,
limit: parseUnits("0.2", TOKEN.decimals), // Unlimited if omitted
},
],
},
],
},
});

const wagmiConfig = createConfig({
connectors: [ssoConnector],
..., // your wagmi config https://wagmi.sh/core/api/createConfig
});

const connectWithSSO = () => {
connect(wagmiConfig, {
connector: ssoConnector,
chainId: zksyncSepoliaTestnet.id, // or another chain id that has SSO support
});
};
```

[Find more information here in our docs.](https://docs.zksync.io/build/zksync-sso)
8 changes: 2 additions & 6 deletions packages/sdk/prepare-package.mjs
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import { promises as fs } from "fs";
import path from "path";

// Define the path to the package.json
const packageJsonPath = path.resolve("./package.json");
console.log(packageJsonPath);

// Get the version from environment variables
const version = process.env.INPUT_VERSION;
if (!version) {
console.error("Error: INPUT_VERSION is required.");
process.exit(1);
}

const packageJsonPath = path.resolve("./package.json");

async function preparePackageJson() {
try {
// Read the existing package.json
const packageJsonData = await fs.readFile(packageJsonPath, "utf8");
const packageJson = JSON.parse(packageJsonData);

Expand Down
10 changes: 10 additions & 0 deletions packages/sdk/src/client-auth-server/Signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Account = {
interface SignerInterface {
accounts: Address[];
chain: Chain;
getClient(parameters?: { chainId?: number }): ZksyncSsoSessionClient;
handshake(): Promise<Address[]>;
request<TMethod extends Method>(request: RequestArguments<TMethod>): Promise<ExtractReturnType<TMethod>>;
disconnect: () => Promise<void>;
Expand Down Expand Up @@ -83,6 +84,15 @@ export class Signer implements SignerInterface {
}
}

getClient(parameters?: { chainId?: number }) {
const chainId = parameters?.chainId || this.chain.id;
const chain = this.chains.find((e) => e.id === chainId);
if (!chain) throw new Error(`Chain with id ${chainId} is not supported`);

if (!this.walletClient) throw new Error("Wallet client is not created");
return this.walletClient;
}

private get account(): Account | null {
const account = this._account.get();
if (!account) return null;
Expand Down
4 changes: 4 additions & 0 deletions packages/sdk/src/client-auth-server/WalletProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export class WalletProvider extends EventEmitter implements ProviderInterface {
return this.signer.accounts.length > 0;
}

public getClient(parameters?: { chainId?: number }) {
return this.signer.getClient(parameters);
}

public async request<M extends Method>(request: RequestArguments<M>): Promise<ExtractReturnType<M>> {
try {
switch (request.method) {
Expand Down
2 changes: 2 additions & 0 deletions packages/sdk/src/client-auth-server/interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EventEmitter } from "eventemitter3";
import type { RpcSchema as RpcSchemaGeneric } from "viem";

import type { ZksyncSsoSessionClient } from "../client/index.js";
import type { ExtractParams, ExtractReturnType, Method, RpcSchema } from "./rpc.js";

export interface RequestArguments<M extends Method<TSchema>, TSchema extends RpcSchemaGeneric = RpcSchema> {
Expand All @@ -26,6 +27,7 @@ interface ProviderConnectInfo {
export interface ProviderInterface extends EventEmitter {
request<M extends Method>(args: RequestArguments<M>): Promise<ExtractReturnType<M>>;
disconnect(): Promise<void>;
getClient(parameters?: { chainId?: number }): Promise<ZksyncSsoSessionClient> | ZksyncSsoSessionClient;
on(event: "connect", listener: (info: ProviderConnectInfo) => void): this;
on(event: "disconnect", listener: (error: ProviderRpcError) => void): this;
on(event: "chainChanged", listener: (chainId: string) => void): this;
Expand Down
Loading

0 comments on commit d90a94d

Please sign in to comment.