Skip to content

Commit

Permalink
feat: transfer_ownership_v2 (#488)
Browse files Browse the repository at this point in the history
* feat/transfer_ownership(in progress)

* cleanup

* fix linting

* added test for transferOwnership with session key manager module

* Fix linting

* refactor: refactor based on PR review

* improve ts doc + refactor tests

* added "moduleAddress" param to transferOwnership()

* fix module tests

* removed console.logs

* fixed lint + removed unused import

* remove unused import

* added argument type for module address

---------

Co-authored-by: GabiDev <gv@popoo.io>
  • Loading branch information
VGabriel45 and GabiDev45 authored May 13, 2024
1 parent 000fa86 commit 76be2c0
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 54 deletions.
50 changes: 35 additions & 15 deletions src/account/BiconomySmartAccountV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import {
} from "../bundler/index.js"
import {
BaseValidationModule,
ECDSA_OWNERSHIP_MODULE_ADDRESSES_BY_VERSION,
type ModuleInfo,
type SendUserOpParams,
createECDSAOwnershipValidationModule
Expand Down Expand Up @@ -83,6 +82,7 @@ import type {
SimulationType,
SupportedToken,
Transaction,
TransferOwnershipCompatibleModule,
WithdrawalRequest
} from "./utils/Types.js"
import {
Expand Down Expand Up @@ -1372,26 +1372,46 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount {
/**
* Transfers ownership of the smart account to a new owner.
* @param newOwner The address of the new owner.
* @param moduleAddress {@link TransferOwnershipCompatibleModule} The address of the validation module (ECDSA Ownership Module or Multichain Validation Module).
* @param buildUseropDto {@link BuildUserOpOptions}. Optional parameter
* @returns A Promise that resolves to a TransferOwnershipResponse or rejects with an Error.
* @returns A Promise that resolves to a UserOpResponse or rejects with an Error.
* @description This function will transfer ownership of the smart account to a new owner. If you use session key manager module, after transferring the ownership
* you will need to re-create a session for the smart account with the new owner (signer) and specify "accountAddress" in "createSmartAccountClient" function.
* @example
* ```typescript
* const walletClient = createWalletClient({
* account,
* chain: baseSepolia,
* transport: http()
* });
* const smartAccount = await createSmartAccountClient({
* signer: walletClient,
* paymasterUrl: "https://paymaster.biconomy.io/api/v1/...",
* bundlerUrl: `https://bundler.biconomy.io/api/v2/84532/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44`,
* chainId: 84532
* });
* const response = await smartAccount.transferOwnership(newOwner, {paymasterServiceData: {mode: PaymasterMode.SPONSORED}});
*
* let walletClient = createWalletClient({
account,
chain: baseSepolia,
transport: http()
});
let smartAccount = await createSmartAccountClient({
signer: walletClient,
paymasterUrl: "https://paymaster.biconomy.io/api/v1/...",
bundlerUrl: `https://bundler.biconomy.io/api/v2/84532/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44`,
chainId: 84532
});
const response = await smartAccount.transferOwnership(newOwner, DEFAULT_ECDSA_OWNERSHIP_MODULE, {paymasterServiceData: {mode: PaymasterMode.SPONSORED}});
walletClient = createWalletClient({
newOwnerAccount,
chain: baseSepolia,
transport: http()
})
smartAccount = await createSmartAccountClient({
signer: walletClient,
paymasterUrl: "https://paymaster.biconomy.io/api/v1/...",
bundlerUrl: `https://bundler.biconomy.io/api/v2/84532/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44`,
chainId: 84532,
accountAddress: await smartAccount.getAccountAddress()
})
* ```
*/
async transferOwnership(
newOwner: Address,
moduleAddress: TransferOwnershipCompatibleModule,
buildUseropDto?: BuildUserOpOptions
): Promise<UserOpResponse> {
const encodedCall = encodeFunctionData({
Expand All @@ -1400,7 +1420,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount {
args: [newOwner]
})
const transaction = {
to: ECDSA_OWNERSHIP_MODULE_ADDRESSES_BY_VERSION.V1_0_0,
to: moduleAddress,
data: encodedCall
}
const userOpResponse: UserOpResponse = await this.sendTransaction(
Expand Down
4 changes: 4 additions & 0 deletions src/account/utils/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,3 +609,7 @@ export interface ISmartContractAccount<
upgradeToInitData: Hex
) => Promise<Hex>
}

export type TransferOwnershipCompatibleModule =
| "0x0000001c5b32F37F5beA87BDD5374eB2aC54eA8e"
| "0x000000824dc138db84FD9109fc154bdad332Aa8E"
79 changes: 46 additions & 33 deletions tests/account/write.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import { EntryPointAbi } from "../../src/account/abi/EntryPointAbi"
import { getAAError } from "../../src/bundler/utils/getAAError"
import {
DEFAULT_ECDSA_OWNERSHIP_MODULE,
DEFAULT_SESSION_KEY_MANAGER_MODULE
DEFAULT_MULTICHAIN_MODULE,
createMultiChainValidationModule
} from "../../src/modules"
import { PaymasterMode } from "../../src/paymaster"
import { testOnlyOnOptimism } from "../setupFiles"
Expand Down Expand Up @@ -503,65 +504,74 @@ describe("Account:Write", () => {
}, 60000)
})

describe("Transfer ownership", () => {
test("should transfer ownership of smart account to accountTwo", async () => {
const newOwner = accountTwo.address
const _smartAccount = await createSmartAccountClient({
signer: walletClient,
paymasterUrl,
bundlerUrl,
accountAddress: "0xe6dBb5C8696d2E0f90B875cbb6ef26E3bBa575AC"
})
describe("Transfer ownership", async () => {
const firstOwner = account.address
const newOwner = accountTwo.address
let _smartAccount = await createSmartAccountClient({
signer: walletClient,
paymasterUrl,
bundlerUrl
// accountAddress: "0xe6dBb5C8696d2E0f90B875cbb6ef26E3bBa575AC"
})

const smartAccountAddress = await _smartAccount.getAccountAddress()

test("should transfer ownership of smart account to accountTwo", async () => {
const signerOfAccount = walletClient.account.address
const ownerOfAccount = await publicClient.readContract({
address: "0x0000001c5b32F37F5beA87BDD5374eB2aC54eA8e",
address: DEFAULT_ECDSA_OWNERSHIP_MODULE,
abi: ECDSAModuleAbi,
functionName: "getOwner",
args: [await _smartAccount.getAccountAddress()]
})

expect(ownerOfAccount).toBe(signerOfAccount)
const response = await _smartAccount.transferOwnership(newOwner, {
paymasterServiceData: { mode: PaymasterMode.SPONSORED }
})
const response = await _smartAccount.transferOwnership(
newOwner,
DEFAULT_ECDSA_OWNERSHIP_MODULE,
{
paymasterServiceData: { mode: PaymasterMode.SPONSORED }
}
)
const receipt = await response.wait()
expect(receipt.success).toBe("true")
}, 35000)

test("should revert transfer ownership with signer that is not the owner", async () => {
const newOwner = accountTwo.address
const _smartAccount = await createSmartAccountClient({
_smartAccount = await createSmartAccountClient({
signer: walletClient,
paymasterUrl,
bundlerUrl,
accountAddress: "0xe6dBb5C8696d2E0f90B875cbb6ef26E3bBa575AC"
accountAddress: smartAccountAddress
})

const signerOfAccount = walletClient.account.address
const ownerOfAccount = await publicClient.readContract({
address: "0x0000001c5b32F37F5beA87BDD5374eB2aC54eA8e",
address: DEFAULT_ECDSA_OWNERSHIP_MODULE,
abi: ECDSAModuleAbi,
functionName: "getOwner",
args: [await _smartAccount.getAccountAddress()]
})

expect(ownerOfAccount).not.toBe(signerOfAccount)
expect(
_smartAccount.transferOwnership(newOwner, {
paymasterServiceData: { mode: PaymasterMode.SPONSORED }
})
_smartAccount.transferOwnership(
newOwner,
DEFAULT_ECDSA_OWNERSHIP_MODULE,
{
paymasterServiceData: { mode: PaymasterMode.SPONSORED }
}
)
).rejects.toThrowError()
}, 35000)

test("send an user op with the new owner", async () => {
const _smartAccount = await createSmartAccountClient({
_smartAccount = await createSmartAccountClient({
signer: walletClientTwo,
paymasterUrl,
bundlerUrl,
accountAddress: "0xe6dBb5C8696d2E0f90B875cbb6ef26E3bBa575AC"
accountAddress: smartAccountAddress
})
const newOwner = accountTwo.address
const currentSmartAccountInstanceSigner = await _smartAccount
.getSigner()
.getAddress()
Expand All @@ -582,11 +592,11 @@ describe("Account:Write", () => {
}, 35000)

test("should revert if sending an user op with the old owner", async () => {
const _smartAccount = await createSmartAccountClient({
_smartAccount = await createSmartAccountClient({
signer: walletClient,
paymasterUrl,
bundlerUrl,
accountAddress: "0xe6dBb5C8696d2E0f90B875cbb6ef26E3bBa575AC"
accountAddress: smartAccountAddress
})
const tx = {
to: nftAddress,
Expand All @@ -606,27 +616,30 @@ describe("Account:Write", () => {
}, 35000)

test("should transfer ownership of smart account back to EOA 1", async () => {
const newOwner = account.address
const _smartAccount = await createSmartAccountClient({
_smartAccount = await createSmartAccountClient({
signer: walletClientTwo,
paymasterUrl,
bundlerUrl,
accountAddress: "0xe6dBb5C8696d2E0f90B875cbb6ef26E3bBa575AC" // account address at index 0 of EOA 1
accountAddress: smartAccountAddress
})

const signerOfAccount = walletClientTwo.account.address
const ownerOfAccount = await publicClient.readContract({
address: "0x0000001c5b32F37F5beA87BDD5374eB2aC54eA8e",
address: DEFAULT_ECDSA_OWNERSHIP_MODULE,
abi: ECDSAModuleAbi,
functionName: "getOwner",
args: [await _smartAccount.getAccountAddress()]
})

expect(ownerOfAccount).toBe(signerOfAccount)

const response = await _smartAccount.transferOwnership(newOwner, {
paymasterServiceData: { mode: PaymasterMode.SPONSORED }
})
const response = await _smartAccount.transferOwnership(
firstOwner,
DEFAULT_ECDSA_OWNERSHIP_MODULE,
{
paymasterServiceData: { mode: PaymasterMode.SPONSORED }
}
)
const receipt = await response.wait()
expect(receipt.success).toBe("true")
}, 45000)
Expand Down
17 changes: 11 additions & 6 deletions tests/modules/write.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ describe("Modules:Write", () => {
// Transfer ownership back to walletClient 1
await smartAccountWithOtherOwner.transferOwnership(
walletClient.account.address,
DEFAULT_ECDSA_OWNERSHIP_MODULE,
{ paymasterServiceData: { mode: PaymasterMode.SPONSORED } }
)
}
Expand Down Expand Up @@ -723,13 +724,17 @@ describe("Modules:Write", () => {
Logger.log("Tx Hash: ", transactionDetails.receipt.transactionHash)

// Transfer ownership back to walletClient
const resp = await smartAccount.transferOwnership(newOwner, {
paymasterServiceData: { mode: PaymasterMode.SPONSORED },
params: {
sessionSigner: sessionSigner,
sessionValidationModule: abiSvmAddress
const resp = await smartAccount.transferOwnership(
newOwner,
DEFAULT_ECDSA_OWNERSHIP_MODULE,
{
paymasterServiceData: { mode: PaymasterMode.SPONSORED },
params: {
sessionSigner: sessionSigner,
sessionValidationModule: abiSvmAddress
}
}
})
)
}, 60000)
})
})

0 comments on commit 76be2c0

Please sign in to comment.