Skip to content
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
30 changes: 19 additions & 11 deletions express_relay/sdk/js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,35 @@ npm run generate-api-types
import {
Client,
OpportunityParams,
BidInfo,
BidParams,
} from "@pythnetwork/express-relay-evm-js";

const client = new Client({ baseUrl: "https://per-staging.dourolabs.app/" });

function calculateOpportunityBid(opportunity: Opportunity): BidInfo | null {
function calculateOpportunityBid(opportunity: Opportunity): BidParams | null {
// searcher implementation here
// if the opportunity is not suitable for the searcher, return null
}

client.setOpportunityHandler(async (opportunity: Opportunity) => {
const bidInfo = calculateOpportunityBid(opportunity);
if (bidInfo === null) return;
async function bidStatusCallback(bidStatus: BidStatusUpdate) {
console.log(`Bid status for bid ${bidStatus.id}: ${bidStatus.status.status}`);
}

async function opportunityCallback(opportunity: Opportunity) {
const bidParams = calculateOpportunityBid(opportunity);
if (bidParams === null) return;
const opportunityBid = await client.signOpportunityBid(
opportunity,
bidInfo,
bidParams,
privateKey // searcher private key with appropriate permissions and assets
);
await client.submitOpportunityBid(opportunityBid);
});
}

const client = new Client(
{ baseUrl: "https://per-staging.dourolabs.app/" },
bidStatusCallback,
opportunityCallback
);

await client.subscribeChains([chain_id]); // chain id you want to subscribe to
```

Expand All @@ -59,14 +68,13 @@ There is an example searcher in [examples](./src/examples/) directory.

#### SimpleSearcher

[This example](./src/examples/SimpleSearcher.ts) fetches `OpportunityParams` from the specified endpoint,
[This example](./src/examples/simpleSearcher.ts) fetches `OpportunityParams` from the specified endpoint,
creates a fixed bid on each opportunity and signs them with the provided private key, and finally submits them back to the server. You can run it with
`npm run simple-searcher`. A full command looks like this:

```bash
npm run simple-searcher -- \
--endpoint https://per-staging.dourolabs.app/ \
--bid 100000 \
--chain-id op_sepolia \
--private-key <YOUR-PRIVATE-KEY>
```
6 changes: 3 additions & 3 deletions express_relay/sdk/js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pythnetwork/express-relay-evm-js",
"version": "0.1.1",
"version": "0.2.0",
"description": "Utilities for interacting with the express relay protocol",
"homepage": "https://pyth.network",
"author": {
Expand All @@ -17,8 +17,8 @@
"scripts": {
"build": "tsc",
"test": "jest src/ --passWithNoTests",
"simple-searcher": "npm run build && node lib/examples/SimpleSearcher.js",
"generate-api-types": "openapi-typescript http://127.0.0.1:9000/docs/openapi.json --output src/types.d.ts",
"simple-searcher": "npm run build && node lib/examples/simpleSearcher.js",
"generate-api-types": "openapi-typescript http://127.0.0.1:9000/docs/openapi.json --output src/serverTypes.d.ts",
"format": "prettier --write \"src/**/*.ts\"",
"lint": "eslint src",
"prepublishOnly": "npm run build && npm test && npm run lint",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,70 @@ import { hideBin } from "yargs/helpers";
import { checkHex, Client } from "../index";
import { privateKeyToAccount } from "viem/accounts";
import { isHex } from "viem";
import { BidStatusUpdate, Opportunity } from "../types";

const DAY_IN_SECONDS = 60 * 60 * 24;

class SimpleSearcher {
private client: Client;
constructor(
public endpoint: string,
public chainId: string,
public privateKey: string
) {
this.client = new Client(
{ baseUrl: endpoint },
undefined,
this.opportunityHandler.bind(this),
this.bidStatusHandler.bind(this)
);
}

async bidStatusHandler(bidStatus: BidStatusUpdate) {
console.log(
`Bid status for bid ${bidStatus.id}: ${bidStatus.status} ${
bidStatus.status == "submitted" ? bidStatus.result : ""
}`
);
}

async opportunityHandler(opportunity: Opportunity) {
const bid = BigInt(argv.bid);
// Bid info should be generated by evaluating the opportunity
// here for simplicity we are using a constant bid and 24 hours of validity
const bidParams = {
amount: bid,
validUntil: BigInt(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
};
const opportunityBid = await this.client.signOpportunityBid(
opportunity,
bidParams,
checkHex(argv.privateKey)
);
try {
const bidId = await this.client.submitOpportunityBid(opportunityBid);
console.log(
`Successful bid. Opportunity id ${opportunityBid.opportunityId} Bid id ${bidId}`
);
} catch (error) {
console.error(
`Failed to bid on opportunity ${opportunity.opportunityId}: ${error}`
);
}
}

async start() {
try {
await this.client.subscribeChains([argv.chainId]);
console.log(
`Subscribed to chain ${argv.chainId}. Waiting for opportunities...`
);
} catch (error) {
console.error(error);
this.client.websocket?.close();
}
}
}

const argv = yargs(hideBin(process.argv))
.option("endpoint", {
Expand Down Expand Up @@ -30,49 +94,19 @@ const argv = yargs(hideBin(process.argv))
.help()
.alias("help", "h")
.parseSync();

async function run() {
const client = new Client({ baseUrl: argv.endpoint });
if (isHex(argv.privateKey)) {
const account = privateKeyToAccount(argv.privateKey);
console.log(`Using account: ${account.address}`);
} else {
throw new Error(`Invalid private key: ${argv.privateKey}`);
}
const DAY_IN_SECONDS = 60 * 60 * 24;
client.setOpportunityHandler(async (opportunity) => {
const bid = BigInt(argv.bid);
// Bid info should be generated by evaluating the opportunity
// here for simplicity we are using a constant bid and 24 hours of validity
const bidInfo = {
amount: bid,
validUntil: BigInt(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
};
const opportunityBid = await client.signOpportunityBid(
opportunity,
bidInfo,
checkHex(argv.privateKey)
);
try {
await client.submitOpportunityBid(opportunityBid);
console.log(
`Successful bid ${bid} on opportunity ${opportunity.opportunityId}`
);
} catch (error) {
console.error(
`Failed to bid on opportunity ${opportunity.opportunityId}: ${error}`
);
}
});
try {
await client.subscribeChains([argv.chainId]);
console.log(
`Subscribed to chain ${argv.chainId}. Waiting for opportunities...`
);
} catch (error) {
console.error(error);
client.websocket?.close();
}
const searcher = new SimpleSearcher(
argv.endpoint,
argv.chainId,
argv.privateKey
);
await searcher.start();
}

run();
Loading