Skip to content

Commit

Permalink
limonade: bump express-relay sdk and remove direct http checks (#264)
Browse files Browse the repository at this point in the history
* do something

* send requests concurrently

* add-rule

* check

* pr comments
  • Loading branch information
guibescos authored Nov 28, 2024
1 parent 1c60ce2 commit f79f56e
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 105 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@ module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
parserOptions: {
project: "./tsconfig.json",
},
rules: {
"@typescript-eslint/no-misused-promises": "error",
},
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
};
12 changes: 6 additions & 6 deletions scripts/limonade/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions scripts/limonade/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "limonade",
"version": "0.2.3",
"version": "0.2.4",
"description": "This script is used to submit new opportunities fetched from the limo program to the express relay.",
"main": "index.js",
"scripts": {
Expand All @@ -22,7 +22,7 @@
"@coral-xyz/anchor": "^0.30.1",
"@coral-xyz/anchor-cli": "^0.30.1",
"@kamino-finance/limo-sdk": "^0.7.1",
"@pythnetwork/express-relay-js": "^0.15.0",
"@pythnetwork/express-relay-js": "^0.15.1",
"@solana/web3.js": "^1.95.3",
"axios": "^1.7.3",
"bs58": "^6.0.0",
Expand Down
161 changes: 64 additions & 97 deletions scripts/limonade/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import yargs from "yargs";
import {
ChainType,
Client,
ClientError,
OpportunityCreate,
} from "@pythnetwork/express-relay-js";
import { getPdaAuthority } from "@kamino-finance/limo-sdk/dist/utils";
Expand Down Expand Up @@ -46,6 +47,11 @@ const argv = yargs(hideBin(process.argv))
type: "string",
demandOption: true,
})
.option("number-of-concurrent-submissions", {
description: "Number of concurrent submissions to the express relay server",
type: "number",
default: 100,
})
.help()
.alias("help", "h")
.parseSync();
Expand All @@ -54,6 +60,7 @@ async function run() {
const connection = new Connection(argv.rpcEndpoint);

const globalConfig = new PublicKey(argv.globalConfig);
const numberOfConcurrentSubmissions = argv.numberOfConcurrentSubmissions;
const filters: GetProgramAccountsFilter[] = [
{
memcmp: {
Expand All @@ -72,6 +79,16 @@ async function run() {
console.log("Listening for program account changes");
const client = new Client({ baseUrl: argv.endpoint, apiKey: argv.apiKey });

const handleSubmitError = (e: unknown) => {
if (
!(
e instanceof ClientError &&
e.message.includes("Same opportunity is submitted recently")
)
) {
console.error("Failed to submit opportunity", e);
}
};
const submitExistingOpportunities = async () => {
const response = await connection.getProgramAccounts(limoId, {
commitment: "confirmed",
Expand Down Expand Up @@ -100,55 +117,63 @@ async function run() {
);

console.log("Resubmitting opportunities", payloads.length);
for (const payload of payloads) {
try {
await client.submitOpportunity(payload);
} catch (e) {
console.error(e);
}
for (let i = 0; i < payloads.length; i += numberOfConcurrentSubmissions) {
const batch = payloads.slice(i, i + numberOfConcurrentSubmissions);
await Promise.all(
batch.map(async (payload) => {
try {
await client.submitOpportunity(payload);
} catch (e) {
handleSubmitError(e);
}
})
);
}
};

connection.onProgramAccountChange(
limoId,
async (info, context) => {
const order = Order.decode(info.accountInfo.data);
if (order.remainingInputAmount.toNumber() === 0) {
const router = getPdaAuthority(limoId, globalConfig);
(info, context) => {
async function handleUpdate() {
const order = Order.decode(info.accountInfo.data);
if (order.remainingInputAmount.toNumber() === 0) {
const router = getPdaAuthority(limoId, globalConfig);

try {
await client.removeOpportunity({
chainType: ChainType.SVM,
program: "limo",
chainId: argv.chainId,
permissionAccount: info.accountId,
router,
});
} catch (e) {
console.error("Failed to remove opportunity", e);
}
return;
}
console.log(
"Fetched order with address:",
info.accountId.toBase58(),
"slot:",
context.slot
);

const payload: OpportunityCreate = {
program: "limo",
chainId: argv.chainId,
slot: context.slot,
order: { state: order, address: info.accountId },
};

try {
await client.removeOpportunity({
chainType: ChainType.SVM,
program: "limo",
chainId: argv.chainId,
permissionAccount: info.accountId,
router,
});
await client.submitOpportunity(payload);
lastChange[info.accountId.toBase58()] = Date.now();
} catch (e) {
console.error("Failed to remove opportunity", e);
handleSubmitError(e);
}
return;
}
console.log(
"Fetched order with address:",
info.accountId.toBase58(),
"slot:",
context.slot
);

const payload: OpportunityCreate = {
program: "limo",
chainId: argv.chainId,
slot: context.slot,
order: { state: order, address: info.accountId },
};

try {
await client.submitOpportunity(payload);
lastChange[info.accountId.toBase58()] = Date.now();
} catch (e) {
console.error("Failed to submit opportunity", e);
}
handleUpdate().catch(console.error);
},
{
commitment: "processed",
Expand All @@ -165,65 +190,7 @@ async function run() {
}
};

const RPC_HEALTH_CHECK_SECONDS_THRESHOLD = 300;
const checkRpcHealth = async () => {
//eslint-disable-next-line no-constant-condition
while (true) {
try {
const slot = await connection.getSlot("finalized");
const blockTime = await connection.getBlockTime(slot);
const timeNow = Date.now() / 1000;
if (blockTime === null) {
console.error(
`Health Error (RPC endpoint): unable to poll block time for slot ${slot}`
);
} else if (blockTime < timeNow - RPC_HEALTH_CHECK_SECONDS_THRESHOLD) {
console.error(
`Health Error (RPC endpoint): block time is stale by ${
timeNow - blockTime
} seconds`
);
}
} catch (e) {
if (e instanceof Error) {
if (!e.message.includes("Block not available for slot")) {
console.error("Health Error (RPC endpoint), failure to fetch: ", e);
}
} else {
console.error("Health Error (RPC endpoint), failure to fetch: ", e);
}
}
// Wait for 10 seconds before rechecking
await new Promise((resolve) => setTimeout(resolve, 10 * 1000));
}
};

const urlExpressRelayHealth = new URL("/live", argv.endpoint);
const checkExpressRelayHealth = async () => {
//eslint-disable-next-line no-constant-condition
while (true) {
try {
const responseHealth = await fetch(urlExpressRelayHealth);
if (responseHealth.status !== 200) {
console.error(
"Health Error (Express Relay endpoint): ",
responseHealth
);
}
} catch (e) {
console.error(
"Health Error (Express Relay endpoint), failure to fetch: ",
e
);
}
// Wait for 10 seconds before rechecking
await new Promise((resolve) => setTimeout(resolve, 10 * 1000));
}
};

resubmitOpportunities().catch(console.error);
checkRpcHealth().catch(console.error);
checkExpressRelayHealth().catch(console.error);
}

run();

0 comments on commit f79f56e

Please sign in to comment.