Skip to content

Commit

Permalink
feat(relay-kit): Improve general structure and follow other packs sty…
Browse files Browse the repository at this point in the history
…le (#534)
  • Loading branch information
yagopv authored Sep 21, 2023
1 parent 873c0d6 commit 3643a51
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 193 deletions.
130 changes: 59 additions & 71 deletions packages/account-abstraction-kit/src/AccountAbstraction.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
import { AccountAbstractionConfig } from '@safe-global/account-abstraction-kit-poc/types'
import Safe, {
EthersAdapter,
SafeAccountConfig,
predictSafeAddress
} from '@safe-global/protocol-kit'
import { RelayPack } from '@safe-global/relay-kit'
import { MetaTransactionData, MetaTransactionOptions } from '@safe-global/safe-core-sdk-types'
import { ethers } from 'ethers'

import Safe, { SafeAccountConfig, predictSafeAddress } from '@safe-global/protocol-kit'
import { RelayKitBasePack } from '@safe-global/relay-kit'
import {
MetaTransactionData,
MetaTransactionOptions,
EthAdapter
} from '@safe-global/safe-core-sdk-types'

/**
* @class
* This class helps to abstract the Account Abstraction logic required to interact with the Safe contracts using our Kits
*/
class AccountAbstraction {
#ethAdapter: EthersAdapter
#signer: ethers.Signer
#safeSdk?: Safe
#relayPack?: RelayPack

constructor(signer: ethers.Signer) {
if (!signer.provider) {
throw new Error('Signer must be connected to a provider')
}
this.#signer = signer
this.#ethAdapter = new EthersAdapter({
ethers,
signerOrProvider: this.#signer
})
protocolKit!: Safe
relayKit?: RelayKitBasePack
#ethAdapter: EthAdapter

/**
* @constructor
* @param ethAdapter The EthAdapter instance to be used by the Account Abstraction (e.g. EthersAdapter)
*/
constructor(ethAdapter: EthAdapter) {
this.#ethAdapter = ethAdapter
}

async init(options: AccountAbstractionConfig) {
const { relayPack } = options
this.setRelayPack(relayPack)
#initializeProtocolKit = async () => {
const signer = await this.#ethAdapter.getSignerAddress()

if (!signer) {
throw new Error("There's no signer in the provided EthAdapter")
}

const signer = await this.getSignerAddress()
const owners = [signer]
const threshold = 1

Expand All @@ -46,70 +46,58 @@ class AccountAbstraction {
const isSafeDeployed = await this.#ethAdapter.isContractDeployed(safeAddress)

if (isSafeDeployed) {
this.#safeSdk = await Safe.create({ ethAdapter: this.#ethAdapter, safeAddress })
this.protocolKit = await Safe.create({ ethAdapter: this.#ethAdapter, safeAddress })
} else {
this.#safeSdk = await Safe.create({
this.protocolKit = await Safe.create({
ethAdapter: this.#ethAdapter,
predictedSafe: { safeAccountConfig }
})
}
}

setRelayPack(relayPack: RelayPack) {
this.#relayPack = relayPack
}

async getSignerAddress(): Promise<string> {
const signerAddress = await this.#signer.getAddress()
return signerAddress
}

async getNonce(): Promise<number> {
if (!this.#safeSdk) {
throw new Error('SDK not initialized')
}

return this.#safeSdk.getNonce()
}

async getSafeAddress(): Promise<string> {
if (!this.#safeSdk) {
throw new Error('SDK not initialized')
}

return this.#safeSdk.getAddress()
/**
* Initialize the AccountAbstraction instance with the safe address or the predicted safe address
* The current implementation only works for a single owner Safe with threshold 1. This will be improved in the future
*/
async init() {
await this.#initializeProtocolKit()
}

async isSafeDeployed(): Promise<boolean> {
if (!this.#safeSdk) {
throw new Error('SDK not initialized')
}

return this.#safeSdk.isSafeDeployed()
/**
* Use this method to set the Relay Pack instance to be used by the AccountAbstraction instance
* It's mandatory to set the instance before using the relayTransaction() method
* @param relayPack The RelayPack instance to be used by the AccountAbstraction instance (e.g. GelatoRelayPack)
*/
setRelayKit(relayPack: RelayKitBasePack) {
this.relayKit = relayPack
}

/**
* Use this method to relay a transaction using the Relay Pack instance set in the AccountAbstraction instance
* @param transactions The list of transactions to be relayed
* @param options The transaction options
* @returns The result of the relay transaction execution (e.g. taskId in the case of Gelato)
*/
async relayTransaction(
transactions: MetaTransactionData[],
options?: MetaTransactionOptions
): Promise<string> {
if (!this.#relayPack || !this.#safeSdk) {
throw new Error('SDK not initialized')
): Promise<unknown> {
if (!this.protocolKit) {
throw new Error('protocolKit not initialized. Call init() first')
}

const relayedTransaction = await this.#relayPack.createRelayedTransaction({
safe: this.#safeSdk,
if (!this.relayKit) {
throw new Error('relayKit not initialized. Call setRelayKit(pack) first')
}

const relayedTransaction = await this.relayKit.createRelayedTransaction({
transactions,
options
})

const signedSafeTransaction = await this.#safeSdk.signTransaction(relayedTransaction)

const response = await this.#relayPack.executeRelayTransaction(
signedSafeTransaction,
this.#safeSdk
)
const signedSafeTransaction = await this.protocolKit.signTransaction(relayedTransaction)

return response.taskId
return await this.relayKit.executeRelayTransaction(signedSafeTransaction)
}
}

Expand Down
1 change: 0 additions & 1 deletion packages/account-abstraction-kit/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import AccountAbstraction from './AccountAbstraction'

export default AccountAbstraction
export * from './types'
5 changes: 0 additions & 5 deletions packages/account-abstraction-kit/src/types/index.ts

This file was deleted.

48 changes: 48 additions & 0 deletions packages/relay-kit/src/RelayKitBasePack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Safe from '@safe-global/protocol-kit'
import { MetaTransactionOptions, SafeTransaction } from '@safe-global/safe-core-sdk-types'

import { RelayKitTransaction } from './types'

export abstract class RelayKitBasePack {
protocolKit: Safe

/**
* The packs implemented using our SDK should extend this class and therefore provide a Safe SDK instance
* @constructor
* @param protocolKit The Safe SDK instance
*/
constructor(protocolKit: Safe) {
this.protocolKit = protocolKit
}

/**
* Get an estimation of the fee that will be paid for a transaction
* @param chainId The chain id
* @param gasLimit Max amount of gas willing to consume
* @param gasToken Token address (or 0 if ETH) that is used for the payment
*/
abstract getEstimateFee(chainId: number, gasLimit: string, gasToken?: string): Promise<string>

/**
* Creates a Safe transaction designed to be executed using the relayer.
* @param {RelayKitTransaction} RelayKitTransaction - Properties required to create the transaction.
* @returns {Promise<SafeTransaction>} - Returns a Promise that resolves with a SafeTransaction object.
*/
abstract createRelayedTransaction({
transactions,
options,
onlyCalls
}: RelayKitTransaction): Promise<SafeTransaction>

/**
* Sends the Safe transaction to the relayer for execution.
* If the Safe is not deployed, it creates a batch of transactions including the Safe deployment transaction.
* @param {SafeTransaction} safeTransaction - The Safe transaction to be executed
* @param {MetaTransactionOptions} options - The transaction options
* @returns {Promise<RelayResponse>} Returns a Promise that resolves with a RelayResponse object.
*/
abstract executeRelayTransaction(
safeTransaction: SafeTransaction,
options?: MetaTransactionOptions
): Promise<unknown>
}
3 changes: 3 additions & 0 deletions packages/relay-kit/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from './packs/gelato/GelatoRelayPack'
export * from './packs/gelato/types'

export * from './RelayKitBasePack'
export * from './types'
Loading

0 comments on commit 3643a51

Please sign in to comment.