Skip to content

Commit

Permalink
fix: 4337 deployment (#8226)
Browse files Browse the repository at this point in the history
* fix: owner absence

* refactor: sign user op

* fix: request encoding

* fix: personal sign

* chore: ci

* fix: personal sign

* fix: deployment

* fix: verificationGas

* fix: unit testing

* fix: refactor

* fix: unit testing

Co-authored-by: nuanyang233 <nuanyang233@gmail.com>
  • Loading branch information
guanbinrui and nuanyang233 committed Dec 12, 2022
1 parent 76bc200 commit df9b19d
Show file tree
Hide file tree
Showing 20 changed files with 348 additions and 571 deletions.
26 changes: 15 additions & 11 deletions packages/mask/background/services/identity/persona/sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { MaskMessages } from '../../../../shared/index.js'
import { PersonaIdentifier, fromBase64URL, PopupRoutes, ECKeyIdentifier } from '@masknet/shared-base'
import { queryPersonasWithPrivateKey } from '../../../../background/database/persona/db.js'
import { openPopupWindow } from '../../../../background/services/helper/index.js'
import { delay, encodeText, timeout } from '@masknet/kit'
import { encodeText, timeout } from '@masknet/kit'
import { personalSign } from '@metamask/eth-sig-util'
import { v4 as uuid } from 'uuid'
export interface SignRequest {
Expand Down Expand Up @@ -42,7 +42,7 @@ export interface SignRequestResult {
*/
export async function signWithPersona({ message, method, identifier }: SignRequest): Promise<SignRequestResult> {
if (method !== 'eth') throw new Error('Unknown sign method')
const requestID = Math.random().toString(16).slice(3)
const requestID = uuid()
await openPopupWindow(PopupRoutes.PersonaSignRequest, { message, requestID, identifier: identifier?.toText() })

const signer = await timeout(
Expand All @@ -62,15 +62,19 @@ export async function signWithPersona({ message, method, identifier }: SignReque
export async function signPayWithPersona({ message, identifier }: Omit<SignRequest, 'method'>): Promise<string> {
const requestID = uuid()

const waitForApprove = new Promise<PersonaIdentifier>((resolve, reject) => {
delay(1000 * 60).then(() => reject(new Error('Timeout')))
MaskMessages.events.personaSignRequest.on((approval) => {
if (approval.requestID !== requestID) return
if (!approval.selectedPersona) reject(new Error('Persona Rejected'))
resolve(approval.selectedPersona!)
})
})
const signer = await waitForApprove
await openPopupWindow(PopupRoutes.PersonaSignRequest, { message, requestID, identifier: identifier?.toText() })

const signer = await timeout(
new Promise<PersonaIdentifier>((resolve, reject) => {
MaskMessages.events.personaSignRequest.on((approval) => {
if (approval.requestID !== requestID) return
if (!approval.selectedPersona) reject(new Error('Persona Rejected'))
resolve(approval.selectedPersona!)
})
}),
60 * 1000,
'Timeout',
)

return generatePaySignResult(signer, message)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/plugins/EVM/src/state/Connection/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ class RequestContext implements Context {
return this.editor.config
}

get identifier() {
return this._options?.identifier
}

get userOperation() {
return this.editor.userOperation
}
Expand Down
6 changes: 3 additions & 3 deletions packages/plugins/EVM/src/state/Connection/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class Connection implements EVM_Connection {
context.write(await this.Provider?.disconnect(options.providerType))
break
case EthereumMethodType.ETH_SUPPORTED_ENTRY_POINTS:
case EthereumMethodType.SC_WALLET_DEPLOY:
case EthereumMethodType.MASK_DEPLOY_CONTRACT_WALLET:
break
default: {
const provider =
Expand Down Expand Up @@ -977,7 +977,7 @@ class Connection implements EVM_Connection {
const options = this.getOptions(initial)
return this.hijackedRequest<string>(
{
method: EthereumMethodType.SC_WALLET_DEPLOY,
method: EthereumMethodType.MASK_TRANSFER_CONTRACT_WALLET,
params: [recipient],
},
options,
Expand All @@ -991,7 +991,7 @@ class Connection implements EVM_Connection {
const options = this.getOptions(initial)
return this.hijackedRequest<string>(
{
method: EthereumMethodType.SC_WALLET_DEPLOY,
method: EthereumMethodType.MASK_DEPLOY_CONTRACT_WALLET,
params: [owner],
},
options,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { first } from 'lodash-es'
import type { AbiItem } from 'web3-utils'
import type { BundlerAPI, ContractAccountAPI } from '@masknet/web3-providers/types'
import type { NetworkPluginID } from '@masknet/shared-base'
import type { NetworkPluginID, PersonaIdentifier } from '@masknet/shared-base'
import {
ChainId,
createContract,
Expand All @@ -11,22 +11,24 @@ import {
UserTransaction,
ContractWallet as ContractWalletLib,
ContractTransaction,
isEmptyHex,
} from '@masknet/web3-shared-evm'
import { isSameAddress } from '@masknet/web3-shared-base'
import WalletABI from '@masknet/web3-contracts/abis/Wallet.json'
import type { Wallet as WalletContract } from '@masknet/web3-contracts/types/Wallet.js'
import { Web3StateSettings } from '../../../settings/index.js'
import { SharedContextSettings, Web3StateSettings } from '../../../settings/index.js'
import type { Middleware, Context } from '../types.js'
import { Providers } from '../provider.js'
import type { BaseContractWalletProvider } from '../providers/BaseContractWallet.js'

export class ContractWallet implements Middleware<Context> {
constructor(
/** The address of logic contract. */
protected address: string,
protected providerType: ProviderType,
protected contractAccount: ContractAccountAPI.Provider<NetworkPluginID.PLUGIN_EVM>,
protected bundler: BundlerAPI.Provider,
protected options: {
getLogicContractAddress(chainId: ChainId): string
},
) {}

private createWeb3(context: Context) {
Expand All @@ -53,13 +55,18 @@ export class ContractWallet implements Middleware<Context> {
private async getEntryPoint(chainId: ChainId) {
const entryPoints = await this.bundler.getSupportedEntryPoints(chainId)
const entryPoint = first(entryPoints)
if (!entryPoint || isValidAddress(entryPoint)) throw new Error(`Not supported ${chainId}`)
if (!entryPoint || !isValidAddress(entryPoint)) throw new Error(`Not supported ${chainId}`)
return entryPoint
}

private async getInitCode(chainId: ChainId, owner: string) {
const contractWallet = new ContractWalletLib(chainId, owner, this.address, await this.getEntryPoint(chainId))
if (!contractWallet.initCode) throw new Error('Failed to create initCode.')
const contractWallet = new ContractWalletLib(
chainId,
owner,
this.options.getLogicContractAddress(chainId),
await this.getEntryPoint(chainId),
)
if (isEmptyHex(contractWallet.initCode)) throw new Error('Failed to create initCode.')
return contractWallet.initCode
}

Expand All @@ -77,6 +84,7 @@ export class ContractWallet implements Middleware<Context> {
context: Context,
owner = context.owner,
userOperation = context.userOperation,
identifier?: PersonaIdentifier,
): Promise<string> {
if (!owner) throw new Error('Failed to sign user operation.')
if (!userOperation) throw new Error('Invalid user operation.')
Expand All @@ -87,12 +95,18 @@ export class ContractWallet implements Middleware<Context> {
await this.getEntryPoint(context.chainId),
userOperation,
)
await userTransaction.sign((message: string) =>
context.connection.signMessage(message, 'personalSign', {
await userTransaction.sign(async (message: string) => {
if (identifier) {
return SharedContextSettings.value.personaSignPayMessage({
message,
identifier,
})
}
return context.connection.signMessage(message, 'personalSign', {
account: owner,
providerType: this.providerType,
}),
)
})
})
return this.bundler.sendUserOperation(context.chainId, userTransaction.toUserOperation())
}

Expand All @@ -101,15 +115,21 @@ export class ContractWallet implements Middleware<Context> {
return new ContractTransaction(this.createWallet(context)).send((x) => x.methods.changeOwner(recipient))
}

private async deploy(context: Context, owner: string) {
private async deploy(context: Context, owner: string, identifier?: PersonaIdentifier) {
const initCode = await this.getInitCode(context.chainId, owner)
const accounts = await this.getDeployedAccounts(context.chainId, owner)

return this.sendUserOperation(context, owner, {
sender: context.account,
nonce: accounts.length,
initCode,
})
return this.sendUserOperation(
context,
owner,
{
sender: context.account,
// nonce: accounts.length,
nonce: 0,
initCode,
},
identifier,
)
}

async fn(context: Context, next: () => Promise<void>) {
Expand Down Expand Up @@ -173,16 +193,22 @@ export class ContractWallet implements Middleware<Context> {
case EthereumMethodType.ETH_SEND_RAW_TRANSACTION:
context.abort(new Error('Not supported by contract wallet.'))
break
case EthereumMethodType.SC_WALLET_CHANGE_OWNER:
case EthereumMethodType.MASK_TRANSFER_CONTRACT_WALLET:
try {
context.write(await this.changeOwner(context, first(context.requestArguments.params)))
} catch (error) {
context.abort(error)
}
break
case EthereumMethodType.SC_WALLET_DEPLOY:
case EthereumMethodType.MASK_DEPLOY_CONTRACT_WALLET:
try {
context.write(await this.deploy(context, first(context.requestArguments.params) ?? context.account))
context.write(
await this.deploy(
context,
first(context.requestArguments.params) ?? context.account,
context.identifier,
),
)
} catch (error) {
context.abort(error)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getSmartPayConstants, ProviderType } from '@masknet/web3-shared-evm'
import { ChainId, getSmartPayConstants, ProviderType } from '@masknet/web3-shared-evm'
import { SmartPayAccount, SmartPayBundler } from '@masknet/web3-providers'
import type { Context, Middleware } from '../types.js'
import { NoneWallet } from '../interceptors/None.js'
Expand All @@ -12,12 +12,11 @@ export class Interceptor implements Middleware<Context> {
private interceptors: Partial<Record<ProviderType, Array<Middleware<Context>>>> = {
[ProviderType.None]: [new NoneWallet()],
[ProviderType.MaskWallet]: [
new ContractWallet(
getSmartPayConstants().LOGIC_WALLET_CONTRACT_ADDRESS ?? '',
ProviderType.MaskWallet,
SmartPayAccount,
SmartPayBundler,
),
new ContractWallet(ProviderType.MaskWallet, SmartPayAccount, SmartPayBundler, {
getLogicContractAddress(chainId: ChainId) {
return getSmartPayConstants(chainId).LOGIC_WALLET_CONTRACT_ADDRESS ?? ''
},
}),
new MaskWallet(),
],
[ProviderType.MetaMask]: [new MetaMask()],
Expand Down
11 changes: 6 additions & 5 deletions packages/plugins/EVM/src/state/Connection/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { RequestArguments } from 'web3-core'
import type { JsonRpcPayload, JsonRpcResponse } from 'web3-core-helpers'
import type { NetworkPluginID } from '@masknet/shared-base'
import type { NetworkPluginID, PersonaIdentifier } from '@masknet/shared-base'
import type { Web3Helper } from '@masknet/web3-helpers'
import type { RecognizableError, WalletProvider } from '@masknet/web3-shared-base'
import type {
Expand Down Expand Up @@ -44,7 +44,7 @@ export interface Context {
readonly providerType: ProviderType
readonly method: EthereumMethodType
readonly connection: EVM_Connection
readonly requestOptions: EVM_Web3ConnectionOptions | undefined
readonly requestOptions?: EVM_Web3ConnectionOptions

/**
* JSON RPC request payload
Expand All @@ -54,10 +54,11 @@ export interface Context {
/**
* JSON RPC response object
*/
readonly response: JsonRpcResponse | undefined
readonly response?: JsonRpcResponse

config: Transaction | undefined
userOperation: UserOperation | undefined
config?: Transaction
identifier?: PersonaIdentifier
userOperation?: UserOperation
requestArguments: RequestArguments
result: unknown
error: RecognizableError | null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ export function Deploy({ open }: { open: boolean }) {
const { value: contractAccount, loading: queryContractLoading } = useAsync(async () => {
if (!signAccount?.identity || !signAccount?.address || !open) return

const accounts = await SmartPayAccount.getAccountsByOwners(ChainId.Mumbai, [signAccount?.address])

return first(accounts)
// TODO: filter by deployed
const deployAccounts = await SmartPayAccount.getAccountsByOwners(ChainId.Mumbai, [signAccount?.address])
return SmartPayAccount.getAccountByNonce(ChainId.Mumbai, signAccount?.address, 0)
}, [signAccount, open])
// #endregion

Expand Down
3 changes: 2 additions & 1 deletion packages/plugins/SmartPay/src/hooks/useDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function useDeploy(
twitterHandler: lastRecognizedIdentity.identifier.userId,
ts: getUnixTime(new Date()),
publicKey: currentPersona?.identifier.publicKeyAsHex,
nonce: 4,
nonce: 2,
})

let signature
Expand Down Expand Up @@ -64,6 +64,7 @@ export function useDeploy(
account: contractAccount?.address,
chainId: ChainId.Mumbai,
providerType: ProviderType.MaskWallet,
identifier: signAccount.raw?.identifier,
})
}, [connection, signAccount, lastRecognizedIdentity?.identifier, currentPersona, contractAccount])
}
Loading

0 comments on commit df9b19d

Please sign in to comment.