Skip to content

Commit

Permalink
Merge branch 'main' into patch-2
Browse files Browse the repository at this point in the history
  • Loading branch information
nickster4400 authored Dec 2, 2024
2 parents fffd70e + 1b37fef commit f2b30be
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 154 deletions.
8 changes: 5 additions & 3 deletions content/00.build/30.zksync-sso/00.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ See [Getting Started](/build/zksync-sso/getting-started) to learn how easy it is
Need help? Join our [GitHub Discussions](%%zk_git_repo_zksync-developers%%/discussions/)
to ask questions, share your experiences, and connect with the ZKsync community.

<!---
## Source Code

The [ZKsync SSO Project](https://github.com/matter-labs/zksync-sso)
The [ZKsync SSO SDK](https://github.com/matter-labs/zksync-sso)
is open-source and available on GitHub under the MIT License.
Feel free to contribute, report issues, or suggest new features to help us improve the tool for everyone.--->
Feel free to contribute, report issues, or suggest new features to help us improve the tool for everyone.

The [ZKsync SSO Smart Contracts](https://github.com/matter-labs/zksync-sso-clave-contracts)
is a fork of [Clave](https://github.com/getclave/clave-contracts) smart contracts, open-source and available on GitHub under the GNU License.
194 changes: 91 additions & 103 deletions content/00.build/30.zksync-sso/25.interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ This provides some technical information on how these components can be used.
### Auth Server

The entrypoint to the SDK is via the ``zksyncSsoConnector`` to connect to the *Auth Server*.
This takes the auth domain, session, and basic app information (name, icon). The fully expanded type looks like:
This takes the session config, basic app information (name, icon), and Auth Server URL. The fully expanded type looks like:

<!-- // cspell: disable -->

```ts
/**
* @member metadata - Defaults to page title and website favicon
* @member session - Session configuration preferences
* @member authServerUrl - URL of the Auth Server
*/
type ZksyncSsoConnectorOptions = {
metadata?: {
name: string,
icon?: string,
},
session?: {
expiresAt?: bigint | Date;
feeLimit?: Limit,
transfers?: TransferPolicy[];
contractCalls?: CallPolicy[];
},
session?: SessionPreferences,
authServerUrl?: string;
};
```
Expand All @@ -36,148 +37,135 @@ making the ZKsync SSO account nearly indistinguishable from any other standard w

## Sessions

Sessions' combination of function selector and multiple layers of limits makes them effective security measures against unexpected behavior,
but they must be configured correctly in order to be most specific to the expected use cases.
The session interface is also included with the account and passkey creation interfaces so sessions can be created
during any important user interactions.

### Limit types

Limits are tracked either for a lifetime or a specific period on-chain.
Not all sessions or constraints are required to have limits, but it's the only way to prevent mis-use.
The limit amount provided is always an upper limit, where exceeding it for the value in question
causes the limit to invalidate the transaction.
The value being limited depends on the limit's use,
the two places where limits are applied are for transfer value policies,
which limit the amount in the *value* of the transaction directly.
The other limit is the dynamic constraint,
where the value is specified via a byte offset from the start of the transaction data.

*(Limit period validation is currently WORK-IN-PROGRESS,
period limits are currently skipped entirely)*
A single lifetime limit per value isn't expressive enough to support great use cases like subscriptions,
so the limits also support the ability to reset the limit on a fixed period basis.
If provided as part of the limit,
the block.timestamp divisor for the limit to be enforced
(eg: 60 for a minute, 86400 for a day, 604800 for a week, unset for lifetime).
Sessions allow only those transactions that are explicitly allowed by the session configuration.

```ts
/**
* @member expiresAt - Expiry time for the session (e.g. "8 hours"). Defaults are set by the Auth Server (currently 1 day).
* @member feeLimit - Maximum amount of ETH that can be spent on gas fees
* @member transfers - ETH transfer transaction policies
* @member contractCalls - Contract call transaction policies
*/
type SessionPreferences = {
expiresAt?: string | bigint | Date;
feeLimit?: Limit,
transfers?: TransferPolicy[];
contractCalls?: CallPolicy[];
}
```
### Limits
#### Limit Types
- **Unlimited**: No limit is enforced
- **Lifetime**: Limit is enforced over the lifetime of the session
- **Allowance**: Limit is enforced over a specific period of time and is reset every `period` seconds (based on the `block.timestamp`)
::callout{icon="i-heroicons-exclamation-triangle" color="amber"}
`Allowance` limits are currently WORK-IN-PROGRESS. Transaction validation will fail if it relies on this limit type.
::
```ts
type LimitType =
"Unlimited" |
"Lifetime" |
"Allowance";
```

#### Limit Use Cases

Limits are used in two places:

- **Value Policies**: Limits the amount of ETH that can be transferred
- **Function Argument Constraints**: Limits the amount that can be passed to a contract function (argument is parsed as `uint256`)

```ts
/**
* Limit is the value tracked either for a lifetime or a period on-chain
* @member limitType - Used to validate limit & period values (unlimited has no limit, lifetime has no period, allowance has both!)
* @member limit - The limit is exceeded if the tracked value is greater than this over the provided period
* @member period - The block.timestamp divisor for the limit to be enforced (eg: 60 for a minute, 86400 for a day, 604800 for a week, unset for lifetime)
* @member limitType - Type of limit to enforce
* @member limit - The limit is exceeded if the tracked value is greater than set value over the provided period (if applicable)
* @member period - Resets limit every `period` seconds. The block.timestamp divisor for the limit to be enforced (eg: "60 minutes", "24 hours")
*/
type Limit =
bigint | // Resolves to "lifetime" limit
{ limit: bigint; period?: bigint } | // If period is set resolves to "allowance" limit, otherwise "lifetime"
{ limit: bigint; period?: string | bigint } | // If period is set resolves to "allowance" limit, otherwise "lifetime"
{ limitType: "lifetime"; limit: bigint } |
{ limitType: "unlimited" } |
{ limitType: "allowance"; limit: bigint; period: bigint };
{ limitType: "allowance"; limit: bigint; period: string | bigint };
```

### Transfer Policy

This is the simplest policy because there's the least amount of data to constrain.
They are applied at the contract (address) level,
so they can be great for preventing transfers with specific contracts or EOAs.
They are only applied when the transaction data is less than 4 bytes and only look at the transaction value field.

They can also apply a max value that's independent of the value limit.

Only one transfer policy is available per targeted contract (per account),
so they don't stack it overwrites if you apply a new policy to the same address.
Allowed ETH transfers (non-contract calls).

```ts
/**
* Simplified CallPolicy for transactions with less than 4 bytes of data
* @member target - Only one policy per target per session (unique mapping from contractCalls)
* @member maxValuePerUse - Will reject transaction if value is set above this amount
* @member valueLimit - Validated from value
* @member to - Allowed recipient address
* @member maxValuePerUse - Per transaction ETH limit
* @member valueLimit - Cumulative ETH limit
*/
type TransferPolicy = {
target: Address;
to: Address;
maxValuePerUse?: bigint;
valueLimit?: Limit;
};
```

### Call Policy
### Contract Call Policy

This is a much more dynamic policy than a transfer policy that can be specified at
the contract (address) level, as well as the specific function selector.
Again,
this policy is unique per target + selector,
so only one policy is applied per transaction.
Allowed contract calls.

```ts
/**
* CallPolicy is a policy for a specific contract (address/function) call.
* @member target - Only one policy per target per session (unique mapping)
* @member function - Solidity function signature (e.g. "transfer(address,uint256)")
* @member selector - Solidity function selector (the selector directly), also unique mapping with target
* @member maxValuePerUse - Will reject transaction if value is set above this amount (for transfer or call)
* @member valueLimit - If not set, then zero. If a number or a limit without a period, converts to a lifetime value. Also rejects transactions that have cumulative value greater than what's set here
* @member constraints - Array of conditions with specific limits for performing range and logic checks (e.g. 5 > x >= 30) on the transaction data (not value!)
* @member address - Contract address
* @member abi - Contract ABI
* @member functionName - Function name to call
* @member maxValuePerUse - Per transaction limit for the ETH value included in the transaction
* @member valueLimit - Cumulative limit for the ETH value of the transaction
* @member constraints - List of constraints to apply to the function arguments; unconstrained if not set
*/
type CallPolicy = {
target: Address;
valueLimit?: Limit;
address: Address;
abi: Abi;
functionName: string;
maxValuePerUse?: bigint;
function?: string;
selector?: Hash; // if function is not provided
valueLimit?: PartialLimit;
constraints?: Constraint[];
};
```

The powerful part of call policies are the list of constraints,
which can apply multiple conditional logic statements to individual values within the transaction data.
These conditions are checked in order,
so any condition failure (not unconstrained) will cause the validation to fail.
The index of the constraint provides the offset from the start of the transaction data
so depending on the data layout any static data can be validated.
Each condition track it's own limit,
making this approach flexible enough to support adding limits to individual arguments to
contract function calls.

```typescript
/**
* Common logic operators to used combine multiple constraints
*/
type ConstraintCondition =
"Unconstrained" |
"Equal" |
"Greater" |
"Less" |
"GreaterEqual" |
"LessEqual" |
"NotEqual";
### Constraints

::callout{icon="i-heroicons-light-bulb"}
If constraints are not set, the function allows any argument values.
::

Constraints enforce limits on the arguments of a contract call.
They can be stacked and are checked in order.
Any constraint failure will cause the transaction validation to fail.

```ts
/**
* Constraint allows performing logic checks on any binary word (bytes32) in the transaction.
* This can let you set spend limits against functions on specific contracts
* @member index - The location of the start of the data in the transaction. This is not the index of the constraint within the containing array!
* @member index - Index of the argument to check
* @member value - Value of the argument to compare against
* @member condition - The kind of check to perform (None, =, >, <, >=, <=, !=)
* @member refValue - The value to compare against (as bytes32)
* @member limit - The limit to enforce on the parsed value (from index)
* @member limit - Limit to enforce on the parsed value
*/
type Constraint = {
index: number;
value?: unknown;
condition?: ConstraintCondition;
refValue?: Hash;
limit?: Limit;
};

```

This design was inspired by [Smart Sessions](https://github.com/erc7579/smartsessions/blob/main/contracts/external/policies/UniActionPolicy.sol).
For those looking to configure sessions to restrict access to functions,
understanding the storage layout of the sessions will help when setting the function selector and limits.

Again, the split between call policy and transfer policy means that only *one* policy will be enforced per account transaction,
and it will be determined based on the amount of data provided.
```ts
type ConstraintCondition =
"Unconstrained" | // No constraint
"Equal" | // =
"Greater" | // >
"Less" | // <
"GreaterEqual" | // >=
"LessEqual" | // <=
"NotEqual"; // !=
```
Loading

0 comments on commit f2b30be

Please sign in to comment.