Skip to content
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

chore(account): update ZeroDev #2827

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed .yarn/cache/plur-npm-1.0.0-1fcc5c988f-bd8725178b.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed .yarn/cache/resolve-patch-1169b4314d-064d09c180.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
20 changes: 20 additions & 0 deletions packages/galaxy-client/gql/account.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,23 @@ mutation updateConnectedAccountsProperties(
) {
updateConnectedAccountsProperties(accountURNList: $accountURNList)
}

mutation registerSessionKey(
$sessionKeyAddress: String!
$smartContractWalletAddress: String!
) {
registerSessionKey(
sessionKeyAddress: $sessionKeyAddress
smartContractWalletAddress: $smartContractWalletAddress
)
}

mutation revokeSessionKey(
$sessionKeyAddress: String!
$smartContractWalletAddress: String!
) {
revokeSessionKey(
sessionKeyAddress: $sessionKeyAddress
smartContractWalletAddress: $smartContractWalletAddress
)
}
47 changes: 46 additions & 1 deletion packages/galaxy-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type Mutation = {
__typename?: 'Mutation';
disconnectAccount?: Maybe<Scalars['Boolean']>;
registerSessionKey: Scalars['String'];
revokeSessionKey?: Maybe<Scalars['Boolean']>;
setExternalAppData?: Maybe<Scalars['Boolean']>;
updateAccountNickname?: Maybe<Scalars['Boolean']>;
updateConnectedAccountsProperties?: Maybe<Scalars['Boolean']>;
Expand All @@ -68,7 +69,13 @@ export type MutationDisconnectAccountArgs = {


export type MutationRegisterSessionKeyArgs = {
sessionPublicKey: Scalars['String'];
sessionKeyAddress: Scalars['String'];
smartContractWalletAddress: Scalars['String'];
};


export type MutationRevokeSessionKeyArgs = {
sessionKeyAddress: Scalars['String'];
smartContractWalletAddress: Scalars['String'];
};

Expand Down Expand Up @@ -215,6 +222,22 @@ export type UpdateConnectedAccountsPropertiesMutationVariables = Exact<{

export type UpdateConnectedAccountsPropertiesMutation = { __typename?: 'Mutation', updateConnectedAccountsProperties?: boolean | null };

export type RegisterSessionKeyMutationVariables = Exact<{
sessionKeyAddress: Scalars['String'];
smartContractWalletAddress: Scalars['String'];
}>;


export type RegisterSessionKeyMutation = { __typename?: 'Mutation', registerSessionKey: string };

export type RevokeSessionKeyMutationVariables = Exact<{
sessionKeyAddress: Scalars['String'];
smartContractWalletAddress: Scalars['String'];
}>;


export type RevokeSessionKeyMutation = { __typename?: 'Mutation', revokeSessionKey?: boolean | null };

export type GetExternalAppDataQueryVariables = Exact<{ [key: string]: never; }>;


Expand Down Expand Up @@ -291,6 +314,22 @@ export const UpdateConnectedAccountsPropertiesDocument = gql`
updateConnectedAccountsProperties(accountURNList: $accountURNList)
}
`;
export const RegisterSessionKeyDocument = gql`
mutation registerSessionKey($sessionKeyAddress: String!, $smartContractWalletAddress: String!) {
registerSessionKey(
sessionKeyAddress: $sessionKeyAddress
smartContractWalletAddress: $smartContractWalletAddress
)
}
`;
export const RevokeSessionKeyDocument = gql`
mutation revokeSessionKey($sessionKeyAddress: String!, $smartContractWalletAddress: String!) {
revokeSessionKey(
sessionKeyAddress: $sessionKeyAddress
smartContractWalletAddress: $smartContractWalletAddress
)
}
`;
export const GetExternalAppDataDocument = gql`
query getExternalAppData {
externalAppData: getExternalAppData
Expand Down Expand Up @@ -364,6 +403,12 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
updateConnectedAccountsProperties(variables: UpdateConnectedAccountsPropertiesMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<UpdateConnectedAccountsPropertiesMutation> {
return withWrapper((wrappedRequestHeaders) => client.request<UpdateConnectedAccountsPropertiesMutation>(UpdateConnectedAccountsPropertiesDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'updateConnectedAccountsProperties', 'mutation');
},
registerSessionKey(variables: RegisterSessionKeyMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<RegisterSessionKeyMutation> {
return withWrapper((wrappedRequestHeaders) => client.request<RegisterSessionKeyMutation>(RegisterSessionKeyDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'registerSessionKey', 'mutation');
},
revokeSessionKey(variables: RevokeSessionKeyMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<RevokeSessionKeyMutation> {
return withWrapper((wrappedRequestHeaders) => client.request<RevokeSessionKeyMutation>(RevokeSessionKeyDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'revokeSessionKey', 'mutation');
},
getExternalAppData(variables?: GetExternalAppDataQueryVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<GetExternalAppDataQuery> {
return withWrapper((wrappedRequestHeaders) => client.request<GetExternalAppDataQuery>(GetExternalAppDataDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'getExternalAppData', 'query');
},
Expand Down
2 changes: 1 addition & 1 deletion packages/urns/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@
},
"dependencies": {
"urns": "0.6.0",
"viem": "1.20.0"
"viem": "2.7.1"
}
}
9 changes: 6 additions & 3 deletions platform/account/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,23 @@
"typescript": "5.0.4"
},
"dependencies": {
"@ethersproject/wallet": "5.7.0",
"@proofzero/platform-clients": "workspace:*",
"@proofzero/platform.core": "workspace:*",
"@proofzero/types": "workspace:*",
"@proofzero/urns": "workspace:*",
"@proofzero/utils": "workspace:*",
"@trpc/server": "10.8.1",
"@types/node": "18.15.3",
"@zerodevapp/sdk": "3.1.57",
"@zerodev/ecdsa-validator": "5.0.3",
"@zerodev/presets": "5.1.2",
"@zerodev/sdk": "5.0.9",
"@zerodev/session-key": "5.0.2",
"do-proxy": "1.3.3",
"jose": "4.11.0",
"permissionless": "0.0.34",
"random-words": "2.0.0",
"remix-auth-google": "1.2.0",
"viem": "1.20.0",
"viem": "2.7.1",
"zod": "3.22.4"
}
}
2 changes: 2 additions & 0 deletions platform/account/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ export const NO_OP_ACCOUNT_PLACEHOLDER = AccountURNSpace.componentizedUrn(
{ addr_type: EmailAccountType.Email, node_type: NodeType.Email },
{ alias: 'no-op-account-placeholder' }
)

export const ZERODEV_SESSION_KEY_TTL = 90 * 24 * 60 * 60 * 10000 // 90 days
28 changes: 17 additions & 11 deletions platform/account/src/jsonrpc/methods/initSmartContractWallet.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { z } from 'zod'
import { Wallet } from '@ethersproject/wallet'
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
import { polygonMumbai as chain } from 'viem/chains'
import { createEcdsaKernelAccountClient } from '@zerodev/presets/zerodev'

import { router } from '@proofzero/platform.core'
import { AccountURNInput } from '@proofzero/platform-middleware/inputValidators'

import { Hex } from '../validators/wallet'
import { Context } from '../../context'
import { CryptoAccountType, NodeType } from '@proofzero/types/account'
import { initAccountNodeByName } from '../../nodes'

import createImageClient from '@proofzero/platform-clients/image'
import { getZeroDevSigner } from '@zerodevapp/sdk'
import { EDGE_ACCOUNT } from '@proofzero/platform.account/src/constants'

import { generateTraceContextHeaders } from '@proofzero/platform-middleware/trace'
Expand All @@ -22,7 +25,7 @@ type InitSmartContractWalletInput = z.infer<typeof InitSmartContractWalletInput>

export const InitSmartContractWalletOutput = z.object({
accountURN: AccountURNInput,
walletAccount: z.string(),
walletAccount: Hex,
})

type InitSmartContractWalletResult = z.infer<
Expand All @@ -37,21 +40,24 @@ export const initSmartContractWalletMethod = async ({
ctx: Context
}): Promise<InitSmartContractWalletResult> => {
const nodeClient = ctx.account
const identity = await nodeClient?.class.getIdentity()

const identity = await nodeClient?.class.getIdentity()
if (!identity) {
throw new Error('missing identity')
}

const owner = Wallet.createRandom()
const projectId = ctx.env.SECRET_ZERODEV_PROJECTID

const privateKey = generatePrivateKey()
const signer = privateKeyToAccount(privateKey)

const smartContractWallet = await getZeroDevSigner({
skipFetchSetup: true,
projectId: ctx.env.SECRET_ZERODEV_PROJECTID,
owner,
const kernelClient = await createEcdsaKernelAccountClient({
chain,
projectId,
signer,
})

const smartContractWalletAddress = await smartContractWallet.getAddress()
const smartContractWalletAddress = kernelClient.account.address

const { accountURN, baseAccountURN } = generateSmartWalletAccountUrn(
smartContractWalletAddress,
Expand All @@ -70,7 +76,7 @@ export const initSmartContractWalletMethod = async ({
gradientSeed: smartContractWalletAddress,
})
await Promise.all([
smartContractWalletNode.storage.put('privateKey', owner.privateKey),
smartContractWalletNode.storage.put('privateKey', privateKey),
smartContractWalletNode.class.setAddress(smartContractWalletAddress),
smartContractWalletNode.class.setNickname(input.nickname),
smartContractWalletNode.class.setNodeType(NodeType.Crypto),
Expand Down
69 changes: 40 additions & 29 deletions platform/account/src/jsonrpc/methods/registerWalletSessionKey.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { z } from 'zod'
import { createPublicClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { polygonMumbai as chain } from 'viem/chains'
import { addressToEmptyAccount, createKernelAccount } from '@zerodev/sdk'
import { signerToEcdsaValidator } from '@zerodev/ecdsa-validator'
import {
serializeSessionKeyAccount,
signerToSessionKeyValidator,
} from '@zerodev/session-key'

import { Hex } from '../validators/wallet'
import { Context } from '../../context'
import { initAccountNodeByName } from '../../nodes'
import {
createSessionKey,
getZeroDevSigner,
getPrivateKeyOwner,
} from '@zerodevapp/sdk'

import { PaymasterSchema } from '@proofzero/platform/starbase/src/jsonrpc/validators/app'
import { BadRequestError, InternalServerError } from '@proofzero/errors'

import { ZERODEV_SESSION_KEY_TTL } from '../../constants'
import { generateSmartWalletAccountUrn } from '../../utils'

export const RegisterSessionKeyInput = z.object({
sessionPublicKey: z.string(),
sessionKeyAddress: Hex,
smartContractWalletAddress: z.string(),
paymaster: PaymasterSchema,
})
Expand All @@ -34,9 +41,9 @@ export const registerSessionKeyMethod = async ({
// This method is being called only from galaxy
// All authorization checks are done in galaxy

const { paymaster, smartContractWalletAddress, sessionPublicKey } = input
const { paymaster, smartContractWalletAddress, sessionKeyAddress } = input

const { baseAccountURN } = await generateSmartWalletAccountUrn(
const { baseAccountURN } = generateSmartWalletAccountUrn(
smartContractWalletAddress,
'' // empty string because we only care about base urn
)
Expand All @@ -46,38 +53,42 @@ export const registerSessionKeyMethod = async ({
ctx.env.Account
)

const ownerPrivateKey = (await smartContractWalletNode.storage.get(
const ownerPrivateKey = await smartContractWalletNode.storage.get<Hex>(
'privateKey'
)) as string
)

if (!ownerPrivateKey) {
throw new BadRequestError({ message: 'missing private key for the user' })
}

let sessionKey = ''

if (paymaster && paymaster.provider === 'zerodev') {
const zdSigner = await getZeroDevSigner({
projectId: paymaster.secret,
owner: getPrivateKeyOwner(ownerPrivateKey),
skipFetchSetup: true,
})

// We need a 90 days in Unix time from now
// 90 days * 24 hours * 60 minutes * 60 seconds * 1000 milliseconds
const truncatedValidUntil = Math.floor(Date.now() / 1000) + 7776000000

try {
sessionKey = await createSessionKey(
zdSigner,
[],
truncatedValidUntil,
sessionPublicKey
)
const signer = privateKeyToAccount(ownerPrivateKey)

const publicClient = createPublicClient({
chain,
transport: http(),
})

const emptySessionKeySigner = addressToEmptyAccount(sessionKeyAddress)

const sessionKeyAccount = await createKernelAccount(publicClient, {
plugins: {
defaultValidator: await signerToEcdsaValidator(publicClient, {
signer,
}),
validator: await signerToSessionKeyValidator(publicClient, {
signer: emptySessionKeySigner,
validatorData: { validUntil: Date.now() + ZERODEV_SESSION_KEY_TTL },
}),
},
})

return serializeSessionKeyAccount(sessionKeyAccount)
} catch (e) {
throw new InternalServerError({ message: 'Failed to create session key' })
}
}

return sessionKey
return ''
}
Loading
Loading