@@ -76,8 +76,8 @@ class TokenboundClient {
7676 private supportsV3 : boolean = true // Default to V3 implementation
7777 private signer ?: AbstractEthersSigner
7878 private walletClient ?: WalletClient
79- private implementationAddress ? : `0x${string } `
80- private registryAddress ? : `0x${string } `
79+ private implementationAddress : `0x${string } `
80+ private registryAddress : `0x${string } `
8181
8282 constructor ( options : TokenboundClientOptions ) {
8383 const {
@@ -123,26 +123,19 @@ class TokenboundClient {
123123 transport : http ( publicClientRPCUrl ?? undefined ) ,
124124 } )
125125
126- if ( registryAddress ) {
127- this . registryAddress = registryAddress
128- }
126+ this . registryAddress = registryAddress ?? ERC_6551_DEFAULT . REGISTRY . ADDRESS
127+ this . implementationAddress =
128+ implementationAddress ?? ERC_6551_DEFAULT . ACCOUNT_PROXY ! . ADDRESS
129129
130- if ( implementationAddress ) {
131- this . implementationAddress = implementationAddress
132-
133- // If legacy V2 implementation is in use, use V2 registry (unless custom registry is provided)
134- const isV2 =
135- ( version && version === TBVersion . V2 ) ||
136- ( implementationAddress &&
137- isAddressEqual (
138- implementationAddress ,
139- ERC_6551_LEGACY_V2 . IMPLEMENTATION . ADDRESS
140- ) )
141-
142- if ( isV2 ) {
143- this . supportsV3 = false
144- if ( ! registryAddress ) this . registryAddress = ERC_6551_LEGACY_V2 . REGISTRY . ADDRESS
145- }
130+ // If legacy V2 implementation is in use, use V2 registry (unless custom registry is provided)
131+ const isV2 =
132+ ( version && version === TBVersion . V2 ) ||
133+ ( implementationAddress &&
134+ isAddressEqual ( implementationAddress , ERC_6551_LEGACY_V2 . IMPLEMENTATION . ADDRESS ) )
135+
136+ if ( isV2 ) {
137+ this . supportsV3 = false
138+ if ( ! registryAddress ) this . registryAddress = ERC_6551_LEGACY_V2 . REGISTRY . ADDRESS
146139 }
147140
148141 this . isInitialized = true
@@ -153,6 +146,14 @@ class TokenboundClient {
153146 }
154147 }
155148
149+ /**
150+ * Returns the SDK's package version.
151+ * @returns The version of the SDK.
152+ */
153+ public getSDKVersion ( ) : string {
154+ return TB_SDK_VERSION
155+ }
156+
156157 /**
157158 * Returns the tokenbound account address for a given token contract and token ID.
158159 * @param {`0x${string}` } params.tokenContract The address of the token contract.
@@ -161,13 +162,17 @@ class TokenboundClient {
161162 */
162163 public getAccount ( params : GetAccountParams ) : `0x${string } ` {
163164 const { tokenContract, tokenId, salt = 0 } = params
164- const implementation =
165- this . implementationAddress ?? ERC_6551_DEFAULT . ACCOUNT_PROXY ! . ADDRESS
166- const registry = this . registryAddress ?? ERC_6551_DEFAULT . REGISTRY . ADDRESS
167165
168166 try {
169167 const getAcct = this . supportsV3 ? getTokenboundV3Account : computeAccount
170- return getAcct ( tokenContract , tokenId , this . chainId , implementation , registry , salt )
168+ return getAcct (
169+ tokenContract ,
170+ tokenId ,
171+ this . chainId ,
172+ this . implementationAddress ,
173+ this . registryAddress ,
174+ salt
175+ )
171176 } catch ( error ) {
172177 throw error
173178 }
@@ -179,50 +184,90 @@ class TokenboundClient {
179184 * @param {string } params.tokenId The token ID.
180185 * @returns The prepared transaction to create a tokenbound account. Can be sent via `sendTransaction` on an Ethers signer or viem WalletClient.
181186 */
182- public async prepareCreateAccount ( params : PrepareCreateAccountParams ) : Promise < {
183- to : `0x${string } `
184- value : bigint
185- data : `0x${string } `
186- } > {
187+ public async prepareCreateAccount ( params : PrepareCreateAccountParams ) : Promise <
188+ | MultiCallTx
189+ | {
190+ to : `0x${string } `
191+ value : bigint
192+ data : `0x${string } `
193+ }
194+ > {
187195 const { tokenContract, tokenId, salt = 0 } = params
188- const implementation =
189- this . implementationAddress ?? ERC_6551_DEFAULT . ACCOUNT_PROXY ! . ADDRESS
190- const registry = this . registryAddress ?? ERC_6551_DEFAULT . REGISTRY . ADDRESS
191196
192- const prepareCreation = this . supportsV3
197+ const getAcct = this . supportsV3 ? getTokenboundV3Account : computeAccount
198+
199+ const computedAcct = getAcct (
200+ tokenContract ,
201+ tokenId ,
202+ this . chainId ,
203+ this . implementationAddress ,
204+ this . registryAddress ,
205+ salt
206+ )
207+
208+ const isCustomImplementation = ! [
209+ ERC_6551_DEFAULT . ACCOUNT_PROXY ! . ADDRESS ,
210+ ERC_6551_DEFAULT . IMPLEMENTATION . ADDRESS ,
211+ ] . includes ( getAddress ( this . implementationAddress ) )
212+
213+ const prepareBasicCreateAccount = this . supportsV3
193214 ? prepareCreateTokenboundV3Account
194215 : prepareCreateAccount
195216
196- return prepareCreation (
217+ const preparedBasicCreateAccount = await prepareBasicCreateAccount (
197218 tokenContract ,
198219 tokenId ,
199220 this . chainId ,
200- implementation ,
201- registry ,
221+ this . implementationAddress ,
222+ this . registryAddress ,
202223 salt
203224 )
225+
226+ if ( isCustomImplementation ) {
227+ // Don't initialize for custom implementations. Allow third-party handling of initialization.
228+ return preparedBasicCreateAccount
229+ } else {
230+ // For standard implementations, use the multicall3 aggregate function to create and initialize the account in one transaction
231+ return {
232+ to : MULTICALL_ADDRESS ,
233+ value : BigInt ( 0 ) ,
234+ data : encodeFunctionData ( {
235+ abi : multicall3Abi ,
236+ functionName : 'aggregate3' ,
237+ args : [
238+ [
239+ {
240+ target : this . registryAddress ,
241+ allowFailure : false ,
242+ callData : preparedBasicCreateAccount . data ,
243+ } ,
244+ {
245+ target : computedAcct ,
246+ allowFailure : false ,
247+ callData : encodeFunctionData ( {
248+ abi : ERC_6551_DEFAULT . ACCOUNT_PROXY ?. ABI ! ,
249+ functionName : 'initialize' ,
250+ args : [ ERC_6551_DEFAULT . IMPLEMENTATION ! . ADDRESS ] ,
251+ } ) ,
252+ } ,
253+ ] ,
254+ ] ,
255+ } ) ,
256+ } as MultiCallTx
257+ }
204258 }
205259
206260 /**
207261 * Returns the transaction hash of the transaction that created the tokenbound account for a given token contract and token ID.
208262 * @param {`0x${string}` } params.tokenContract The address of the token contract.
209263 * @param {string } params.tokenId The token ID.
210- * @param {`0x${string}` } [params.implementationAddress] The address of the implementation contract.
211- * @param {`0x${string}` } [params.registryAddress] The address of the registry contract.
212264 * @returns a Promise that resolves to the account address of the created tokenbound account.
213265 */
214266 public async createAccount (
215267 params : CreateAccountParams
216268 ) : Promise < { account : `0x${string } `; txHash : `0x${string } ` } > {
217269 const { tokenContract, tokenId, salt = 0 } = params
218270
219- const implementation =
220- this . implementationAddress ?? ERC_6551_DEFAULT . ACCOUNT_PROXY ! . ADDRESS
221- const registry = this . registryAddress ?? ERC_6551_DEFAULT . REGISTRY . ADDRESS
222- const isCustomImplementation =
223- this . implementationAddress &&
224- ! isAddressEqual ( this . implementationAddress , ERC_6551_DEFAULT . ACCOUNT_PROXY ! . ADDRESS )
225-
226271 try {
227272 let txHash : `0x${string } ` | undefined
228273
@@ -232,77 +277,34 @@ class TokenboundClient {
232277 tokenContract ,
233278 tokenId ,
234279 this . chainId ,
235- implementation ,
236- registry ,
280+ this . implementationAddress ,
281+ this . registryAddress ,
237282 salt
238283 )
239284
240- const prepareCreateAccount = await this . prepareCreateAccount ( {
285+ const preparedCreateAccount = await this . prepareCreateAccount ( {
241286 tokenContract,
242287 tokenId,
243288 salt,
244289 } )
245290
246- let prepareCreateV3Account :
247- | MultiCallTx
248- | {
249- to : `0x${string } `
250- value : bigint
251- data : `0x${string } `
252- }
253-
254- if ( isCustomImplementation ) {
255- // Don't initalize for custom implementations. Allow third-party handling of initialization.
256- prepareCreateV3Account = prepareCreateAccount
257- } else {
258- // For standard implementations, use the multicall3 aggregate function to create the account and initialize it in one transaction
259- prepareCreateV3Account = {
260- to : MULTICALL_ADDRESS ,
261- value : BigInt ( 0 ) ,
262- data : encodeFunctionData ( {
263- abi : multicall3Abi ,
264- functionName : 'aggregate3' ,
265- args : [
266- [
267- {
268- target : registry ,
269- allowFailure : false ,
270- callData : prepareCreateAccount . data ,
271- } ,
272- {
273- target : computedAcct ,
274- allowFailure : false ,
275- callData : encodeFunctionData ( {
276- abi : ERC_6551_DEFAULT . ACCOUNT_PROXY ?. ABI ! ,
277- functionName : 'initialize' ,
278- args : [ ERC_6551_DEFAULT . IMPLEMENTATION ! . ADDRESS ] ,
279- } ) ,
280- } ,
281- ] ,
282- ] ,
283- } ) ,
284- } as MultiCallTx
285- }
286-
287291 if ( this . signer ) {
288292 txHash = ( await this . signer
289- . sendTransaction (
290- this . supportsV3 ? prepareCreateV3Account : prepareCreateAccount
291- )
293+ . sendTransaction ( preparedCreateAccount )
292294 . then ( ( tx : AbstractEthersTransactionResponse ) => tx . hash ) ) as `0x${string } `
293295 } else if ( this . walletClient ) {
294296 txHash = this . supportsV3
295297 ? await this . walletClient . sendTransaction ( {
296- ...prepareCreateV3Account ,
298+ ...preparedCreateAccount ,
297299 chain : chainIdToChain ( this . chainId ) ,
298300 account : this . walletClient ?. account ?. address ! ,
299301 } ) // @BJ TODO: extract into viemV3?
300302 : await createAccount (
301303 tokenContract ,
302304 tokenId ,
303305 this . walletClient ,
304- implementation ,
305- registry ,
306+ this . implementationAddress ,
307+ this . registryAddress ,
306308 salt
307309 )
308310 }
0 commit comments