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

Chain Abstraction: add execute method #280

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 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
88 changes: 24 additions & 64 deletions docs/walletkit/android/experimental/chain-abstraction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ Apps need to pass `gas` as null, while sending a transaction to allow proper gas
When sending a transaction, you need to:
1. Check if the required chain has enough funds to complete the transaction
2. If not, use the `prepare` method to generate necessary bridging transactions
3. Check the status of the bridging transactions and wait for them to be completed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it's worth to mention: "Sign routing and initial transaction"

4. Once the bridging transactions are completed, execute the initial transaction
3. Sign routing and initial transaction hashes, prepared by the prepare method
4. Use `execute` method to broadcast routing and initial transactions and wait for it to be completed

The following sequence diagram illustrates the complete flow of a chain abstraction operation, from the initial dapp request to the final transaction confirmation

![Chain Abstraction Flow](/assets/chain-abstraction-flow.png)
![Chain Abstraction Flow](/assets/chain-abstraction-sequence.png)

## Methods

Expand All @@ -42,83 +42,47 @@ fun prepare(
)
```

### Status
### Execute

This method is used to check the status of the fulfillment operation. It will return a `Status.Completed` object if the operation completed successfully or a `Status.Error` object if it encountered an error.
This method is used to execute the chain abstraction operation. It broadcasts the bridging and initial transactions and waits for them to be completed.
The method returns a `ExecuteSuccess` object with the transaction hash and receipt.

```kotlin
@ChainAbstractionExperimentalApi
fun status(
fulfilmentId: String,
checkIn: Long,
onSuccess: (Wallet.Model.Status.Completed) -> Unit,
onError: (Wallet.Model.Status.Error) -> Unit
)
```

### Estimate Fees

This method is used to estimate the fees for the fulfillment operation.

```kotlin
@Throws(Exception::class)
@ChainAbstractionExperimentalApi
fun estimateFees(chainId: String): Wallet.Model.EstimatedFees
```

### Get ERC20 Balance

This method is used to get the balance of an ERC20 token on a specific chain.

```kotlin
@Throws(Exception::class)
@ChainAbstractionExperimentalApi
fun getERC20Balance(chainId: String, tokenAddress: String, ownerAddress: String): String
```

### Get Fulfilment Details

This method is used to get the details of the fulfillment operation.

```kotlin
@ChainAbstractionExperimentalApi
fun getTransactionsDetails(
available: Wallet.Model.PrepareSuccess.Available,
onSuccess: (Wallet.Model.TransactionsDetails) -> Unit,
fun execute(
prepareAvailable: Wallet.Model.PrepareSuccess.Available,
prepareSignedTxs: List<String>,
initSignedTx: String,
onSuccess: (Wallet.Model.ExecuteSuccess) -> Unit,
onError: (Wallet.Model.Error) -> Unit
)
```

### Usage

When sending a transaction, first check if chain abstraction is needed using the `prepare` method. If it is needed, you must sign all the fulfillment transactions and broadcast them in parallel.
After that, you need to call the `status` method to check the status of the fulfillment operation.
When sending a transaction, first check if chain abstraction is needed using the `prepare` method. If it is needed, you must sign all the fulfillment transactions and use the `execute` method.

If the operation is successful, broadcast the initial transaction and await the transaction hash and receipt.
If the operation is successful, use `execute` method and await the transaction hash and receipt.
If the operation is unsuccessful, send the JsonRpcError to the dapp and display the error to the user.

```kotlin
val initialTransaction = Wallet.Model.Transaction(...)

WalletKit.prepare(
WalletKit.ChainAbstraction.prepare(
initialTransaction,
onSuccess = { prepareSuccess ->
when (prepareSuccess) {
is Wallet.Model.PrepareSuccess.Available -> {
//Sign all the fulfilment transactions
//Broadcast the fulfilment transactions in parallel
//Await for the transaction hashes and receipts
//Sign all the fulfilment transactions and init transaction
//Use execute method

//Call the status
WalletKit.status(fulfilmentId, checkIn,
//Call the execute
WalletKit.ChainAbstraction.execute(prepareSuccess, prepareSignedTxs, initSignedTx
onSuccess = {
//Successfull filfilment operation
//Broadcast the inittial transaction
//Await for the tx hash ande receipt
//The execution of the Chain Abstraction is successfull
//Send the response to the Dapp
},
onError = {
//Status error - wallet should send the JsonRpcError to a dapp for given request and display error to the user
//Execute error - wallet should send the JsonRpcError to a dapp for given request and display error to the user
}
)
}
Expand Down Expand Up @@ -182,12 +146,13 @@ data class FeeEstimatedTransaction(

sealed class PrepareSuccess {
data class Available(
val fulfilmentId: String,
val orchestratorId: String,
val checkIn: Long,
val transactions: List<Transaction>,
val initialTransaction: Transaction,
val initialTransactionMetadata: InitialTransactionMetadata,
val funding: List<FundingMetadata>
val funding: List<FundingMetadata>,
val transactionsDetails: TransactionsDetails
) : PrepareSuccess()

data class NotRequired(val initialTransaction: Transaction) : PrepareSuccess()
Expand Down Expand Up @@ -217,13 +182,8 @@ sealed class PrepareError : Model() {
data class Unknown(val message: String) : PrepareError()
}

sealed class Status : Model() {
data class Completed(val createdAt: Long) : Status()
data class Error(val reason: String) : Status()
}

data class TransactionsDetails(
var fulfilmentDetails: List<TransactionDetails>,
var details: List<TransactionDetails>,
var initialDetails: TransactionDetails,
var bridgeFees: List<TransactionFee>,
var localBridgeTotal: Amount,
Expand Down
100 changes: 34 additions & 66 deletions docs/walletkit/ios/experimental/chain-abstraction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ Apps need to pass `gas` as null, while sending a transaction to allow proper gas
When sending a transaction, you need to:
1. Check if the required chain has enough funds to complete the transaction
2. If not, use the `prepare` method to generate necessary bridging transactions
3. Check the status of the bridging transactions and wait for them to be completed
4. Once the bridging transactions are completed, execute the initial transaction
3. Sign routing and initial transaction hashes, prepared by the prepare method
4. Invoke `execute` method to broadcast routing and initial transactions and wait for it to be completed

The following sequence diagram illustrates the complete flow of a chain abstraction operation, from the initial dapp request to the final transaction confirmation

![Chain Abstraction Flow](/assets/chain-abstraction-flow.png)
![Chain Abstraction Flow](/assets/chain-abstraction-sequence.png)

## Methods

Expand All @@ -39,88 +39,56 @@ public func prepare(transaction: InitialTransaction) async throws -> PrepareResp
}
```

### Wait For Success With Timeout
### Execute

This method is used to wait for all transactions to complete successfully. It will return a response with the transaction hash and receipt if the operation completed successfully.
If the operation did not complete successfully, it will throw an error.
This method is used to execute the chain abstraction operation. The method will handle broadcasting all transactions in the correct order and monitor the cross-chain transfer process. It returns an `ExecuteDetails` object with the transaction status and results.

```swift
@available(*, message: "This method is experimental. Use with caution.")
public func waitForSuccessWithTimeout(orchestrationId: String, checkIn: UInt64, timeout: UInt64 = 180) async throws -> StatusResponseCompleted {
}
```

### Estimate Fees

This method is used to estimate fees on a specific chain.

```swift
@available(*, message: "This method is experimental. Use with caution.")
public func estimateFees(chainId: String) async throws -> Eip1559Estimation {
}
```

### Get UI Fields

Get UI fields that you can use to display like estimated fees in user's local currency for specific chain.

```swift
@available(*, message: "This method is experimental. Use with caution.")
public func getUIFields(chainId: String) async throws -> UIFields {
public func execute(uiFields: UiFields, routeTxnSigs: [FfiPrimitiveSignature], initialTxnSig: FfiPrimitiveSignature) async throws -> ExecuteDetails {
}
```

## Usage

When sending a transaction, first check if chain abstraction is needed using the `prepare` method. If it is needed, you need to sign all the fulfillment transactions and broadcast them in parallel.
After that, you need to call the `waitForSuccessWithTimeout` method to check the status of the fulfillment operation.
When sending a transaction, first check if chain abstraction is needed using the `prepare` method. Call the `execute` method to broadcast the routing and initial transactions and wait for it to be completed.

If the operation is successful, you need to broadcast the initial transaction and await the transaction hash and receipt.
If the operation is not successful, you need to send a JsonRpcError to the dapp and display the error to the user.
If the operation is not successful, send a JsonRpcError to the dapp and display the error to the user.

```swift
guard request.method == "eth_sendTransaction" else {
return
}

let tx = try request.params.get([Tx].self)[0]

let transaction = InitialTransaction(
chainId: request.chainId.absoluteString
from: tx.from,
to: tx.to,
value: "0",
input: tx.data,
let routeResponseSuccess = try await WalletKit.instance.ChainAbstraction.prepare(
chainId: selectedNetwork.chainId.absoluteString,
from: myAccount.address,
call: call,
localCurrency: .usd
)

let routeResponseSuccess = try await WalletKit.instance.prepare(transaction: transaction)

switch routeResponseSuccess {
case .success(let routeResponseSuccess):
switch routeResponseSuccess {
case .available(let routeResponseAvailable):
// sign and broadcast routing transactions and then the initial transaction
case .notRequired(let routeResponseNotRequired):
case .success(let routeResponse):
switch routeResponse {
case .available(let UiFileds):
// If the route is available, present a CA transaction flow
for txnDetails in uiFields.route {
let hash = txnDetails.transactionHashToSign
let sig = try! signer.signHash(hash)
routeTxnSigs.append(sig)
}

// sign initial transaction hash
let initialTxHash = uiFields.initial.transactionHashToSign
let initialTxnSig = try! signer.signHash(initialTxHash)

let executeDetails = try await WalletKit.instance.ChainAbstraction.execute(uiFields: uiFields, routeTxnSigs: routeTxnSigs, initialTxnSig: initialTxnSig)

case .notRequired:
// user does not need to move funds from other chains, sign and broadcast original transaction
case .error(let routeResponseError):
// error has occurred, respond with an error to a dapp

}
```

After broadcasting the transactions, use the `waitForSuccessWithTimeout` method to monitor their completion. Once all transactions are successful, you can proceed with broadcasting the initial transaction.

```swift
let orchestrationId = routeResponseAvailable.orchestrationId
print("📋 Orchestration ID: \(orchestrationId)")

print("🔄 Starting status checking...")

let completed = try await WalletKit.instance.waitForSuccessWithTimeout(orchestrationId: orchestrationId, checkIn: 5, timeout: 180)
print("✅ Routing Transactions completed successfully!")
print("📊 Completion details: \(completed)")
case .error(let routeResponseError):
// Show an error
}

print("🚀 Initiating initial transaction...")
try await sendInitialTransaction(initialTransaction: routeResponseAvailable.initialTransaction)
```

For example, check out implementation of chain abstraction in [sample wallet](https://github.com/reown-com/reown-swift/blob/develop/Example/WalletApp/PresentationLayer/Wallet/CATransactionModal/CATransactionPresenter.swift) with Swift.
Expand Down
Binary file modified static/assets/chain-abstraction-demo.mp4
Binary file not shown.
Binary file added static/assets/chain-abstraction-sequence.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading