Skip to content

Commit

Permalink
feat enable erc7715 support (#581)
Browse files Browse the repository at this point in the history
* added ERC7715 data (method rpc and types)

* added ERC7715 method in supported namespace

* added support for grantPermissions on safe7579 wallet

* deps update

* react_dapp_v2: added support for grantPermissions 

* setting timeout for waitForUserOperationReceipt

* Added decodeDIDToSECP256k1PublicKey helper method

* handle did:key in grantPermissions

* change default kernel impl to entrypoint v6
  • Loading branch information
KannuSingh authored Jun 26, 2024
1 parent 02d2d06 commit 6a94d64
Show file tree
Hide file tree
Showing 18 changed files with 441 additions and 101 deletions.
46 changes: 46 additions & 0 deletions advanced/dapps/react-dapp-v2/src/constants/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,52 @@ export type GetCallsResult = {
transactionHash: `0x${string}`;
}[];
};
/**
* EIP7715
*/
export const DEFAULT_EIP7715_METHODS = {
WALLET_GRANT_PERMISSIONS: "wallet_grantPermissions",
} as const;
export type WalletGrantPermissionsParameters = {
signer?:
| {
type: string;
data?: unknown | undefined;
}
| undefined;
permissions: readonly {
data: unknown;
policies: readonly {
data: unknown;
type: string;
}[];
required?: boolean | undefined;
type: string;
}[];
expiry: number;
};

export type WalletGrantPermissionsReturnType = {
expiry: number;
factory?: `0x${string}` | undefined;
factoryData?: string | undefined;
grantedPermissions: readonly {
data: unknown;
policies: readonly {
data: unknown;
type: string;
}[];
required?: boolean | undefined;
type: string;
}[];
permissionsContext: string;
signerData?:
| {
userOpBuilder?: `0x${string}` | undefined;
submitToAddress?: `0x${string}` | undefined;
}
| undefined;
};
/**
* EIP155
*/
Expand All @@ -121,6 +166,7 @@ export const DEFAULT_EIP155_OPTIONAL_METHODS = {
export const DEFAULT_OPTIONAL_METHODS = {
...DEFAULT_EIP155_OPTIONAL_METHODS,
...DEFAULT_EIP5792_METHODS,
...DEFAULT_EIP7715_METHODS,
};

export enum DEFAULT_EIP_155_EVENTS {
Expand Down
61 changes: 61 additions & 0 deletions advanced/dapps/react-dapp-v2/src/contexts/JsonRpcContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ import {
SendCallsParams,
GetCapabilitiesResult,
GetCallsResult,
DEFAULT_EIP7715_METHODS,
WalletGrantPermissionsParameters,
WalletGrantPermissionsReturnType,
} from "../constants";
import { useChainData } from "./ChainDataContext";
import { rpcProvidersByChainId } from "../../src/helpers/api";
Expand Down Expand Up @@ -96,6 +99,7 @@ interface IContext {
testSignTypedDatav4: TRpcRequestCallback;
testWalletGetCapabilities: TRpcRequestCallback;
testWalletSendCalls: TRpcRequestCallback;
testWalletGrantPermissions: TRpcRequestCallback;
testWalletGetCallsStatus: TRpcRequestCallback;
};
cosmosRpc: {
Expand Down Expand Up @@ -633,6 +637,63 @@ export function JsonRpcContextProvider({
};
}
),
testWalletGrantPermissions: _createJsonRpcRequestHandler(
async (chainId: string, address: string) => {
const caipAccountAddress = `${chainId}:${address}`;
const account = accounts.find(
(account) => account === caipAccountAddress
);
if (account === undefined)
throw new Error(`Account for ${caipAccountAddress} not found`);
// split chainId
const [namespace, reference] = chainId.split(":");
const rpc = rpcProvidersByChainId[Number(reference)];
if (typeof rpc === "undefined") {
throw new Error(
`Missing rpcProvider definition for chainId: ${chainId}`
);
}
const walletGrantPermissionsParameters: WalletGrantPermissionsParameters =
{
signer: {
type: "key",
data: {
id: "0xc3cE257B5e2A2ad92747dd486B38d7b4B36Ac7C9",
},
},
permissions: [
{
type: "native-token-limit",
data: {
amount: parseEther("0.5"),
},
policies: [],
required: true,
},
],

expiry: 1716846083638,
} as WalletGrantPermissionsParameters;
// send wallet_grantPermissions rpc request
const issuePermissionResponse =
await client!.request<WalletGrantPermissionsReturnType>({
topic: session!.topic,
chainId,
request: {
method: DEFAULT_EIP7715_METHODS.WALLET_GRANT_PERMISSIONS,
params: [walletGrantPermissionsParameters],
},
});

// format displayed result
return {
method: DEFAULT_EIP7715_METHODS.WALLET_GRANT_PERMISSIONS,
address,
valid: true,
result: JSON.stringify(issuePermissionResponse),
};
}
),
};

// -------- COSMOS RPC METHODS --------
Expand Down
9 changes: 9 additions & 0 deletions advanced/dapps/react-dapp-v2/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
DEFAULT_EIP155_OPTIONAL_METHODS,
DEFAULT_EIP5792_METHODS,
GetCapabilitiesResult,
DEFAULT_EIP7715_METHODS,
} from "../constants";
import { AccountAction, setLocaleStorageTestnetFlag } from "../helpers";
import Toggle from "../components/Toggle";
Expand Down Expand Up @@ -217,6 +218,13 @@ const Home: NextPage = () => {
await ethereumRpc.testWalletGetCallsStatus(chainId, address);
},
},
[DEFAULT_EIP7715_METHODS.WALLET_GRANT_PERMISSIONS]: {
method: DEFAULT_EIP7715_METHODS.WALLET_GRANT_PERMISSIONS,
callback: async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testWalletGrantPermissions(chainId, address);
},
},
};

let availableActions: AccountAction[] = [];
Expand All @@ -230,6 +238,7 @@ const Home: NextPage = () => {
const requiresCapabilityCheck = [
"wallet_sendCalls",
"wallet_getCallsStatus",
"wallet_grantPermissions",
].includes(methodName);
// Check capabilities only if the method requires it
if (
Expand Down
14 changes: 7 additions & 7 deletions advanced/wallets/react-wallet-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@
"@taquito/taquito": "^15.1.0",
"@types/semver": "^7.5.8",
"@walletconnect/web3wallet": "1.12.3",
"@zerodev/ecdsa-validator": "5.2.3",
"@zerodev/presets": "5.2.2",
"@zerodev/sdk": "5.2.11",
"@zerodev/session-key": "5.2.2",
"@zerodev/weighted-ecdsa-validator": "5.2.4",
"@zerodev/ecdsa-validator": "5.3.0",
"@zerodev/presets": "5.3.0",
"@zerodev/sdk": "5.3.1",
"@zerodev/session-key": "5.4.0",
"@zerodev/weighted-ecdsa-validator": "5.3.0",
"borsh": "^1.0.0",
"bs58": "5.0.0",
"cosmos-wallet": "1.2.0",
Expand All @@ -47,7 +47,7 @@
"graphql": "^16.8.2",
"near-api-js": "^0.45.0",
"next": "12.1.5",
"permissionless": "0.1.21",
"permissionless": "0.1.31",
"react": "17.0.2",
"react-code-blocks": "0.1.5",
"react-dom": "17.0.2",
Expand All @@ -56,7 +56,7 @@
"solana-wallet": "^1.0.2",
"tronweb": "^4.4.0",
"valtio": "1.13.2",
"viem": "2.9.29"
"viem": "2.16.2"
},
"devDependencies": {
"@types/node": "17.0.35",
Expand Down
3 changes: 3 additions & 0 deletions advanced/wallets/react-wallet-v2/src/components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useCallback, useMemo } from 'react'
import AuthRequestModal from '@/views/AuthRequestModal'
import LoadingModal from '@/views/LoadingModal'
import SessionAuthenticateModal from '@/views/SessionAuthenticateModal'
import SessionGrantPermissionsModal from '@/views/SessionGrantPermissionsModal'

export default function Modal() {
const { open, view } = useSnapshot(ModalStore.state)
Expand All @@ -39,6 +40,8 @@ export default function Modal() {
return <SessionSignTypedDataModal />
case 'SessionSendTransactionModal':
return <SessionSendTransactionModal />
case 'SessionGrantPermissionsModal':
return <SessionGrantPermissionsModal />
case 'SessionSendCallsModal':
return <SessionSendCallsModal />
case 'SessionUnsuportedMethodModal':
Expand Down
6 changes: 6 additions & 0 deletions advanced/wallets/react-wallet-v2/src/data/EIP7715Data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* EIP7715Method
*/
export const EIP7715_METHOD = {
WALLET_GRANT_PERMISSIONS: 'wallet_grantPermissions'
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { formatJsonRpcError } from '@json-rpc-tools/utils'
import { approveEIP5792Request } from '@/utils/EIP5792RequestHandlerUtils'
import EIP155Lib from '@/lib/EIP155Lib'
import { getWallet } from '@/utils/EIP155WalletUtil'
import { EIP7715_METHOD } from '@/data/EIP7715Data'

export default function useWalletConnectEventsManager(initialized: boolean) {
/******************************************************************************
Expand Down Expand Up @@ -64,6 +65,11 @@ export default function useWalletConnectEventsManager(initialized: boolean) {
case EIP155_SIGNING_METHODS.ETH_SIGN_TRANSACTION:
return ModalStore.open('SessionSendTransactionModal', { requestEvent, requestSession })

case EIP7715_METHOD.WALLET_GRANT_PERMISSIONS: {
console.log({ request })
return ModalStore.open('SessionGrantPermissionsModal', { requestEvent, requestSession })
}

case EIP5792_METHODS.WALLET_GET_CAPABILITIES:
case EIP5792_METHODS.WALLET_GET_CALLS_STATUS:
return await web3wallet.respondSessionRequest({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import {
PermissionContext,
SingleSignerPermission
} from '@/utils/permissionValidatorUtils'
import { KERNEL_V2_4, KERNEL_V3_1 } from '@zerodev/sdk/constants'
import { KERNEL_V2_VERSION_TYPE, KERNEL_V3_VERSION_TYPE } from '@zerodev/sdk/types'

type SmartAccountLibOptions = {
privateKey: string
Expand All @@ -64,12 +66,11 @@ export class KernelSmartAccountLib implements EIP155Wallet {
public address?: `0x${string}`
public sponsored: boolean = true
public entryPoint: EntryPoint
public kernelVersion: KERNEL_V3_VERSION_TYPE | KERNEL_V2_VERSION_TYPE
private signer: PrivateKeyAccount
private client: KernelAccountClient<EntryPoint, Transport, Chain | undefined> | undefined
private publicClient:
| (PublicClient &
BundlerClient<ENTRYPOINT_ADDRESS_V07_TYPE> &
BundlerActions<ENTRYPOINT_ADDRESS_V07_TYPE>)
| (PublicClient & BundlerClient<EntryPoint> & BundlerActions<EntryPoint>)
| undefined
private validator: KernelValidator<EntryPoint> | undefined
public initialized = false
Expand All @@ -88,8 +89,10 @@ export class KernelSmartAccountLib implements EIP155Wallet {
this.#signerPrivateKey = privateKey
this.signer = privateKeyToAccount(privateKey as Hex)
this.entryPoint = ENTRYPOINT_ADDRESS_V07
this.kernelVersion = KERNEL_V3_1
if (entryPointVersion === 6) {
this.entryPoint = ENTRYPOINT_ADDRESS_V06
this.kernelVersion = KERNEL_V2_4
}
}
async init() {
Expand All @@ -100,18 +103,20 @@ export class KernelSmartAccountLib implements EIP155Wallet {
const bundlerRpc = http(`https://rpc.zerodev.app/api/v2/bundler/${projectId}`)
this.publicClient = createPublicClient({
transport: bundlerRpc // use your RPC provider or bundler
}).extend(bundlerActions(ENTRYPOINT_ADDRESS_V07))
}).extend(bundlerActions(this.entryPoint))

this.validator = await signerToEcdsaValidator(this.publicClient, {
signer: this.signer,
entryPoint: this.entryPoint
entryPoint: this.entryPoint,
kernelVersion: this.kernelVersion
})

const account = await createKernelAccount(this.publicClient, {
plugins: {
sudo: this.validator
},
entryPoint: this.entryPoint
entryPoint: this.entryPoint,
kernelVersion: this.kernelVersion
})
const client = createKernelAccountClient({
account,
Expand All @@ -132,7 +137,7 @@ export class KernelSmartAccountLib implements EIP155Wallet {
})
}
}
}).extend(bundlerActions(ENTRYPOINT_ADDRESS_V07))
}).extend(bundlerActions(this.entryPoint))
this.client = client
console.log('Smart account initialized', {
address: account.address,
Expand Down Expand Up @@ -252,20 +257,22 @@ export class KernelSmartAccountLib implements EIP155Wallet {
const sessionKeyAddress = address
console.log('Issuing new session key', { sessionKeyAddress })
const emptySessionKeySigner = addressToEmptyAccount(sessionKeyAddress)

console.log(parsedPermissions)
const sessionKeyValidator = await signerToSessionKeyValidator(this.publicClient, {
signer: emptySessionKeySigner,
validatorData: {
permissions: parsedPermissions
},
kernelVersion: this.kernelVersion,
entryPoint: this.entryPoint
})
const sessionKeyAccount = await createKernelAccount(this.publicClient, {
plugins: {
sudo: this.validator,
regular: sessionKeyValidator
},
entryPoint: this.entryPoint
entryPoint: this.entryPoint,
kernelVersion: this.kernelVersion
})
console.log('Session key account initialized', { address: sessionKeyAccount.address })
const serializedSessionKey = await serializeSessionKeyAccount(sessionKeyAccount)
Expand All @@ -290,7 +297,7 @@ export class KernelSmartAccountLib implements EIP155Wallet {
const newSigners = [{ address: currentAddress, weight: 100 }, ...coSigners]
console.log('Updating account Co-Signers', { newSigners })

const updateCall = getUpdateConfigCall(this.entryPoint, {
const updateCall = getUpdateConfigCall(this.entryPoint, this.kernelVersion, {
threshold: 100,
signers: newSigners
})
Expand All @@ -314,7 +321,9 @@ export class KernelSmartAccountLib implements EIP155Wallet {
}
],
functionName: 'currentNonce',
args: []
args: [],
factory: undefined,
factoryData: undefined
})
console.log(`currentNonce : ${currentNonce}`)
return currentNonce
Expand Down
Loading

0 comments on commit 6a94d64

Please sign in to comment.