import type { Build7702UpgradeTxRequest, Build7702UpgradeTxResponse, BuildAuthorizationListRequest, BuildAuthorizationListResponse, EvmAccountTypeGetStatusRequest, EvmAccountTypeGetStatusResponse, GetAddressesResponse, UpgradeTo7702Request, UpgradeTo7702Response, } from '../../shared/types' import Mpc from '../../mpc' import { PortalCurve } from '../../index' export class EvmAccountType { private mpc: Mpc constructor({ mpc }: { mpc: Mpc }) { this.mpc = mpc } /** * Get the account type for a given chain * @param {EvmAccountTypeGetStatusRequest} params - The request parameters * @returns {Promise} The account type */ public async getStatus({ chain, }: EvmAccountTypeGetStatusRequest): Promise { return this.mpc.handleRequestToIframeAndPost< EvmAccountTypeGetStatusResponse, EvmAccountTypeGetStatusRequest >({ methodMessage: 'portal:evmAccountType:getStatus', errorMessage: 'portal:evmAccountType:getStatusError', resultMessage: 'portal:evmAccountType:getStatusResult', data: { chain }, }) } /** * Get EOA and smart contract addresses for the wallet on the given chain. * Returns addresses from the account-type API. * @param {EvmAccountTypeGetStatusRequest} params - The request parameters (chain id, e.g. eip155:1) * @returns {Promise} The EOA and smart contract addresses */ public async getAddresses({ chain, }: EvmAccountTypeGetStatusRequest): Promise { return this.mpc.handleRequestToIframeAndPost< GetAddressesResponse, EvmAccountTypeGetStatusRequest >({ methodMessage: 'portal:evmAccountType:getAddresses', errorMessage: 'portal:evmAccountType:getAddressesError', resultMessage: 'portal:evmAccountType:getAddressesResult', data: { chain }, }) } /** * Build the authorization list for a given chain. * @param {BuildAuthorizationListRequest} params - The request parameters * @returns {Promise} The authorization list */ public async buildAuthorizationList( params: BuildAuthorizationListRequest, ): Promise { return this.mpc.handleRequestToIframeAndPost< BuildAuthorizationListResponse, BuildAuthorizationListRequest >({ methodMessage: 'portal:evmAccountType:buildAuthorizationList', errorMessage: 'portal:evmAccountType:buildAuthorizationListError', resultMessage: 'portal:evmAccountType:buildAuthorizationListResult', data: params, }) } /** * Build the 7702 upgrade transaction for a given chain. * @param {Build7702UpgradeTxRequest} params - The request parameters * @returns {Promise} The upgrade transaction */ public async build7702UpgradeTx( params: Build7702UpgradeTxRequest, ): Promise { return this.mpc.handleRequestToIframeAndPost< Build7702UpgradeTxResponse, Build7702UpgradeTxRequest >({ methodMessage: 'portal:evmAccountType:build7702UpgradeTx', errorMessage: 'portal:evmAccountType:build7702UpgradeTxError', resultMessage: 'portal:evmAccountType:build7702UpgradeTxResult', data: params, }) } /** * Upgrade the account to EIP-7702. This handles the entire upgrade process, including building the authorization list, signing the transaction, and sending the transaction. * @param {UpgradeTo7702Request} params - The request parameters * @returns {Promise} The upgrade response */ public async upgradeTo7702( params: UpgradeTo7702Request, ): Promise { const { chain } = params if (!chain.startsWith('eip155:')) { throw new Error( `Invalid chain for EIP-7702 upgrade. Expected EVM chain (eip155:*), got: ${chain}`, ) } const accountType = await this.getStatus({ chain }) if (accountType.data.status === 'SMART_CONTRACT') { throw new Error( 'Account is already a smart contract. EIP-7702 upgrade is only available for EOA accounts.', ) } if (accountType.data.status === 'EIP_7702_EOA') { throw new Error('Account is already upgraded to EIP-7702.') } if (accountType.data.status !== 'EIP_155_EOA') { throw new Error(`Unexpected account type: ${accountType.data.status}`) } const authorizationList = await this.buildAuthorizationList({ chain, subsidize: true, }) const hashWithoutPrefix = authorizationList.data.hash.startsWith('0x') ? authorizationList.data.hash.slice(2) : authorizationList.data.hash const rawSignTx = await this.mpc.rawSign( PortalCurve.SECP256K1, hashWithoutPrefix, ) const upgradeTx = await this.build7702UpgradeTx({ chain, signature: rawSignTx, subsidize: true, }) const txHash = upgradeTx.data.transactionHash if (!txHash) { throw new Error( 'Transaction hash not returned from build7702UpgradeTx. The upgrade transaction may not have been submitted.', ) } return { txHash } } }