Skip to content
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: Safe integration #6

Merged
merged 3 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@
"repositoryURL": "https://github.com/apple/swift-docc-plugin",
"state": {
"branch": null,
"revision": "26ac5758409154cc448d7ab82389c520fa8a8247",
"version": "1.3.0"
"revision": "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64",
"version": "1.4.3"
}
},
{
"package": "SymbolKit",
"repositoryURL": "https://github.com/apple/swift-docc-symbolkit",
"repositoryURL": "https://github.com/swiftlang/swift-docc-symbolkit",
"state": {
"branch": null,
"revision": "b45d1f2ed151d057b54504d653e0da5552844e34",
Expand Down
1 change: 1 addition & 0 deletions Example/WalletApp/ApplicationLayer/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
let entryPointAddress = "0x0000000071727De22E5E9d8BAf0edAc6f37da032" // v0.7 on Sepolia
let chainId = 11155111 // Sepolia
SmartAccount.instance.configure(entryPoint: entryPointAddress, chainId: chainId)
SmartAccountSafe.instance.configure(entryPoint: entryPointAddress, chainId: chainId)
return true
}

Expand Down
94 changes: 91 additions & 3 deletions Example/WalletApp/BusinessLayer/SmartAccount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,100 @@ class SmartAccount {
)
)

let pickedConfig = if !(InputConfig.pimlicoBundlerUrl?.isEmpty ?? true) && !(InputConfig.rpcUrl?.isEmpty ?? true) {
pimlicoSepolia
} else {
localConfig
}

let client = AccountClient(
ownerAddress: owner,
entryPoint: config.entryPoint,
chainId: config.chainId,
config: pickedConfig,
safe: false
)
client.register(privateKey: privateKey)

self.client = client
}


public func getClient() async -> AccountClient {
if let client = client {
return client
}

await withCheckedContinuation { continuation in
self.clientSetContinuation = continuation
}

return client!
}

struct Config {
let entryPoint: String
let chainId: Int
}
}

class SmartAccountSafe {

static var instance = SmartAccountSafe()

private var client: AccountClient? {
didSet {
if let _ = client {
clientSetContinuation?.resume()
}
}
}

private var clientSetContinuation: CheckedContinuation<Void, Never>?

private var config: Config?

private init() {

}

public func configure(entryPoint: String, chainId: Int) {
self.config = Config(
entryPoint: entryPoint,
chainId: chainId
)
}

public func register(owner: String, privateKey: String) {
guard let config = self.config else {
fatalError("Error - you must call SmartAccount.configure(entryPoint:chainId:onSign:) before accessing the shared instance.")
}
assert(owner.count == 40)

let localConfig = YttriumWrapper.Config.local()

let pimlicoBundlerUrl = "https://\(InputConfig.pimlicoBundlerUrl!)"
let rpcUrl = "https://\(InputConfig.rpcUrl!)"
let pimlicoSepolia = YttriumWrapper.Config(
endpoints: .init(
rpc: .init(baseURL: rpcUrl),
bundler: .init(baseURL: pimlicoBundlerUrl),
paymaster: .init(baseURL: pimlicoBundlerUrl)
)
)

let pickedConfig = if !(InputConfig.pimlicoBundlerUrl?.isEmpty ?? true) && !(InputConfig.rpcUrl?.isEmpty ?? true) {
pimlicoSepolia
} else {
localConfig
}

let client = AccountClient(
ownerAddress: owner,
entryPoint: config.entryPoint,
chainId: config.chainId,
config: pimlicoSepolia
// config: localConfig
config: pickedConfig,
safe: true
)
client.register(privateKey: privateKey)

Expand Down Expand Up @@ -100,7 +188,7 @@ class AccountClientMock: YttriumWrapper.AccountClientProtocol {

private var config: Yttrium.Config

required init(ownerAddress: String, entryPoint: String, chainId: Int, config: Yttrium.Config) {
required init(ownerAddress: String, entryPoint: String, chainId: Int, config: Yttrium.Config, safe: Bool) {
self.ownerAddress = ownerAddress
self.entryPoint = entryPoint
self.chainId = chainId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ final class MainModule {
owner: ownerAddress,
privateKey: privateKey
)
SmartAccountSafe.instance.register(
owner: ownerAddress,
privateKey: privateKey
)
// SmartAccount.instance.register(onSign: { (messageToSign: String) in
// func dataToHash(_ data: Data) -> Bytes {
// let prefix = "\u{19}Ethereum Signed Message:\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ final class SettingsPresenter: ObservableObject {
private let accountStorage: AccountStorage
private var disposeBag = Set<AnyCancellable>()
@Published var smartAccount: String = "Loading..."
@Published var smartAccountSafe: String = "Loading..."

init(interactor: SettingsInteractor, router: SettingsRouter, accountStorage: AccountStorage) {
defer { setupInitialState() }
self.interactor = interactor
self.router = router
self.accountStorage = accountStorage
fetchSmartAccount()

fetchSmartAccountSafe()
}

func fetchSmartAccount() {
Task {
do {
Expand All @@ -39,6 +40,26 @@ final class SettingsPresenter: ObservableObject {
private func getSmartAccount() async throws -> String {
try await SmartAccount.instance.getClient().getAccount().absoluteString
}

func fetchSmartAccountSafe() {
Task {
do {
let smartAccount = try await getSmartAccountSafe()
DispatchQueue.main.async {
self.smartAccountSafe = smartAccount
}
} catch {
DispatchQueue.main.async {
self.smartAccountSafe = "Failed to load"
}
print("Failed to get smart account safe: \(error)")
}
}
}

private func getSmartAccountSafe() async throws -> String {
try await SmartAccountSafe.instance.getClient().getAccount().absoluteString
}

var account: String {
guard let importAccount = accountStorage.importAccount else { return .empty }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct SettingsView: View {
header(title: "Account")
row(title: "CAIP-10", subtitle: viewModel.account)
row(title: "Smart Account", subtitle: viewModel.smartAccount)
row(title: "Smart Account Safe", subtitle: viewModel.smartAccountSafe)
row(title: "Private key", subtitle: viewModel.privateKey)
}
.padding(.horizontal, 20)
Expand Down Expand Up @@ -52,6 +53,20 @@ struct SettingsView: View {
.stroke(Color.green, lineWidth: 1)
)
.padding(.bottom, 24)

AsyncButton {
try await sendTransactionSafe()
} label: {
Text("Send Transaction Safe")
.foregroundColor(.green)
.frame(maxWidth: .infinity)
}
.frame(height: 44.0)
.overlay(
RoundedRectangle(cornerRadius: Radius.m)
.stroke(Color.green, lineWidth: 1)
)
.padding(.bottom, 24)

AsyncButton {
try await viewModel.logoutPressed()
Expand Down Expand Up @@ -87,6 +102,16 @@ struct SettingsView: View {
data: "0x68656c6c6f"
))
}

@discardableResult
func sendTransactionSafe() async throws -> String {
let client = await SmartAccountSafe.instance.getClient()
return try await client.sendTransaction(.init(
to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
value: "0",
data: "0x68656c6c6f"
))
}

func header(title: String) -> some View {
HStack {
Expand Down