Skip to content

Commit

Permalink
Fix: improvements on express relay sdks and simple searchers (#2045)
Browse files Browse the repository at this point in the history
* feat: Cache mint decimals and use global config from the order

* Improve svm types

* Minor updates on ts package
  • Loading branch information
m30m authored Oct 16, 2024
1 parent c7d5cf0 commit ed98240
Show file tree
Hide file tree
Showing 14 changed files with 677 additions and 619 deletions.
43 changes: 26 additions & 17 deletions express_relay/sdk/js/src/examples/simpleSearcherLimo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,23 @@ import { Keypair, PublicKey, Connection } from "@solana/web3.js";

import * as limo from "@kamino-finance/limo-sdk";
import { Decimal } from "decimal.js";
import { getPdaAuthority } from "@kamino-finance/limo-sdk/dist/utils";
import {
getMintDecimals,
getPdaAuthority,
} from "@kamino-finance/limo-sdk/dist/utils";

const DAY_IN_SECONDS = 60 * 60 * 24;

class SimpleSearcherLimo {
private client: Client;
private connectionSvm: Connection;
private clientLimo: limo.LimoClient;
private readonly connectionSvm: Connection;
private mintDecimals: Record<string, number> = {};
private expressRelayConfig: ExpressRelaySvmConfig | undefined;
constructor(
public endpointExpressRelay: string,
public chainId: string,
private searcher: Keypair,
public endpointSvm: string,
public globalConfig: PublicKey,
public fillRate: number,
public apiKey?: string
) {
Expand All @@ -42,7 +44,6 @@ class SimpleSearcherLimo {
this.bidStatusHandler.bind(this)
);
this.connectionSvm = new Connection(endpointSvm, "confirmed");
this.clientLimo = new limo.LimoClient(this.connectionSvm, globalConfig);
}

async bidStatusHandler(bidStatus: BidStatusUpdate) {
Expand All @@ -59,13 +60,27 @@ class SimpleSearcherLimo {
);
}

async getMintDecimalsCached(mint: PublicKey): Promise<number> {
const mintAddress = mint.toBase58();
if (this.mintDecimals[mintAddress]) {
return this.mintDecimals[mintAddress];
}
const decimals = await getMintDecimals(this.connectionSvm, mint);
this.mintDecimals[mintAddress] = decimals;
return decimals;
}

async generateBid(opportunity: OpportunitySvm) {
const order = opportunity.order;
const inputMintDecimals = await this.clientLimo.getOrderInputMintDecimals(
order
const limoClient = new limo.LimoClient(
this.connectionSvm,
order.state.globalConfig
);
const outputMintDecimals = await this.clientLimo.getOrderOutputMintDecimals(
order
const inputMintDecimals = await this.getMintDecimalsCached(
order.state.inputMint
);
const outputMintDecimals = await this.getMintDecimalsCached(
order.state.outputMint
);
const inputAmountDecimals = new Decimal(
order.state.remainingInputAmount.toNumber()
Expand Down Expand Up @@ -96,7 +111,7 @@ class SimpleSearcherLimo {
outputAmountDecimals.toString()
);

const ixsTakeOrder = await this.clientLimo.takeOrderIx(
const ixsTakeOrder = await limoClient.takeOrderIx(
this.searcher.publicKey,
order,
inputAmountDecimals,
Expand All @@ -107,7 +122,7 @@ class SimpleSearcherLimo {
const txRaw = new anchor.web3.Transaction().add(...ixsTakeOrder);

const router = getPdaAuthority(
this.clientLimo.getProgramID(),
limoClient.getProgramID(),
order.state.globalConfig
);
const bidAmount = new anchor.BN(argv.bid);
Expand Down Expand Up @@ -174,11 +189,6 @@ const argv = yargs(hideBin(process.argv))
type: "string",
demandOption: true,
})
.option("global-config", {
description: "Global config address",
type: "string",
demandOption: true,
})
.option("bid", {
description: "Bid amount in lamports",
type: "string",
Expand Down Expand Up @@ -242,7 +252,6 @@ async function run() {
argv.chainId,
searcherKeyPair,
argv.endpointSvm,
new PublicKey(argv.globalConfig),
argv.fillRate,
argv.apiKey
);
Expand Down
3 changes: 2 additions & 1 deletion express_relay/sdk/js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import * as evm from "./evm";
import * as svm from "./svm";

export * from "./types";
export * from "./const";

export class ClientError extends Error {}

Expand Down Expand Up @@ -400,7 +401,7 @@ export class Client {
* @param opportunity
* @returns Opportunity in the converted client format
*/
private convertOpportunity(
public convertOpportunity(
opportunity: components["schemas"]["Opportunity"]
): Opportunity | undefined {
if (opportunity.version !== "v1") {
Expand Down
59 changes: 34 additions & 25 deletions express_relay/sdk/js/src/serverTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export interface components {
* @example beedbeed-58cc-4372-a567-0e02b2c3d479
*/
id: string;
/**
* @description The status of the request. If the bid was placed successfully, the status will be "OK".
* @example OK
*/
status: string;
};
BidStatus:
Expand Down Expand Up @@ -464,11 +468,6 @@ export interface components {
| components["schemas"]["SimulatedBidSvm"];
/** BidResponseEvm */
SimulatedBidEvm: {
/**
* @description Amount of bid in wei.
* @example 10
*/
bid_amount: string;
/**
* @description The chain id for bid.
* @example op_sepolia
Expand All @@ -484,23 +483,28 @@ export interface components {
* @example 2024-05-23T21:26:57.329954Z
*/
initiation_time: string;
/**
* @description The permission key for bid.
* @example 0xdeadbeef
*/
permission_key: string;
/**
* @description The profile id for the bid owner.
* @example
* @example obo3ee3e-58cc-4372-a567-0e02b2c3d479
*/
profile_id: string;
status: components["schemas"]["BidStatus"];
} & {
/**
* @description Amount of bid in wei.
* @example 10
*/
bid_amount: string;
/**
* @description The gas limit for the contract call.
* @example 2000000
*/
gas_limit: string;
/**
* @description The permission key for bid.
* @example 0xdeadbeef
*/
permission_key: string;
status: components["schemas"]["BidStatusEvm"];
/**
* @description Calldata for the contract call.
* @example 0xdeadbeef
Expand All @@ -514,11 +518,6 @@ export interface components {
};
/** BidResponseSvm */
SimulatedBidSvm: {
/**
* @description Amount of bid in wei.
* @example 10
*/
bid_amount: string;
/**
* @description The chain id for bid.
* @example op_sepolia
Expand All @@ -534,18 +533,25 @@ export interface components {
* @example 2024-05-23T21:26:57.329954Z
*/
initiation_time: string;
/**
* @description The permission key for bid.
* @example 0xdeadbeef
*/
permission_key: string;
/**
* @description The profile id for the bid owner.
* @example
* @example obo3ee3e-58cc-4372-a567-0e02b2c3d479
*/
profile_id: string;
status: components["schemas"]["BidStatus"];
} & {
/**
* Format: int64
* @description Amount of bid in lamports.
* @example 1000
*/
bid_amount: number;
/**
* @description The permission key for bid in base64 format.
* This is the concatenation of the permission account and the router account.
* @example DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5
*/
permission_key: string;
status: components["schemas"]["BidStatusSvm"];
/**
* @description The transaction of the bid.
* @example SGVsbG8sIFdvcmxkIQ==
Expand Down Expand Up @@ -591,6 +597,10 @@ export interface components {
* @example beedbeed-58cc-4372-a567-0e02b2c3d479
*/
id: string;
/**
* @description The status of the request. If the bid was placed successfully, the status will be "OK".
* @example OK
*/
status: string;
};
};
Expand Down Expand Up @@ -686,7 +696,6 @@ export interface operations {
};
};
responses: {
/** @description Latest status of the bid */
200: {
content: {
"application/json": components["schemas"]["BidStatus"];
Expand Down
24 changes: 21 additions & 3 deletions express_relay/sdk/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,28 @@ $ poetry add express-relay

## Quickstart

To run the simple searcher script, navigate to `python/` and run
To run the simple searcher script, navigate to `python/` and run the following command:

### Evm

```
$ poetry run python3 -m express_relay.searcher.examples.simple_searcher --private-key <PRIVATE_KEY_HEX_STRING> --chain-id development --verbose --server-url https://per-staging.dourolabs.app/
$ poetry run python3 -m express_relay.searcher.examples.simple_searcher_evm \
--private-key <PRIVATE_KEY_HEX_STRING> \
--chain-id development \
--verbose \
--server-url https://per-staging.dourolabs.app/
```

This simple example runs a searcher that queries the Express Relay liquidation server for available liquidation opportunities and naively submits a bid on each available opportunity.
This simple example runs a searcher that queries the Express Relay liquidation server for available liquidation
opportunities and naively submits a bid on each available opportunity.

### Svm

```
$ poetry run python3 -m express_relay.searcher.examples.simple_searcher_svm \
--endpoint-express-relay https://per-staging.dourolabs.app/ \
--chain-id development-solana \
--private-key-json-file <PATH_TO_JSON_FILE> \
--endpoint-svm https://api.mainnet-beta.solana.com \
--bid 10000000 # Bid amount in lamports
```
50 changes: 28 additions & 22 deletions express_relay/sdk/python/express_relay/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import json
import urllib.parse
import warnings
from asyncio import Task
from collections.abc import Coroutine
from datetime import datetime
Expand All @@ -10,7 +11,7 @@
import httpx
import web3
import websockets
from eth_abi import encode
from eth_abi.abi import encode
from eth_account.account import Account
from eth_account.datastructures import SignedMessage
from eth_utils import to_checksum_address
Expand All @@ -25,27 +26,33 @@
EXECUTION_PARAMS_TYPESTRING,
SVM_CONFIGS,
)
from express_relay.express_relay_types import (
BidResponse,
Opportunity,
BidStatusUpdate,
ClientMessage,
Bid,
OpportunityBid,
OpportunityParams,
from express_relay.models.evm import (
Address,
Bytes32,
TokenAmount,
OpportunityBidParams,
BidEvm,
)
from express_relay.models import (
Bid,
BidStatusUpdate,
BidResponse,
OpportunityBidParams,
OpportunityBid,
OpportunityParams,
Opportunity,
OpportunityRoot,
OpportunityEvm,
ClientMessage,
BidResponseRoot,
)
from express_relay.svm.generated.express_relay.instructions import submit_bid
from express_relay.models.base import UnsupportedOpportunityVersionException
from express_relay.models.evm import OpportunityEvm
from express_relay.svm.generated.express_relay.instructions.submit_bid import submit_bid
from express_relay.svm.generated.express_relay.program_id import (
PROGRAM_ID as SVM_EXPRESS_RELAY_PROGRAM_ID,
)
from express_relay.svm.generated.express_relay.types import SubmitBidArgs
from express_relay.svm.generated.express_relay.types.submit_bid_args import (
SubmitBidArgs,
)
from express_relay.svm.limo_client import LimoClient


Expand Down Expand Up @@ -334,11 +341,10 @@ async def ws_handler(

elif msg_json.get("type") == "bid_status_update":
if bid_status_callback is not None:
bid_status_update = BidStatusUpdate.process_bid_status_dict(
bid_status_update = BidStatusUpdate.model_validate(
msg_json["status"]
)
if bid_status_update:
asyncio.create_task(bid_status_callback(bid_status_update))
asyncio.create_task(bid_status_callback(bid_status_update))

elif msg_json.get("id"):
future = self.ws_msg_futures.pop(msg_json["id"])
Expand Down Expand Up @@ -369,10 +375,11 @@ async def get_opportunities(self, chain_id: str | None = None) -> list[Opportuni

opportunities: list[Opportunity] = []
for opportunity in resp.json():
opportunity_processed = OpportunityRoot.model_validate(opportunity)
if opportunity_processed:
try:
opportunity_processed = OpportunityRoot.model_validate(opportunity)
opportunities.append(opportunity_processed.root)

except UnsupportedOpportunityVersionException as e:
warnings.warn(str(e))
return opportunities

async def submit_opportunity(self, opportunity: OpportunityParams) -> UUID:
Expand Down Expand Up @@ -419,9 +426,8 @@ async def get_bids(self, from_time: datetime | None = None) -> list[BidResponse]

bids = []
for bid in resp.json()["items"]:
bid_processed = BidResponse.process_bid_response_dict(bid)
if bid_processed:
bids.append(bid_processed)
bid_processed = BidResponseRoot.model_validate(bid)
bids.append(bid_processed.root)

return bids

Expand Down
2 changes: 1 addition & 1 deletion express_relay/sdk/python/express_relay/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from solders.pubkey import Pubkey

from express_relay.express_relay_types import OpportunityAdapterConfig
from express_relay.models import OpportunityAdapterConfig

OPPORTUNITY_ADAPTER_CONFIGS = {
"op_sepolia": OpportunityAdapterConfig(
Expand Down
Loading

0 comments on commit ed98240

Please sign in to comment.