Skip to content

Commit f14dd35

Browse files
authored
feat(per-js-sdk): Add support for bid state and raw bid submission + renames (#1329)
* feat(per-js-sdk): Add support for bid state and raw bid submission * Add custom error class for easier excpetion handling * Merge websocket/http apis inside a single function * Adapt to new names on the server * Adapt field names
1 parent 0b1135c commit f14dd35

File tree

7 files changed

+579
-290
lines changed

7 files changed

+579
-290
lines changed

express_relay/sdk/js/README.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,35 @@ npm run generate-api-types
3030
import {
3131
Client,
3232
OpportunityParams,
33-
BidInfo,
33+
BidParams,
3434
} from "@pythnetwork/express-relay-evm-js";
3535

36-
const client = new Client({ baseUrl: "https://per-staging.dourolabs.app/" });
37-
38-
function calculateOpportunityBid(opportunity: Opportunity): BidInfo | null {
36+
function calculateOpportunityBid(opportunity: Opportunity): BidParams | null {
3937
// searcher implementation here
4038
// if the opportunity is not suitable for the searcher, return null
4139
}
4240

43-
client.setOpportunityHandler(async (opportunity: Opportunity) => {
44-
const bidInfo = calculateOpportunityBid(opportunity);
45-
if (bidInfo === null) return;
41+
async function bidStatusCallback(bidStatus: BidStatusUpdate) {
42+
console.log(`Bid status for bid ${bidStatus.id}: ${bidStatus.status.status}`);
43+
}
44+
45+
async function opportunityCallback(opportunity: Opportunity) {
46+
const bidParams = calculateOpportunityBid(opportunity);
47+
if (bidParams === null) return;
4648
const opportunityBid = await client.signOpportunityBid(
4749
opportunity,
48-
bidInfo,
50+
bidParams,
4951
privateKey // searcher private key with appropriate permissions and assets
5052
);
5153
await client.submitOpportunityBid(opportunityBid);
52-
});
54+
}
55+
56+
const client = new Client(
57+
{ baseUrl: "https://per-staging.dourolabs.app/" },
58+
bidStatusCallback,
59+
opportunityCallback
60+
);
61+
5362
await client.subscribeChains([chain_id]); // chain id you want to subscribe to
5463
```
5564

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

6069
#### SimpleSearcher
6170

62-
[This example](./src/examples/SimpleSearcher.ts) fetches `OpportunityParams` from the specified endpoint,
71+
[This example](./src/examples/simpleSearcher.ts) fetches `OpportunityParams` from the specified endpoint,
6372
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
6473
`npm run simple-searcher`. A full command looks like this:
6574

6675
```bash
6776
npm run simple-searcher -- \
6877
--endpoint https://per-staging.dourolabs.app/ \
69-
--bid 100000 \
7078
--chain-id op_sepolia \
7179
--private-key <YOUR-PRIVATE-KEY>
7280
```

express_relay/sdk/js/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/express-relay-evm-js",
3-
"version": "0.1.1",
3+
"version": "0.2.0",
44
"description": "Utilities for interacting with the express relay protocol",
55
"homepage": "https://github.com/pyth-network/pyth-crosschain/tree/main/express_relay/sdk/js",
66
"author": "Douro Labs",
@@ -15,8 +15,8 @@
1515
"scripts": {
1616
"build": "tsc",
1717
"test": "jest src/ --passWithNoTests",
18-
"simple-searcher": "npm run build && node lib/examples/SimpleSearcher.js",
19-
"generate-api-types": "openapi-typescript http://127.0.0.1:9000/docs/openapi.json --output src/types.d.ts",
18+
"simple-searcher": "npm run build && node lib/examples/simpleSearcher.js",
19+
"generate-api-types": "openapi-typescript http://127.0.0.1:9000/docs/openapi.json --output src/serverTypes.d.ts",
2020
"format": "prettier --write \"src/**/*.ts\"",
2121
"lint": "eslint src",
2222
"prepublishOnly": "npm run build && npm test && npm run lint",

express_relay/sdk/js/src/examples/SimpleSearcher.ts renamed to express_relay/sdk/js/src/examples/simpleSearcher.ts

Lines changed: 70 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,70 @@ import { hideBin } from "yargs/helpers";
33
import { checkHex, Client } from "../index";
44
import { privateKeyToAccount } from "viem/accounts";
55
import { isHex } from "viem";
6+
import { BidStatusUpdate, Opportunity } from "../types";
7+
8+
const DAY_IN_SECONDS = 60 * 60 * 24;
9+
10+
class SimpleSearcher {
11+
private client: Client;
12+
constructor(
13+
public endpoint: string,
14+
public chainId: string,
15+
public privateKey: string
16+
) {
17+
this.client = new Client(
18+
{ baseUrl: endpoint },
19+
undefined,
20+
this.opportunityHandler.bind(this),
21+
this.bidStatusHandler.bind(this)
22+
);
23+
}
24+
25+
async bidStatusHandler(bidStatus: BidStatusUpdate) {
26+
console.log(
27+
`Bid status for bid ${bidStatus.id}: ${bidStatus.status} ${
28+
bidStatus.status == "submitted" ? bidStatus.result : ""
29+
}`
30+
);
31+
}
32+
33+
async opportunityHandler(opportunity: Opportunity) {
34+
const bid = BigInt(argv.bid);
35+
// Bid info should be generated by evaluating the opportunity
36+
// here for simplicity we are using a constant bid and 24 hours of validity
37+
const bidParams = {
38+
amount: bid,
39+
validUntil: BigInt(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
40+
};
41+
const opportunityBid = await this.client.signOpportunityBid(
42+
opportunity,
43+
bidParams,
44+
checkHex(argv.privateKey)
45+
);
46+
try {
47+
const bidId = await this.client.submitOpportunityBid(opportunityBid);
48+
console.log(
49+
`Successful bid. Opportunity id ${opportunityBid.opportunityId} Bid id ${bidId}`
50+
);
51+
} catch (error) {
52+
console.error(
53+
`Failed to bid on opportunity ${opportunity.opportunityId}: ${error}`
54+
);
55+
}
56+
}
57+
58+
async start() {
59+
try {
60+
await this.client.subscribeChains([argv.chainId]);
61+
console.log(
62+
`Subscribed to chain ${argv.chainId}. Waiting for opportunities...`
63+
);
64+
} catch (error) {
65+
console.error(error);
66+
this.client.websocket?.close();
67+
}
68+
}
69+
}
670

771
const argv = yargs(hideBin(process.argv))
872
.option("endpoint", {
@@ -30,49 +94,19 @@ const argv = yargs(hideBin(process.argv))
3094
.help()
3195
.alias("help", "h")
3296
.parseSync();
33-
3497
async function run() {
35-
const client = new Client({ baseUrl: argv.endpoint });
3698
if (isHex(argv.privateKey)) {
3799
const account = privateKeyToAccount(argv.privateKey);
38100
console.log(`Using account: ${account.address}`);
39101
} else {
40102
throw new Error(`Invalid private key: ${argv.privateKey}`);
41103
}
42-
const DAY_IN_SECONDS = 60 * 60 * 24;
43-
client.setOpportunityHandler(async (opportunity) => {
44-
const bid = BigInt(argv.bid);
45-
// Bid info should be generated by evaluating the opportunity
46-
// here for simplicity we are using a constant bid and 24 hours of validity
47-
const bidInfo = {
48-
amount: bid,
49-
validUntil: BigInt(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
50-
};
51-
const opportunityBid = await client.signOpportunityBid(
52-
opportunity,
53-
bidInfo,
54-
checkHex(argv.privateKey)
55-
);
56-
try {
57-
await client.submitOpportunityBid(opportunityBid);
58-
console.log(
59-
`Successful bid ${bid} on opportunity ${opportunity.opportunityId}`
60-
);
61-
} catch (error) {
62-
console.error(
63-
`Failed to bid on opportunity ${opportunity.opportunityId}: ${error}`
64-
);
65-
}
66-
});
67-
try {
68-
await client.subscribeChains([argv.chainId]);
69-
console.log(
70-
`Subscribed to chain ${argv.chainId}. Waiting for opportunities...`
71-
);
72-
} catch (error) {
73-
console.error(error);
74-
client.websocket?.close();
75-
}
104+
const searcher = new SimpleSearcher(
105+
argv.endpoint,
106+
argv.chainId,
107+
argv.privateKey
108+
);
109+
await searcher.start();
76110
}
77111

78112
run();

0 commit comments

Comments
 (0)