Skip to content

Implementation wallet_grantPermissions based on Appkit spec #723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from 19 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
26 changes: 22 additions & 4 deletions advanced/wallets/react-wallet-v2/src/data/EIP5792Data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ export const EIP5792_METHODS = {
}

// capability names as string literals
export type CapabilityName = 'atomicBatch' | 'paymasterService' | 'sessionKey'
export type CapabilityName = 'atomicBatch' | 'paymasterService' | 'permissions'
// Capability type where each key is a capability name and value has `supported` field
export type Capabilities = {
[K in CapabilityName]?: {
supported: boolean
[key: string]: any
}
}
// GetCapabilitiesResult type using mapped types
Expand Down Expand Up @@ -65,9 +66,26 @@ export const supportedEIP5792CapabilitiesForSCA: GetCapabilitiesResult = {
paymasterService: {
supported: true
},
// sessionKey: {
// supported: true,
// },
permissions: {
supported: true,
signerTypes: ['keys'],
permissionTypes: ['contract-call'],
policyTypes: []
},
atomicBatch: {
supported: true
}
},
'0x14a34': {
paymasterService: {
supported: true
},
permissions: {
supported: true,
signerTypes: ['keys'],
permissionTypes: ['contract-call'],
policyTypes: []
},
atomicBatch: {
supported: true
}
Expand Down
134 changes: 74 additions & 60 deletions advanced/wallets/react-wallet-v2/src/data/EIP7715Data.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,96 @@
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 WalletSigner = {
type: 'wallet'
data: {}
}

// 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.
export type KeySigner = {
type: 'key'
data: {
id: string
}
export type Signer = MultiKeySigner
export type KeyType = 'secp256k1' | 'secp256r1'
// 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
}

// 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}`
}
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
}

export enum SignerType {
EOA,
PASSKEY
// 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 ContractCallPermission = {
type: 'contract-call'
data: {
address: `0x${string}`
abi: Record<string, unknown>[]
functions: FunctionPermission[]
}
}

// Union type for all possible permissions
export type Permission = ContractCallPermission

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}`
}
}
export type PermissionType =
| 'native-token-transfer'
| 'erc20-token-transfer'
| 'erc721-token-transfer'
| 'erc1155-token-transfer'
| {
custom: any
}
export type PolicyType =
| 'gas-limit'
| 'call-limit'
| 'rate-limit'
| 'spent-limit'
| 'value-limit'
| 'time-frame'
| 'uni-action'
| 'simpler-signer'
| {
custom: any
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,24 @@ 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,45 +73,39 @@ 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,
account: getAccount({
address: this.client.account.address,
type: 'safe'
}),
permissions: [...grantPermissionsRequestParameters.permissions] as unknown as Permission[],
expiry: grantPermissionsRequestParameters.expiry,
signer: grantPermissionsRequestParameters.signer as MultiKeySigner
})
} catch (error) {
console.error(`Error getting permission context: ${error}`)
throw error
}

const permissionContext = await getContext(walletClient, {
account: getAccount({
address: this.client.account.address,
type: 'safe'
}),
grantPermissionsRequest: grantPermissionsRequestParameters
})

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