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: cancel intent order + retry postExecution #21

Merged
merged 1 commit into from
Nov 18, 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
40 changes: 38 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,42 @@ if (isAllowanceValid.ok) {
}
```

## Cancel Intent Order

Active Intent Order can be cancelled using order ID obtained as explained in [Get Intent Order](#get-intent-order).

Example cancel order:

```typescript
import { IntentService, SwapOrder, EvmProvider } from "@balanced/solver-sdk"

const evmProvider = new EvmProvider("0x601020c5797Cdd34f64476b9bf887a353150Cb9a", (window as any).ethereum)
const intentOrder: Result<SwapOrder> = await IntentService.getOrder(
"0xabcdefasdasdsafssadasdsadsadasdsadasdsadsa",
IntentService.getChainConfig("arb").intentContract,
evmProvider,
)

if (intentOrder.ok) {
const cancelResult: Result<string> = await IntentService.cancelIntentOrder(
intentOrder.value.id,
"arb",
evmProvider,
)

if (cancelResult.ok) {
const txHash = cancelResult.value
..
} else {
// handle error
}
} else {
// handle error
}


```

## Get Intent Order

After the Intent order is created (`executeIntentOrder`), the resulting `txHash` can be used to query created on-chain order data.
Expand All @@ -221,10 +257,10 @@ you should invoke `IntentService.getOrder(..)` function.
Example get order:

```typescript
import { IntentService } from "@balanced/solver-sdk"
import { IntentService, SwapOrder, EvmProvider } from "@balanced/solver-sdk"

const evmProvider = new EvmProvider("0x601020c5797Cdd34f64476b9bf887a353150Cb9a", (window as any).ethereum)
const intentOrder: SwapOrder = await IntentService.getOrder(
const intentOrder: Result<SwapOrder> = await IntentService.getOrder(
"0xabcdefasdasdsafssadasdsadsadasdsadasdsadsa",
IntentService.getChainConfig("arb").intentContract,
evmProvider,
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { ChainConfig, ChainName, EvmChainConfig, SuiChainConfig } from "./types.js"

export const DEFAULT_MAX_RETRY = 3
export const DEFAULT_RETRY_DELAY_MS = 2000
export const SOLVER_API_ENDPOINT = "localhost:3000" // TODO

export const chainConfig: Record<ChainName, ChainConfig> = {
Expand Down
30 changes: 30 additions & 0 deletions src/services/EvmIntentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,36 @@ export class EvmIntentService {
}
}

/**
* Cancel EVM intent order
* @param orderId - Intent order ID
* @param chainConfig - EVM chain config
* @param provider - EVM provider
*/
static async cancelIntentOrder(
orderId: bigint,
chainConfig: EvmChainConfig,
provider: EvmProvider,
): Promise<Result<Hash>> {
try {
return {
ok: true,
value: await provider.walletClient.writeContract({
address: chainConfig.intentContract,
abi: intentAbi,
functionName: "cancel",
args: [orderId],
chain: undefined,
}),
}
} catch (e) {
return {
ok: false,
error: e,
}
}
}

/**
* Retrieve Intent order
* @param txHash - Transaction hash
Expand Down
67 changes: 50 additions & 17 deletions src/services/IntentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
type GetChainProviderType,
SwapOrder,
type ChainProvider,
type ChainProviderType,
} from "../entities/index.js"
import { EvmIntentService } from "./EvmIntentService.js"
import { SuiIntentService } from "./SuiIntentService.js"
Expand Down Expand Up @@ -184,23 +185,8 @@ export class IntentService {
provider: GetChainProviderType<T["fromChain"]>,
): Promise<Result<Hash | string>> {
try {
const fromChainConfig = chainConfig[payload.fromChain]

if (!fromChainConfig) {
return {
ok: false,
error: new Error(`Unsupported fromChain: ${payload.fromChain}`),
}
}

const toChainConfig = chainConfig[payload.toChain]

if (!toChainConfig) {
return {
ok: false,
error: new Error(`Unsupported toChain: ${payload.toChain}`),
}
}
const fromChainConfig = IntentService.getChainConfig(payload.fromChain)
const toChainConfig = IntentService.getChainConfig(payload.toChain)

if (isEvmChainConfig(fromChainConfig)) {
if (provider instanceof EvmProvider) {
Expand Down Expand Up @@ -234,6 +220,53 @@ export class IntentService {
}
}

/**
* Cancel active Intent Order
* @param orderId - Intent Order ID (retrievable by getOrder)
* @param chain - Chain on which Order was created on
* @param provider - EVM or SUI provider
* @return string - Transaction Hash
*/
public static async cancelIntentOrder(
orderId: bigint,
chain: ChainName,
provider: ChainProviderType,
): Promise<Result<string>> {
try {
const chainConfig = IntentService.getChainConfig(chain)

if (isEvmChainConfig(chainConfig)) {
if (provider instanceof EvmProvider) {
return EvmIntentService.cancelIntentOrder(orderId, chainConfig, provider)
} else {
return {
ok: false,
error: new Error(`[IntentService.cancelIntentOrder] provider should be of type EvmProvider`),
}
}
} else if (isSuiChainConfig(chainConfig)) {
if (provider instanceof SuiProvider) {
return SuiIntentService.cancelIntentOrder(orderId, chainConfig, provider)
} else {
return {
ok: false,
error: new Error(`[IntentService.cancelIntentOrder] provider should be of type SuiProvider`),
}
}
} else {
return {
ok: false,
error: new Error(`${chain} chain not supported`),
}
}
} catch (e) {
return {
ok: false,
error: e,
}
}
}

/**
* Retrieve Intent order
* @param txHash - Transaction hash
Expand Down
17 changes: 10 additions & 7 deletions src/services/SolverApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
type IntentQuoteResponseRaw,
} from "../types.js"
import invariant from "tiny-invariant"
import { retry } from "../utils.js"

export class SolverApiService {
private constructor() {}
Expand Down Expand Up @@ -94,13 +95,15 @@ export class SolverApiService {
intentExecutionRequest: IntentExecutionRequest,
): Promise<Result<IntentExecutionResponse, IntentErrorResponse>> {
try {
const response = await fetch(`${SOLVER_API_ENDPOINT}/execute`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(intentExecutionRequest),
})
const response = await retry(() =>
fetch(`${SOLVER_API_ENDPOINT}/execute`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(intentExecutionRequest),
}),
)

if (!response.ok) {
return {
Expand Down
47 changes: 47 additions & 0 deletions src/services/SuiIntentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,53 @@ export class SuiIntentService {
}
}

/**
* Cancel SUI intent order
* @param orderId - Intent order ID
* @param chainConfig - SUI chain config
* @param provider - SUI provider
*/
public static async cancelIntentOrder(
orderId: bigint,
chainConfig: SuiChainConfig,
provider: SuiProvider,
): Promise<Result<string>> {
try {
const tx = new Transaction()

tx.moveCall({
target: `${chainConfig.packageId}::main::cancel`,
arguments: [tx.object(chainConfig.storageId), tx.pure.string(orderId.toString())],
})

const signerAccount = provider.account
const chain = signerAccount.chains[0]

if (!chain) {
return {
ok: false,
error: new Error("[SuiIntentService.cancelIntentOrder] Chain undefined in signerAccount"),
}
}

const result = await signAndExecuteTransaction(provider.wallet, {
transaction: tx,
account: provider.account,
chain: provider.account.chains[0]!,
})

return {
ok: true,
value: result.digest,
}
} catch (e) {
return {
ok: false,
error: e,
}
}
}

public static async getNativeCoin(
tx: Transaction,
intent: SwapOrder,
Expand Down
24 changes: 24 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DEFAULT_MAX_RETRY, DEFAULT_RETRY_DELAY_MS } from "./constants.js"

export async function retry<T>(
action: (retryCount: number) => Promise<T>,
retryCount: number = DEFAULT_MAX_RETRY,
delayMs = DEFAULT_RETRY_DELAY_MS,
): Promise<T> {
do {
try {
return await action(retryCount)
} catch (e) {
retryCount--

if (retryCount <= 0) {
console.error(`Failed to perform operation even after ${DEFAULT_MAX_RETRY} attempts.. Throwing origin error..`)
throw e
}
}

await new Promise((resolve) => setTimeout(resolve, delayMs))
} while (retryCount > 0)

throw new Error(`Retry exceeded MAX_RETRY_DEFAULT=${DEFAULT_MAX_RETRY}`)
}