-
Notifications
You must be signed in to change notification settings - Fork 80
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
Feat/versioning #188
Feat/versioning #188
Changes from 8 commits
4a19ca3
0003789
d47e911
3a0bd52
7475fc5
cb32406
34b1537
999ba92
f1c9cd5
2804582
88da9b2
46ab5dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,22 @@ | ||
import { JsonRpcProvider } from '@ethersproject/providers' | ||
import {ethers, BigNumberish, BytesLike, BigNumber } from 'ethers' | ||
import { ethers, BigNumberish, BytesLike, BigNumber } from 'ethers' | ||
import { SmartAccount } from './BaseAccount' | ||
import { | ||
Logger, | ||
NODE_CLIENT_URL, | ||
RPC_PROVIDER_URLS, | ||
EntryPoint_v100__factory, | ||
SmartAccountFactory_v100, | ||
SmartAccountFactory_v100__factory, | ||
SmartAccount_v100__factory | ||
getEntryPointContract, | ||
getSAFactoryContract, | ||
getSAProxyContract | ||
} from '@biconomy/common' | ||
import { BiconomySmartAccountConfig, Overrides } from './utils/Types' | ||
import { UserOperation, Transaction } from '@biconomy/core-types' | ||
import { UserOperation, Transaction, SmartAccountType } from '@biconomy/core-types' | ||
import NodeClient from '@biconomy/node-client' | ||
import INodeClient from '@biconomy/node-client' | ||
import { IBiconomySmartAccount } from 'interfaces/IBiconomySmartAccount' | ||
import { | ||
ISmartAccount, | ||
SupportedChainsResponse, | ||
BalancesResponse, | ||
BalancesDto, | ||
|
@@ -23,13 +25,15 @@ import { | |
SmartAccountsResponse, | ||
SCWTransactionResponse | ||
} from '@biconomy/node-client' | ||
import { ENTRYPOINT_ADDRESSES, BICONOMY_FACTORY_ADDRESSES } from './utils/Constants' | ||
import { ENTRYPOINT_ADDRESSES, BICONOMY_FACTORY_ADDRESSES, BICONOMY_IMPLEMENTATION_ADDRESSES, DEFAULT_ENTRYPOINT_ADDRESS } from './utils/Constants' | ||
|
||
export class BiconomySmartAccount extends SmartAccount implements IBiconomySmartAccount { | ||
private factory: SmartAccountFactory_v100 | ||
private factory!: SmartAccountFactory_v100 | ||
private nodeClient: INodeClient | ||
private accountIndex!: Number | ||
private accountIndex!: number | ||
private address!: string | ||
private smartAccountInfo!: ISmartAccount | ||
private isInited!: boolean | ||
|
||
constructor(readonly biconomySmartAccountConfig: BiconomySmartAccountConfig) { | ||
const { | ||
|
@@ -43,28 +47,25 @@ export class BiconomySmartAccount extends SmartAccount implements IBiconomySmart | |
nodeClientUrl | ||
} = biconomySmartAccountConfig | ||
|
||
const _entryPointAddress = entryPointAddress ?? ENTRYPOINT_ADDRESSES.default | ||
const _factoryAddress = factoryAddress ?? BICONOMY_FACTORY_ADDRESSES.default | ||
const _entryPointAddress = entryPointAddress ?? DEFAULT_ENTRYPOINT_ADDRESS | ||
super({ | ||
bundler, | ||
entryPointAddress: _entryPointAddress | ||
}) | ||
const _rpcUrl = rpcUrl ?? RPC_PROVIDER_URLS[chainId] | ||
if (!_rpcUrl){ | ||
|
||
if (!_rpcUrl) { | ||
throw new Error(`Chain Id ${chainId} is not supported. Please refer to the following link for supported chains list https://docs.biconomy.io/build-with-biconomy-sdk/gasless-transactions#supported-chains`) | ||
} | ||
this.provider = new JsonRpcProvider(_rpcUrl) | ||
this.entryPoint = EntryPoint_v100__factory.connect(_entryPointAddress, this.provider) | ||
this.factory = SmartAccountFactory_v100__factory.connect(_factoryAddress, this.provider) | ||
this.nodeClient = new NodeClient({ txServiceUrl: nodeClientUrl ?? NODE_CLIENT_URL }) | ||
this.signer = signer | ||
|
||
if (paymaster) { | ||
this.paymaster = paymaster | ||
} | ||
if ( bundler ) | ||
this.bundler = bundler | ||
if (bundler) | ||
this.bundler = bundler | ||
} | ||
/** | ||
* @description This function will initialise BiconomyAccount class state | ||
|
@@ -74,38 +75,95 @@ export class BiconomySmartAccount extends SmartAccount implements IBiconomySmart | |
try { | ||
this.isProviderDefined() | ||
this.isSignerDefined() | ||
await this.setAccountIndex(accountIndex) | ||
this.owner = await this.signer.getAddress() | ||
this.chainId = await this.provider.getNetwork().then((net) => net.chainId) | ||
await this.setAccountIndex(accountIndex) | ||
this.isInited = true | ||
} catch (error) { | ||
console.error(`Failed to call init: ${error}`); | ||
Logger.error(`Failed to call init: ${error}`); | ||
throw error | ||
} | ||
|
||
return this | ||
} | ||
|
||
private isInitialized(): boolean{ | ||
if (!this.isInited) | ||
throw new Error('BiconomySmartAccount is not initialized. Please call init() on BiconomySmartAccount before interacting with any other function') | ||
tomarsachin2271 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return true | ||
} | ||
|
||
private setProxyContractState(){ | ||
if ( !BICONOMY_IMPLEMENTATION_ADDRESSES[this.smartAccountInfo.implementationAddress] ) | ||
throw new Error('Could not find attach implementation address again your smart account. Please generate support ticket for further investegation.') | ||
tomarsachin2271 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const proxyInstanceDto = { | ||
smartAccountType: SmartAccountType.BICONOMY, | ||
version: BICONOMY_IMPLEMENTATION_ADDRESSES[this.address], | ||
contractAddress: this.address, | ||
provider: this.provider | ||
} | ||
this.proxy = getSAProxyContract(proxyInstanceDto) | ||
} | ||
|
||
private setEntryPointContractState(){ | ||
const _entryPointAddress = this.smartAccountInfo.entryPointAddress | ||
this.setEntryPointAddress(_entryPointAddress) | ||
if ( !ENTRYPOINT_ADDRESSES[_entryPointAddress] ) | ||
throw new Error('Could not find attach entrypoint address again your smart account. Please generate support ticket for further investegation.') | ||
const entryPointInstanceDto = { | ||
smartAccountType: SmartAccountType.BICONOMY, | ||
version: ENTRYPOINT_ADDRESSES[_entryPointAddress], | ||
contractAddress: _entryPointAddress, | ||
provider: this.provider | ||
} | ||
this.entryPoint = getEntryPointContract(entryPointInstanceDto) | ||
} | ||
|
||
private setFactoryContractState(){ | ||
const _factoryAddress = this.smartAccountInfo.factoryAddress | ||
if ( !BICONOMY_FACTORY_ADDRESSES[_factoryAddress] ) | ||
throw new Error('Could not find attach factory address again your smart account. Please generate support ticket for further investegation.') | ||
const factoryInstanceDto = { | ||
smartAccountType: SmartAccountType.BICONOMY, | ||
version: BICONOMY_FACTORY_ADDRESSES[_factoryAddress], | ||
contractAddress: _factoryAddress, | ||
provider: this.provider | ||
} | ||
this.factory = getSAFactoryContract(factoryInstanceDto) | ||
} | ||
|
||
private async setContractsState() { | ||
this.setProxyContractState() | ||
this.setEntryPointContractState() | ||
this.setFactoryContractState() | ||
} | ||
|
||
async setAccountIndex(accountIndex: number): Promise<void> { | ||
this.accountIndex = accountIndex | ||
this.address = await this.getSmartAccountAddress(accountIndex) | ||
this.proxy = await SmartAccount_v100__factory.connect(this.address, this.provider) | ||
await this.setContractsState() | ||
tomarsachin2271 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
await this.setInitCode(this.accountIndex) | ||
} | ||
|
||
async getSmartAccountAddress(accountIndex: number = 0): Promise<string> { | ||
try { | ||
this.isSignerDefined() | ||
await this.getInitCode(accountIndex) | ||
const address = await this.factory.getAddressForCounterFactualAccount( | ||
await this.signer.getAddress(), | ||
ethers.BigNumber.from(accountIndex) | ||
) | ||
return address | ||
let smartAccountsList: ISmartAccount[] = (await this.getSmartAccountsByOwner({ | ||
chainId: this.chainId, | ||
owner: this.owner | ||
})).data | ||
smartAccountsList = smartAccountsList.filter((smartAccount: ISmartAccount) => { return accountIndex === smartAccount.index }) | ||
tomarsachin2271 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (smartAccountsList.length === 0) | ||
throw new Error('Failed to get smart account address') | ||
this.smartAccountInfo = smartAccountsList[0] | ||
return this.smartAccountInfo.smartAccountAddress | ||
} catch (error) { | ||
console.error(`Failed to get smart account address: ${error}`); | ||
Logger.error(`Failed to get smart account address: ${error}`); | ||
throw error | ||
} | ||
} | ||
|
||
async getInitCode(accountIndex: number = 0): Promise<string> { | ||
private async setInitCode(accountIndex: number = 0): Promise<string> { | ||
this.initCode = ethers.utils.hexConcat([ | ||
this.factory.address, | ||
this.factory.interface.encodeFunctionData('deployCounterFactualAccount', [ | ||
|
@@ -131,6 +189,7 @@ export class BiconomySmartAccount extends SmartAccount implements IBiconomySmart | |
* @returns | ||
*/ | ||
getExecuteCallData(to: string, value: BigNumberish, data: BytesLike): string { | ||
this.isInitialized() | ||
this.isProxyDefined() | ||
const executeCallData = this.proxy.interface.encodeFunctionData('executeCall', [to, value, data]) | ||
return executeCallData | ||
|
@@ -143,12 +202,16 @@ export class BiconomySmartAccount extends SmartAccount implements IBiconomySmart | |
* @returns | ||
*/ | ||
getExecuteBatchCallData(to: Array<string>, value: Array<BigNumberish>, data: Array<BytesLike>): string { | ||
this.isInitialized() | ||
this.isProxyDefined() | ||
const executeBatchCallData = this.proxy.interface.encodeFunctionData('executeBatchCall', [to, value, data]) | ||
return executeBatchCallData | ||
} | ||
|
||
async buildUserOp(transactions: Transaction[], overrides?: Overrides): Promise<Partial<UserOperation>> { | ||
this.isInitialized() | ||
// TODO: validate to, value and data fields | ||
// TODO: validate overrides if supplied | ||
const to = transactions.map((element: Transaction) => element.to) | ||
const data = transactions.map((element: Transaction) => element.data ?? '0x') | ||
const value = transactions.map((element: Transaction) => element.value ?? BigNumber.from('0')) | ||
|
@@ -172,7 +235,9 @@ export class BiconomySmartAccount extends SmartAccount implements IBiconomySmart | |
initCode: nonce.eq(0) ? this.initCode : '0x', | ||
callData: callData | ||
} | ||
|
||
userOp = await this.estimateUserOpGas(userOp, overrides) | ||
Logger.log('userOp after estimation ', userOp) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These Logger.log across all files should be called based on a debug flag in class constructor.... SDK should not print any logs on its own unless developer has enabled it using debug flag. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so logger only logs if debug is enabled. this check is already there in log function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From where is the Logger class getting the debug flag? |
||
userOp.paymasterAndData = await this.getPaymasterAndData(userOp) | ||
return userOp | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,29 @@ | ||
import { ChainId } from '@biconomy/core-types' | ||
import { EntrypointAddresses, BiconomyFactories, BiconomyImplementation } from './Types' | ||
|
||
export const ENTRYPOINT_ADDRESSES = { | ||
'V0_0_5': '0x27a4Db290B89AE3373ce4313cBEaE72112Ae7Da9', | ||
'V0_0_6': '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', | ||
default: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789' | ||
|
||
// will always be latest entrypoint address | ||
export const DEFAULT_ENTRYPOINT_ADDRESS = '0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789' | ||
export const ENTRYPOINT_ADDRESSES: EntrypointAddresses = { | ||
'0x27a4db290b89ae3373ce4313cbeae72112ae7da9': 'V0_0_5', | ||
'0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789': 'V0_0_6' | ||
} | ||
|
||
export const BICONOMY_FACTORY_ADDRESSES = { | ||
'V2_0_0': '0x000000F9eE1842Bb72F6BBDD75E6D3d4e3e9594C', | ||
default: '0x000000F9eE1842Bb72F6BBDD75E6D3d4e3e9594C' | ||
|
||
// will always be latest factory address | ||
export const DEFAULT_BICONOMY_FACTORY_ADDRESS = '0x000000f9ee1842bb72f6bbdd75e6d3d4e3e9594c' | ||
export const BICONOMY_FACTORY_ADDRESSES: BiconomyFactories = { | ||
'0x000000f9ee1842bb72f6bbdd75e6d3d4e3e9594c': 'V1_0_0' | ||
} | ||
|
||
|
||
|
||
export const BICONOMY_IMPLEMENTATION_ADDRESSES: BiconomyImplementation = { | ||
'0x00006b7e42e01957da540dc6a8f7c30c4d816af5': 'V1_0_0' | ||
} | ||
|
||
// will always be latest implementation address | ||
export const DEFAULT_BICONOMY_IMPLEMENTATION_ADDRESS = '0x00006b7e42e01957da540dc6a8f7c30c4d816af5' | ||
|
||
|
||
export const EIP1559_UNSUPPORTED_NETWORKS: Array<ChainId> = [97, 56, 1442, 1101] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename
isInited
toisInitialized
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so there is already a function with same name that i am using to verify either init is called or not. for this reason, i kept is variable name as isInited
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can just keep boolean called initialized