Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
11 changes: 11 additions & 0 deletions advanced/wallets/react-wallet-v2/src/data/EIP5792Data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,16 @@ export const supportedEIP5792CapabilitiesForSCA: GetCapabilitiesResult = {
atomicBatch: {
supported: true
}
},
'0x14a34': {
paymasterService: {
supported: true
},
// sessionKey: {
// supported: true,
// },
atomicBatch: {
supported: true
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we set up capabilities in this way?

}
}
206 changes: 162 additions & 44 deletions advanced/wallets/react-wallet-v2/src/data/EIP7715Data.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,200 @@
import { Address } from 'viem'

/**
* EIP7715Method
*/
export const EIP7715_METHOD = {
WALLET_GRANT_PERMISSIONS: 'wallet_grantPermissions'
}

// `data` is not necessary for this signer type as the wallet is both the signer and grantor of these permissions
export type Signer = WalletSigner | KeySigner | MultiKeySigner | AccountSigner
export type KeyType = 'secp256k1' | 'secp256r1' | 'ed25519' | 'schonorr'
// The types of keys that are supported for the following `key` and `keys` signer types.
export enum SignerKeyType {
SECP256K1 = 0, // EOA - k1
SECP256R1 = 1, // Passkey - r1
ED25519 = 3,
SCHNORR = 4
}
/*
* A wallet is the signer for these permissions
* `data` is not necessary for this signer type as the wallet is both the signer and grantor of these permissions
*/
export type WalletSigner = {
type: 'wallet'
data: {}
data: Record<string, unknown>
}

// A signer representing a single key.
// `id` is a did:key identifier and can therefore represent both Secp256k1 or Secp256r1 keys, among other key types.
/*
* A signer representing a single key.
* "Key" types are explicitly secp256r1 (p256) or secp256k1, and the public keys are hex-encoded.
*/
export type KeySigner = {
type: 'key'
data: {
id: string
type: KeyType
publicKey: `0x${string}`
}
}

// A signer representing a multisig signer.
// Each element of `ids` is a did:key identifier just like the `key` signer.
/*
* A signer representing a multisig signer.
* Each element of `publicKeys` are all explicitly the same `KeyType`, and the public keys are hex-encoded.
*/
export type MultiKeySigner = {
type: 'keys'
data: {
ids: string[]
address?: Address
keys: {
type: KeyType
publicKey: `0x${string}`
}[]
}
}

// An account that can be granted with permissions as in ERC-7710.
export type AccountSigner = {
type: 'account'
data: {
id: `0x${string}`
address: `0x${string}`
}
}

export enum SignerType {
EOA,
PASSKEY
export type Policy = {
type: string
data: Record<string, unknown>
}
// Enum for parameter operators
enum ParamOperator {
EQUAL = 'EQUAL',
GREATER_THAN = 'GREATER_THAN',
LESS_THAN = 'LESS_THAN'
// Add other operators as needed
}

// Enum for operation types
enum Operation {
Call = 'Call',
DelegateCall = 'DelegateCall'
}

export type Signer = {
type: SignerType
data: string
// Type for a single argument condition
type ArgumentCondition = {
operator: ParamOperator
value: any // You might want to be more specific based on your use case
}

export type Permission = {
type: PermissionType
policies: Policy[]
required: boolean
data: any
// Type for a single function permission
type FunctionPermission = {
functionName: string // Function name
args: ArgumentCondition[] // An array of conditions, each corresponding to an argument for the function
valueLimit: bigint // Maximum value that can be transferred for this specific function call
operation?: Operation // (optional) whether this is a call or a delegatecall. Defaults to call
}
export type Policy = {
type: PolicyType
data: any
}
export type PermissionType =
| 'native-token-transfer'
| 'erc20-token-transfer'
| 'erc721-token-transfer'
| 'erc1155-token-transfer'
| {
custom: any
export type ContractCallPermission = {
type: 'contract-call'
data: {
address: `0x${string}`
abi: Record<string, unknown>[]
functions: FunctionPermission[]
}
}
// Native token transfer, e.g. ETH on Ethereum
export type NativeTokenTransferPermission = {
type: 'native-token-transfer'
data: {
allowance: `0x${string}` // hex value
}
}

// ERC20 token transfer
export type ERC20TokenTransferPermission = {
type: 'erc20-token-transfer'
data: {
address: `0x${string}` // erc20 contract
allowance: `0x${string}` // hex value
}
}

// ERC721 token transfer
export type ERC721TokenTransferPermission = {
type: 'erc721-token-transfer'
data: {
address: `0x${string}` // erc721 contract
tokenIds: `0x${string}`[] // hex value array
}
}

// ERC1155 token transfer
export type ERC1155TokenTransferPermission = {
type: 'erc1155-token-transfer'
data: {
address: `0x${string}` // erc1155 contract
allowances: {
[tokenId: string]: `0x${string}` // hex value
}
export type PolicyType =
| 'gas-limit'
| 'call-limit'
| 'rate-limit'
| 'spent-limit'
| 'value-limit'
| 'time-frame'
| 'uni-action'
| 'simpler-signer'
}
}

// The maximum gas limit spent in the session in total
export type GasLimitPermission = {
type: 'gas-limit'
data: {
limit: `0x${string}` // hex value
}
}

// The number of calls the session can make in total
export type CallLimitPermission = {
type: 'call-limit'
data: {
count: `0x${string}` // hex value
}
}

// The number of calls the session can make during each interval
export type RateLimitPermission = {
type: 'rate-limit'
data: {
count: `0x${string}` //hex value: the number of times during each interval
interval: `0x${string}` //hex value in seconds
}
}

// Union type for all possible permissions
export type Permission =
| ContractCallPermission
| NativeTokenTransferPermission
| ERC20TokenTransferPermission
| ERC721TokenTransferPermission
| ERC1155TokenTransferPermission
| GasLimitPermission
| CallLimitPermission
| RateLimitPermission
| {
custom: any
type: string
data: Record<string, unknown>
}

export type WalletGrantPermissionsRequest = {
chainId: `0x${string}`
address?: `0x${string}`
expiry: number
signer: Signer
permissions: Permission[]
policies: {
type: string
data: Record<string, unknown>
}[]
}

export type WalletGrantPermissionsResponse = WalletGrantPermissionsRequest & {
context: `0x${string}`
accountMeta?: {
factory: `0x${string}`
factoryData: `0x${string}`
}
signerMeta?: {
// 7679 userOp building
userOpBuilder?: `0x${string}`
// 7710 delegation
delegationManager?: `0x${string}`
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@ import { EntryPoint } from 'permissionless/types/entrypoint'
import {
Address,
Hex,
WalletGrantPermissionsParameters,
createWalletClient,
encodeFunctionData,
getAddress,
http,
parseAbi,
type WalletGrantPermissionsReturnType
toHex
} from 'viem'
import { MultiKeySigner } from 'viem/_types/experimental/erc7715/types/signer'
import { ModuleType } from 'permissionless/actions/erc7579'
import {
MOCK_VALIDATOR_ADDRESSES,
TRUSTED_SMART_SESSIONS_ATTERSTER_ADDRESS
} from './builders/SmartSessionUtil'
import { Permission } from '@/data/EIP7715Data'
import { getSmartSessionContext } from './builders/ContextBuilderUtil'
import {
Permission,
WalletGrantPermissionsRequest,
WalletGrantPermissionsResponse
} from '@/data/EIP7715Data'
import { getContext } from './builders/ContextBuilderUtil'
import { readContract } from 'viem/actions'
import { Execution, Module } from '@rhinestone/module-sdk'

Expand Down Expand Up @@ -71,30 +74,27 @@ export class SafeSmartAccountLib extends SmartAccountLib {

/* 7715 method */
async grantPermissions(
grantPermissionsRequestParameters: WalletGrantPermissionsParameters
): Promise<WalletGrantPermissionsReturnType> {
grantPermissionsRequestParameters: WalletGrantPermissionsRequest
): Promise<WalletGrantPermissionsResponse> {
if (!this.client?.account) {
throw new Error('Client not initialized')
}
await this.ensureAccountReadyForGrantPermissions()

const walletClient = createWalletClient({
chain: this.chain,
account: this.client.account,
account: this.signer,
transport: http()
})
console.log('walletClient chainId:', walletClient.chain.id)
let permissionContext = '0x'
try {
permissionContext = await getSmartSessionContext({
walletClient,
permissionContext = await getContext(walletClient, {
account: getAccount({
address: this.client.account.address,
type: 'safe'
}),
permissions: [...grantPermissionsRequestParameters.permissions] as unknown as Permission[],
expiry: grantPermissionsRequestParameters.expiry,
signer: grantPermissionsRequestParameters.signer as MultiKeySigner
grantPermissionsRequest: grantPermissionsRequestParameters
})
} catch (error) {
console.error(`Error getting permission context: ${error}`)
Expand All @@ -103,13 +103,15 @@ export class SafeSmartAccountLib extends SmartAccountLib {

console.log(`Returning the permissions request`)
return {
permissionsContext: permissionContext,
grantedPermissions: grantPermissionsRequestParameters.permissions,
expiry: grantPermissionsRequestParameters.expiry,
signerData: {
submitToAddress: this.client.account.address
}
} as WalletGrantPermissionsReturnType
...grantPermissionsRequestParameters,
context: permissionContext as Hex,
chainId: toHex(this.chain.id),
accountMeta: {
factory: (await this.client.account.getFactory()) || '0x',
factoryData: (await this.client.account.getFactoryData()) || '0x'
},
expiry: grantPermissionsRequestParameters.expiry
}
}

/**
Expand Down
Loading