Browse Source

refactor: move AccumulatorUpdateData to hermes client

Move AccumulatorUpdateData.ts and its tests from price-service-sdk to hermes-client
to better organize the code and prepare for future changes. This change:

- Moves AccumulatorUpdateData.ts to apps/hermes/client/js/src/
- Moves AccumulatorUpdateData.test.ts to apps/hermes/client/js/src/__tests__/
- Updates imports in price-service-sdk to use @pythnetwork/hermes-client
- Adds necessary exports in hermes-client's index.ts

Note: This PR requires package dependency updates that will be added once approved.

Co-Authored-By: Jayant Krishnamurthy <jayant@dourolabs.xyz>
Devin AI 10 tháng trước cách đây
mục cha
commit
e97d2aae05
33 tập tin đã thay đổi với 2469 bổ sung1 xóa
  1. 0 0
      apps/hermes/client/js/src/AccumulatorUpdateData.ts
  2. 0 0
      apps/hermes/client/js/src/__tests__/AccumulatorUpdateData.test.ts
  3. 14 0
      apps/hermes/client/js/src/index.ts
  4. 37 0
      express_relay/sdk/js/lib/abi.d.ts
  5. 1 0
      express_relay/sdk/js/lib/abi.d.ts.map
  6. 106 0
      express_relay/sdk/js/lib/abi.js
  7. 4 0
      express_relay/sdk/js/lib/const.d.ts
  8. 1 0
      express_relay/sdk/js/lib/const.d.ts.map
  9. 25 0
      express_relay/sdk/js/lib/const.js
  10. 6 0
      express_relay/sdk/js/lib/evm.d.ts
  11. 1 0
      express_relay/sdk/js/lib/evm.d.ts.map
  12. 156 0
      express_relay/sdk/js/lib/evm.js
  13. 141 0
      express_relay/sdk/js/lib/examples/idl/idlDummy.json
  14. 2 0
      express_relay/sdk/js/lib/examples/simpleSearcherEvm.d.ts
  15. 1 0
      express_relay/sdk/js/lib/examples/simpleSearcherEvm.d.ts.map
  16. 121 0
      express_relay/sdk/js/lib/examples/simpleSearcherEvm.js
  17. 2 0
      express_relay/sdk/js/lib/examples/simpleSearcherLimo.d.ts
  18. 1 0
      express_relay/sdk/js/lib/examples/simpleSearcherLimo.d.ts.map
  19. 201 0
      express_relay/sdk/js/lib/examples/simpleSearcherLimo.js
  20. 2 0
      express_relay/sdk/js/lib/examples/simpleSearcherSvm.d.ts
  21. 1 0
      express_relay/sdk/js/lib/examples/simpleSearcherSvm.d.ts.map
  22. 171 0
      express_relay/sdk/js/lib/examples/simpleSearcherSvm.js
  23. 525 0
      express_relay/sdk/js/lib/idl/idlExpressRelay.json
  24. 145 0
      express_relay/sdk/js/lib/index.d.ts
  25. 0 0
      express_relay/sdk/js/lib/index.d.ts.map
  26. 465 0
      express_relay/sdk/js/lib/index.js
  27. 9 0
      express_relay/sdk/js/lib/svm.d.ts
  28. 1 0
      express_relay/sdk/js/lib/svm.d.ts.map
  29. 96 0
      express_relay/sdk/js/lib/svm.js
  30. 231 0
      express_relay/sdk/js/lib/types.d.ts
  31. 0 0
      express_relay/sdk/js/lib/types.d.ts.map
  32. 2 0
      express_relay/sdk/js/lib/types.js
  33. 1 1
      price_service/sdk/js/src/index.ts

+ 0 - 0
price_service/sdk/js/src/AccumulatorUpdateData.ts → apps/hermes/client/js/src/AccumulatorUpdateData.ts


+ 0 - 0
price_service/sdk/js/src/__tests__/AccumulatorUpdateData.test.ts → apps/hermes/client/js/src/__tests__/AccumulatorUpdateData.test.ts


+ 14 - 0
apps/hermes/client/js/src/index.ts

@@ -0,0 +1,14 @@
+export {
+  isAccumulatorUpdateData,
+  sliceAccumulatorUpdateData,
+  parseAccumulatorUpdateData,
+  AccumulatorUpdateData,
+  parsePriceFeedMessage,
+  parseTwapMessage,
+  PriceFeedMessage,
+  TwapMessage,
+} from "./AccumulatorUpdateData";
+
+export * from "./HermesClient";
+export * from "./zodSchemas";
+export * from "./utils";

+ 37 - 0
express_relay/sdk/js/lib/abi.d.ts

@@ -0,0 +1,37 @@
+export declare const executeOpportunityAbi: {
+    type: string;
+    name: string;
+    inputs: ({
+        name: string;
+        type: string;
+        internalType: string;
+        components: {
+            name: string;
+            type: string;
+            internalType: string;
+            components: ({
+                name: string;
+                type: string;
+                internalType: string;
+                components: {
+                    name: string;
+                    type: string;
+                    internalType: string;
+                }[];
+            } | {
+                name: string;
+                type: string;
+                internalType: string;
+                components?: undefined;
+            })[];
+        }[];
+    } | {
+        name: string;
+        type: string;
+        internalType: string;
+        components?: undefined;
+    })[];
+    outputs: never[];
+    stateMutability: string;
+};
+//# sourceMappingURL=abi.d.ts.map

+ 1 - 0
express_relay/sdk/js/lib/abi.d.ts.map

@@ -0,0 +1 @@
+{"version":3,"file":"abi.d.ts","sourceRoot":"","sources":["../src/abi.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsGjC,CAAC"}

+ 106 - 0
express_relay/sdk/js/lib/abi.js

@@ -0,0 +1,106 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.executeOpportunityAbi = void 0;
+exports.executeOpportunityAbi = {
+    type: "function",
+    name: "executeOpportunity",
+    inputs: [
+        {
+            name: "params",
+            type: "tuple",
+            internalType: "struct ExecutionParams",
+            components: [
+                {
+                    name: "permit",
+                    type: "tuple",
+                    internalType: "struct ISignatureTransfer.PermitBatchTransferFrom",
+                    components: [
+                        {
+                            name: "permitted",
+                            type: "tuple[]",
+                            internalType: "struct ISignatureTransfer.TokenPermissions[]",
+                            components: [
+                                {
+                                    name: "token",
+                                    type: "address",
+                                    internalType: "address",
+                                },
+                                {
+                                    name: "amount",
+                                    type: "uint256",
+                                    internalType: "uint256",
+                                },
+                            ],
+                        },
+                        {
+                            name: "nonce",
+                            type: "uint256",
+                            internalType: "uint256",
+                        },
+                        {
+                            name: "deadline",
+                            type: "uint256",
+                            internalType: "uint256",
+                        },
+                    ],
+                },
+                {
+                    name: "witness",
+                    type: "tuple",
+                    internalType: "struct ExecutionWitness",
+                    components: [
+                        {
+                            name: "buyTokens",
+                            type: "tuple[]",
+                            internalType: "struct TokenAmount[]",
+                            components: [
+                                {
+                                    name: "token",
+                                    type: "address",
+                                    internalType: "address",
+                                },
+                                {
+                                    name: "amount",
+                                    type: "uint256",
+                                    internalType: "uint256",
+                                },
+                            ],
+                        },
+                        {
+                            name: "executor",
+                            type: "address",
+                            internalType: "address",
+                        },
+                        {
+                            name: "targetContract",
+                            type: "address",
+                            internalType: "address",
+                        },
+                        {
+                            name: "targetCalldata",
+                            type: "bytes",
+                            internalType: "bytes",
+                        },
+                        {
+                            name: "targetCallValue",
+                            type: "uint256",
+                            internalType: "uint256",
+                        },
+                        {
+                            name: "bidAmount",
+                            type: "uint256",
+                            internalType: "uint256",
+                        },
+                    ],
+                },
+            ],
+        },
+        {
+            name: "signature",
+            type: "bytes",
+            internalType: "bytes",
+        },
+    ],
+    outputs: [],
+    stateMutability: "payable",
+};

+ 4 - 0
express_relay/sdk/js/lib/const.d.ts

@@ -0,0 +1,4 @@
+import { OpportunityAdapterConfig, SvmConstantsConfig } from "./types";
+export declare const OPPORTUNITY_ADAPTER_CONFIGS: Record<string, OpportunityAdapterConfig>;
+export declare const SVM_CONSTANTS: Record<string, SvmConstantsConfig>;
+//# sourceMappingURL=const.d.ts.map

+ 1 - 0
express_relay/sdk/js/lib/const.d.ts.map

@@ -0,0 +1 @@
+{"version":3,"file":"const.d.ts","sourceRoot":"","sources":["../src/const.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEvE,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAC9C,MAAM,EACN,wBAAwB,CAkBzB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAM5D,CAAC"}

+ 25 - 0
express_relay/sdk/js/lib/const.js

@@ -0,0 +1,25 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.SVM_CONSTANTS = exports.OPPORTUNITY_ADAPTER_CONFIGS = void 0;
+const web3_js_1 = require("@solana/web3.js");
+exports.OPPORTUNITY_ADAPTER_CONFIGS = {
+    op_sepolia: {
+        chain_id: 11155420,
+        opportunity_adapter_factory: "0xfA119693864b2F185742A409c66f04865c787754",
+        opportunity_adapter_init_bytecode_hash: "0x3d71516d94b96a8fdca4e3a5825a6b41c9268a8e94610367e69a8462cc543533",
+        permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
+        weth: "0x74A4A85C611679B73F402B36c0F84A7D2CcdFDa3",
+    },
+    mode: {
+        chain_id: 34443,
+        opportunity_adapter_factory: "0x59F78DE21a0b05d96Ae00c547BA951a3B905602f",
+        opportunity_adapter_init_bytecode_hash: "0xd53b8e32ab2ecba07c3e3a17c3c5e492c62e2f7051b89e5154f52e6bfeb0e38f",
+        permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
+        weth: "0x4200000000000000000000000000000000000006",
+    },
+};
+exports.SVM_CONSTANTS = {
+    "development-solana": {
+        expressRelayProgram: new web3_js_1.PublicKey("PytERJFhAKuNNuaiXkApLfWzwNwSNDACpigT3LwQfou"),
+    },
+};

+ 6 - 0
express_relay/sdk/js/lib/evm.d.ts

@@ -0,0 +1,6 @@
+import { Bid, BidParams, OpportunityBid, OpportunityEvm } from "./types";
+import { Hex } from "viem";
+export declare function signBid(opportunity: OpportunityEvm, bidParams: BidParams, privateKey: Hex): Promise<Bid>;
+export declare function getSignature(opportunity: OpportunityEvm, bidParams: BidParams, privateKey: Hex): Promise<`0x${string}`>;
+export declare function signOpportunityBid(opportunity: OpportunityEvm, bidParams: BidParams, privateKey: Hex): Promise<OpportunityBid>;
+//# sourceMappingURL=evm.d.ts.map

+ 1 - 0
express_relay/sdk/js/lib/evm.d.ts.map

@@ -0,0 +1 @@
+{"version":3,"file":"evm.d.ts","sourceRoot":"","sources":["../src/evm.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EACH,SAAS,EACT,cAAc,EACd,cAAc,EAGf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAmD,GAAG,EAAE,MAAM,MAAM,CAAC;AA8C5E,wBAAsB,OAAO,CAC3B,WAAW,EAAE,cAAc,EAC3B,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,GAAG,GACd,OAAO,CAAC,GAAG,CAAC,CA2Bd;AAqCD,wBAAsB,YAAY,CAChC,WAAW,EAAE,cAAc,EAC3B,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,GAAG,GACd,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC,CAoExB;AAED,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,cAAc,EAC3B,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,GAAG,GACd,OAAO,CAAC,cAAc,CAAC,CAWzB"}

+ 156 - 0
express_relay/sdk/js/lib/evm.js

@@ -0,0 +1,156 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.signBid = signBid;
+exports.getSignature = getSignature;
+exports.signOpportunityBid = signOpportunityBid;
+const viem_1 = require("viem");
+const accounts_1 = require("viem/accounts");
+const index_1 = require("./index");
+const const_1 = require("./const");
+const abi_1 = require("./abi");
+/**
+ * Converts sellTokens, bidAmount, and callValue to permitted tokens
+ * @param tokens List of sellTokens
+ * @param bidAmount
+ * @param callValue
+ * @param weth
+ * @returns List of permitted tokens
+ */
+function getPermittedTokens(tokens, bidAmount, callValue, weth) {
+    const permitted = tokens.map(({ token, amount }) => ({
+        token,
+        amount,
+    }));
+    const wethIndex = permitted.findIndex(({ token }) => token === weth);
+    const extraWethNeeded = bidAmount + callValue;
+    if (wethIndex !== -1) {
+        permitted[wethIndex].amount += extraWethNeeded;
+        return permitted;
+    }
+    if (extraWethNeeded > 0) {
+        permitted.push({ token: weth, amount: extraWethNeeded });
+    }
+    return permitted;
+}
+function getOpportunityConfig(chainId) {
+    const opportunityAdapterConfig = const_1.OPPORTUNITY_ADAPTER_CONFIGS[chainId];
+    if (!opportunityAdapterConfig) {
+        throw new index_1.ClientError(`Opportunity adapter config not found for chain id: ${chainId}`);
+    }
+    return opportunityAdapterConfig;
+}
+async function signBid(opportunity, bidParams, privateKey) {
+    const opportunityAdapterConfig = getOpportunityConfig(opportunity.chainId);
+    const executor = (0, accounts_1.privateKeyToAccount)(privateKey).address;
+    const permitted = getPermittedTokens(opportunity.sellTokens, bidParams.amount, opportunity.targetCallValue, (0, index_1.checkAddress)(opportunityAdapterConfig.weth));
+    const signature = await getSignature(opportunity, bidParams, privateKey);
+    const calldata = makeAdapterCalldata(opportunity, permitted, executor, bidParams, signature);
+    return {
+        amount: bidParams.amount,
+        targetCalldata: calldata,
+        chainId: opportunity.chainId,
+        targetContract: opportunityAdapterConfig.opportunity_adapter_factory,
+        permissionKey: opportunity.permissionKey,
+        env: "evm",
+    };
+}
+/**
+ * Constructs the calldata for the opportunity adapter contract.
+ * @param opportunity Opportunity to bid on
+ * @param permitted Permitted tokens
+ * @param executor Address of the searcher's wallet
+ * @param bidParams Bid amount, nonce, and deadline timestamp
+ * @param signature Searcher's signature for opportunity params and bidParams
+ * @returns Calldata for the opportunity adapter contract
+ */
+function makeAdapterCalldata(opportunity, permitted, executor, bidParams, signature) {
+    return (0, viem_1.encodeFunctionData)({
+        abi: [abi_1.executeOpportunityAbi],
+        args: [
+            [
+                [permitted, bidParams.nonce, bidParams.deadline],
+                [
+                    opportunity.buyTokens,
+                    executor,
+                    opportunity.targetContract,
+                    opportunity.targetCalldata,
+                    opportunity.targetCallValue,
+                    bidParams.amount,
+                ],
+            ],
+            signature,
+        ],
+    });
+}
+async function getSignature(opportunity, bidParams, privateKey) {
+    const types = {
+        PermitBatchWitnessTransferFrom: [
+            { name: "permitted", type: "TokenPermissions[]" },
+            { name: "spender", type: "address" },
+            { name: "nonce", type: "uint256" },
+            { name: "deadline", type: "uint256" },
+            { name: "witness", type: "OpportunityWitness" },
+        ],
+        OpportunityWitness: [
+            { name: "buyTokens", type: "TokenAmount[]" },
+            { name: "executor", type: "address" },
+            { name: "targetContract", type: "address" },
+            { name: "targetCalldata", type: "bytes" },
+            { name: "targetCallValue", type: "uint256" },
+            { name: "bidAmount", type: "uint256" },
+        ],
+        TokenAmount: [
+            { name: "token", type: "address" },
+            { name: "amount", type: "uint256" },
+        ],
+        TokenPermissions: [
+            { name: "token", type: "address" },
+            { name: "amount", type: "uint256" },
+        ],
+    };
+    const account = (0, accounts_1.privateKeyToAccount)(privateKey);
+    const executor = account.address;
+    const opportunityAdapterConfig = getOpportunityConfig(opportunity.chainId);
+    const permitted = getPermittedTokens(opportunity.sellTokens, bidParams.amount, opportunity.targetCallValue, (0, index_1.checkAddress)(opportunityAdapterConfig.weth));
+    const create2Address = (0, viem_1.getContractAddress)({
+        bytecodeHash: opportunityAdapterConfig.opportunity_adapter_init_bytecode_hash,
+        from: opportunityAdapterConfig.opportunity_adapter_factory,
+        opcode: "CREATE2",
+        salt: `0x${executor.replace("0x", "").padStart(64, "0")}`,
+    });
+    return (0, accounts_1.signTypedData)({
+        privateKey,
+        domain: {
+            name: "Permit2",
+            verifyingContract: (0, index_1.checkAddress)(opportunityAdapterConfig.permit2),
+            chainId: opportunityAdapterConfig.chain_id,
+        },
+        types,
+        primaryType: "PermitBatchWitnessTransferFrom",
+        message: {
+            permitted,
+            spender: create2Address,
+            nonce: bidParams.nonce,
+            deadline: bidParams.deadline,
+            witness: {
+                buyTokens: opportunity.buyTokens,
+                executor,
+                targetContract: opportunity.targetContract,
+                targetCalldata: opportunity.targetCalldata,
+                targetCallValue: opportunity.targetCallValue,
+                bidAmount: bidParams.amount,
+            },
+        },
+    });
+}
+async function signOpportunityBid(opportunity, bidParams, privateKey) {
+    const account = (0, accounts_1.privateKeyToAccount)(privateKey);
+    const signature = await getSignature(opportunity, bidParams, privateKey);
+    return {
+        permissionKey: opportunity.permissionKey,
+        bid: bidParams,
+        executor: account.address,
+        signature,
+        opportunityId: opportunity.opportunityId,
+    };
+}

+ 141 - 0
express_relay/sdk/js/lib/examples/idl/idlDummy.json

@@ -0,0 +1,141 @@
+{
+    "address": "HYCgALnu6CM2gkQVopa1HGaNf8Vzbs9bomWRiKP267P3",
+    "metadata": {
+        "name": "dummy",
+        "version": "0.2.0",
+        "spec": "0.1.0",
+        "description": "Created with Anchor"
+    },
+    "instructions": [
+        {
+            "name": "do_nothing",
+            "discriminator": [112, 130, 224, 161, 71, 149, 192, 187],
+            "accounts": [
+                {
+                    "name": "payer",
+                    "writable": true,
+                    "signer": true
+                },
+                {
+                    "name": "express_relay",
+                    "address": "GwEtasTAxdS9neVE4GPUpcwR7DB7AizntQSPcG36ubZM"
+                },
+                {
+                    "name": "express_relay_metadata",
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [109, 101, 116, 97, 100, 97, 116, 97]
+                            }
+                        ],
+                        "program": {
+                            "kind": "account",
+                            "path": "express_relay"
+                        }
+                    }
+                },
+                {
+                    "name": "sysvar_instructions",
+                    "address": "Sysvar1nstructions1111111111111111111111111"
+                },
+                {
+                    "name": "permission"
+                },
+                {
+                    "name": "router"
+                },
+                {
+                    "name": "config_router",
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [
+                                    99, 111, 110, 102, 105, 103, 95, 114, 111, 117, 116, 101, 114
+                                ]
+                            },
+                            {
+                                "kind": "account",
+                                "path": "router"
+                            }
+                        ],
+                        "program": {
+                            "kind": "account",
+                            "path": "express_relay"
+                        }
+                    }
+                },
+                {
+                    "name": "accounting",
+                    "writable": true,
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [97, 99, 99, 111, 117, 110, 116, 105, 110, 103]
+                            }
+                        ]
+                    }
+                },
+                {
+                    "name": "system_program",
+                    "address": "11111111111111111111111111111111"
+                }
+            ],
+            "args": []
+        }
+    ],
+    "accounts": [
+        {
+            "name": "Accounting",
+            "discriminator": [1, 249, 15, 214, 81, 88, 40, 108]
+        },
+        {
+            "name": "ExpressRelayMetadata",
+            "discriminator": [204, 75, 133, 7, 175, 241, 130, 11]
+        }
+    ],
+    "types": [
+        {
+            "name": "Accounting",
+            "type": {
+                "kind": "struct",
+                "fields": [
+                    {
+                        "name": "total_fees",
+                        "type": "u64"
+                    }
+                ]
+            }
+        },
+        {
+            "name": "ExpressRelayMetadata",
+            "type": {
+                "kind": "struct",
+                "fields": [
+                    {
+                        "name": "admin",
+                        "type": "pubkey"
+                    },
+                    {
+                        "name": "relayer_signer",
+                        "type": "pubkey"
+                    },
+                    {
+                        "name": "fee_receiver_relayer",
+                        "type": "pubkey"
+                    },
+                    {
+                        "name": "split_router_default",
+                        "type": "u64"
+                    },
+                    {
+                        "name": "split_relayer",
+                        "type": "u64"
+                    }
+                ]
+            }
+        }
+    ]
+}

+ 2 - 0
express_relay/sdk/js/lib/examples/simpleSearcherEvm.d.ts

@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=simpleSearcherEvm.d.ts.map

+ 1 - 0
express_relay/sdk/js/lib/examples/simpleSearcherEvm.d.ts.map

@@ -0,0 +1 @@
+{"version":3,"file":"simpleSearcherEvm.d.ts","sourceRoot":"","sources":["../../src/examples/simpleSearcherEvm.ts"],"names":[],"mappings":""}

+ 121 - 0
express_relay/sdk/js/lib/examples/simpleSearcherEvm.js

@@ -0,0 +1,121 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const yargs_1 = __importDefault(require("yargs"));
+const helpers_1 = require("yargs/helpers");
+const index_1 = require("../index");
+const accounts_1 = require("viem/accounts");
+const viem_1 = require("viem");
+const const_1 = require("../const");
+const DAY_IN_SECONDS = 60 * 60 * 24;
+class SimpleSearcherEvm {
+    endpoint;
+    chainId;
+    privateKey;
+    apiKey;
+    client;
+    constructor(endpoint, chainId, privateKey, apiKey) {
+        this.endpoint = endpoint;
+        this.chainId = chainId;
+        this.privateKey = privateKey;
+        this.apiKey = apiKey;
+        this.client = new index_1.Client({
+            baseUrl: endpoint,
+            apiKey,
+        }, undefined, this.opportunityHandler.bind(this), this.bidStatusHandler.bind(this));
+    }
+    async bidStatusHandler(_bidStatus) {
+        const bidStatus = _bidStatus;
+        let resultDetails = "";
+        if (bidStatus.type == "submitted" || bidStatus.type == "won") {
+            resultDetails = `, transaction ${bidStatus.result}, index ${bidStatus.index} of multicall`;
+        }
+        else if (bidStatus.type == "lost") {
+            if (bidStatus.result) {
+                resultDetails = `, transaction ${bidStatus.result}`;
+            }
+            if (bidStatus.index) {
+                resultDetails += `, index ${bidStatus.index} of multicall`;
+            }
+        }
+        console.log(`Bid status for bid ${bidStatus.id}: ${bidStatus.type}${resultDetails}`);
+    }
+    async opportunityHandler(opportunity) {
+        if (!("targetContract" in opportunity))
+            throw new Error("Not a valid EVM opportunity");
+        const bidAmount = 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
+        // TODO: generate nonce more intelligently, to reduce gas costs
+        const nonce = BigInt(Math.floor(Math.random() * 2 ** 50));
+        const bidParams = {
+            amount: bidAmount,
+            nonce: nonce,
+            deadline: BigInt(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
+        };
+        const bid = await this.client.signBid(opportunity, bidParams, (0, index_1.checkHex)(argv.privateKey));
+        try {
+            const bidId = await this.client.submitBid(bid);
+            console.log(`Successful bid. Opportunity id ${opportunity.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 = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
+    .option("endpoint", {
+    description: "Express relay endpoint. e.g: https://per-staging.dourolabs.app/",
+    type: "string",
+    demandOption: true,
+})
+    .option("chain-id", {
+    description: "Chain id to fetch opportunities for. e.g: sepolia",
+    type: "string",
+    demandOption: true,
+})
+    .option("bid", {
+    description: "Bid amount in wei",
+    type: "string",
+    default: "10000000000000000",
+})
+    .option("private-key", {
+    description: "Private key to sign the bid with in hex format with 0x prefix. e.g: 0xdeadbeef...",
+    type: "string",
+    demandOption: true,
+})
+    .option("api-key", {
+    description: "The API key of the searcher to authenticate with the server for fetching and submitting bids",
+    type: "string",
+    demandOption: false,
+})
+    .help()
+    .alias("help", "h")
+    .parseSync();
+async function run() {
+    if ((0, viem_1.isHex)(argv.privateKey)) {
+        const account = (0, accounts_1.privateKeyToAccount)(argv.privateKey);
+        console.log(`Using account: ${account.address}`);
+    }
+    else {
+        throw new Error(`Invalid private key: ${argv.privateKey}`);
+    }
+    const searcher = new SimpleSearcherEvm(argv.endpoint, argv.chainId, argv.privateKey, argv.apiKey);
+    if (const_1.OPPORTUNITY_ADAPTER_CONFIGS[argv.chainId] === undefined) {
+        throw new Error(`Opportunity adapter config not found for chain ${argv.chainId}`);
+    }
+    await searcher.start();
+}
+run();

+ 2 - 0
express_relay/sdk/js/lib/examples/simpleSearcherLimo.d.ts

@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=simpleSearcherLimo.d.ts.map

+ 1 - 0
express_relay/sdk/js/lib/examples/simpleSearcherLimo.d.ts.map

@@ -0,0 +1 @@
+{"version":3,"file":"simpleSearcherLimo.d.ts","sourceRoot":"","sources":["../../src/examples/simpleSearcherLimo.ts"],"names":[],"mappings":""}

+ 201 - 0
express_relay/sdk/js/lib/examples/simpleSearcherLimo.js

@@ -0,0 +1,201 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    var desc = Object.getOwnPropertyDescriptor(m, k);
+    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+      desc = { enumerable: true, get: function() { return m[k]; } };
+    }
+    Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const yargs_1 = __importDefault(require("yargs"));
+const helpers_1 = require("yargs/helpers");
+const index_1 = require("../index");
+const const_1 = require("../const");
+const anchor = __importStar(require("@coral-xyz/anchor"));
+const web3_js_1 = require("@solana/web3.js");
+const limo = __importStar(require("@kamino-finance/limo-sdk"));
+const decimal_js_1 = require("decimal.js");
+const utils_1 = require("@kamino-finance/limo-sdk/dist/utils");
+const DAY_IN_SECONDS = 60 * 60 * 24;
+class SimpleSearcherLimo {
+    endpointExpressRelay;
+    chainId;
+    searcher;
+    endpointSvm;
+    fillRate;
+    apiKey;
+    client;
+    connectionSvm;
+    mintDecimals = {};
+    expressRelayConfig;
+    constructor(endpointExpressRelay, chainId, searcher, endpointSvm, fillRate, apiKey) {
+        this.endpointExpressRelay = endpointExpressRelay;
+        this.chainId = chainId;
+        this.searcher = searcher;
+        this.endpointSvm = endpointSvm;
+        this.fillRate = fillRate;
+        this.apiKey = apiKey;
+        this.client = new index_1.Client({
+            baseUrl: endpointExpressRelay,
+            apiKey,
+        }, undefined, this.opportunityHandler.bind(this), this.bidStatusHandler.bind(this));
+        this.connectionSvm = new web3_js_1.Connection(endpointSvm, "confirmed");
+    }
+    async bidStatusHandler(bidStatus) {
+        let resultDetails = "";
+        if (bidStatus.type == "submitted" || bidStatus.type == "won") {
+            resultDetails = `, transaction ${bidStatus.result}`;
+        }
+        else if (bidStatus.type == "lost") {
+            if (bidStatus.result) {
+                resultDetails = `, transaction ${bidStatus.result}`;
+            }
+        }
+        console.log(`Bid status for bid ${bidStatus.id}: ${bidStatus.type}${resultDetails}`);
+    }
+    async getMintDecimalsCached(mint) {
+        const mintAddress = mint.toBase58();
+        if (this.mintDecimals[mintAddress]) {
+            return this.mintDecimals[mintAddress];
+        }
+        const decimals = await (0, utils_1.getMintDecimals)(this.connectionSvm, mint);
+        this.mintDecimals[mintAddress] = decimals;
+        return decimals;
+    }
+    async generateBid(opportunity) {
+        const order = opportunity.order;
+        const limoClient = new limo.LimoClient(this.connectionSvm, order.state.globalConfig);
+        const inputMintDecimals = await this.getMintDecimalsCached(order.state.inputMint);
+        const outputMintDecimals = await this.getMintDecimalsCached(order.state.outputMint);
+        const effectiveFillRate = Math.min(this.fillRate, (100 * order.state.remainingInputAmount.toNumber()) /
+            order.state.initialInputAmount.toNumber());
+        const inputAmountDecimals = new decimal_js_1.Decimal(order.state.initialInputAmount.toNumber())
+            .div(new decimal_js_1.Decimal(10).pow(inputMintDecimals))
+            .mul(effectiveFillRate)
+            .div(100);
+        const outputAmountDecimals = new decimal_js_1.Decimal(order.state.expectedOutputAmount.toNumber())
+            .div(new decimal_js_1.Decimal(10).pow(outputMintDecimals))
+            .mul(effectiveFillRate)
+            .div(100);
+        console.log("Order address", order.address.toBase58());
+        console.log("Fill rate", effectiveFillRate);
+        console.log("Sell token", order.state.inputMint.toBase58(), "amount:", inputAmountDecimals.toString());
+        console.log("Buy token", order.state.outputMint.toBase58(), "amount:", outputAmountDecimals.toString());
+        const ixsTakeOrder = await limoClient.takeOrderIx(this.searcher.publicKey, order, inputAmountDecimals, outputAmountDecimals, const_1.SVM_CONSTANTS[this.chainId].expressRelayProgram, inputMintDecimals, outputMintDecimals);
+        const txRaw = new anchor.web3.Transaction().add(...ixsTakeOrder);
+        const router = (0, utils_1.getPdaAuthority)(limoClient.getProgramID(), order.state.globalConfig);
+        const bidAmount = new anchor.BN(argv.bid);
+        if (!this.expressRelayConfig) {
+            this.expressRelayConfig = await this.client.getExpressRelaySvmConfig(this.chainId, this.connectionSvm);
+        }
+        const bid = await this.client.constructSvmBid(txRaw, this.searcher.publicKey, router, order.address, bidAmount, new anchor.BN(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)), this.chainId, this.expressRelayConfig.relayerSigner, this.expressRelayConfig.feeReceiverRelayer);
+        bid.transaction.recentBlockhash = opportunity.blockHash;
+        bid.transaction.sign(this.searcher);
+        return bid;
+    }
+    async opportunityHandler(opportunity) {
+        const bid = await this.generateBid(opportunity);
+        try {
+            const bidId = await this.client.submitBid(bid);
+            console.log(`Successful bid. Opportunity id ${opportunity.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 = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
+    .option("endpoint-express-relay", {
+    description: "Express relay endpoint. e.g: https://per-staging.dourolabs.app/",
+    type: "string",
+    demandOption: true,
+})
+    .option("chain-id", {
+    description: "Chain id to bid on Limo opportunities for. e.g: solana",
+    type: "string",
+    demandOption: true,
+})
+    .option("bid", {
+    description: "Bid amount in lamports",
+    type: "string",
+    default: "100",
+})
+    .option("private-key", {
+    description: "Private key of the searcher in base58 format",
+    type: "string",
+    conflicts: "private-key-json-file",
+})
+    .option("private-key-json-file", {
+    description: "Path to a json file containing the private key of the searcher in array of bytes format",
+    type: "string",
+    conflicts: "private-key",
+})
+    .option("api-key", {
+    description: "The API key of the searcher to authenticate with the server for fetching and submitting bids",
+    type: "string",
+    demandOption: false,
+})
+    .option("endpoint-svm", {
+    description: "SVM RPC endpoint",
+    type: "string",
+    demandOption: true,
+})
+    .option("fill-rate", {
+    description: "How much of the initial order size to fill in percentage. Default is 100%",
+    type: "number",
+    default: 100,
+})
+    .help()
+    .alias("help", "h")
+    .parseSync();
+async function run() {
+    if (!const_1.SVM_CONSTANTS[argv.chainId]) {
+        throw new Error(`SVM constants not found for chain ${argv.chainId}`);
+    }
+    let searcherKeyPair;
+    if (argv.privateKey) {
+        const secretKey = anchor.utils.bytes.bs58.decode(argv.privateKey);
+        searcherKeyPair = web3_js_1.Keypair.fromSecretKey(secretKey);
+    }
+    else if (argv.privateKeyJsonFile) {
+        searcherKeyPair = web3_js_1.Keypair.fromSecretKey(Buffer.from(
+        // eslint-disable-next-line @typescript-eslint/no-var-requires
+        JSON.parse(require("fs").readFileSync(argv.privateKeyJsonFile))));
+    }
+    else {
+        throw new Error("Either private-key or private-key-json-file must be provided");
+    }
+    console.log(`Using searcher pubkey: ${searcherKeyPair.publicKey.toBase58()}`);
+    const simpleSearcher = new SimpleSearcherLimo(argv.endpointExpressRelay, argv.chainId, searcherKeyPair, argv.endpointSvm, argv.fillRate, argv.apiKey);
+    await simpleSearcher.start();
+}
+run();

+ 2 - 0
express_relay/sdk/js/lib/examples/simpleSearcherSvm.d.ts

@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=simpleSearcherSvm.d.ts.map

+ 1 - 0
express_relay/sdk/js/lib/examples/simpleSearcherSvm.d.ts.map

@@ -0,0 +1 @@
+{"version":3,"file":"simpleSearcherSvm.d.ts","sourceRoot":"","sources":["../../src/examples/simpleSearcherSvm.ts"],"names":[],"mappings":""}

+ 171 - 0
express_relay/sdk/js/lib/examples/simpleSearcherSvm.js

@@ -0,0 +1,171 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    var desc = Object.getOwnPropertyDescriptor(m, k);
+    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+      desc = { enumerable: true, get: function() { return m[k]; } };
+    }
+    Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const yargs_1 = __importDefault(require("yargs"));
+const helpers_1 = require("yargs/helpers");
+const index_1 = require("../index");
+const const_1 = require("../const");
+const anchor = __importStar(require("@coral-xyz/anchor"));
+const anchor_1 = require("@coral-xyz/anchor");
+const web3_js_1 = require("@solana/web3.js");
+const idlDummy_json_1 = __importDefault(require("./idl/idlDummy.json"));
+const svm_1 = require("../svm");
+const DAY_IN_SECONDS = 60 * 60 * 24;
+const DUMMY_PIDS = {
+    "development-solana": new web3_js_1.PublicKey("HYCgALnu6CM2gkQVopa1HGaNf8Vzbs9bomWRiKP267P3"),
+};
+class SimpleSearcherSvm {
+    endpointExpressRelay;
+    chainId;
+    privateKey;
+    endpointSvm;
+    apiKey;
+    client;
+    connectionSvm;
+    constructor(endpointExpressRelay, chainId, privateKey, endpointSvm, apiKey) {
+        this.endpointExpressRelay = endpointExpressRelay;
+        this.chainId = chainId;
+        this.privateKey = privateKey;
+        this.endpointSvm = endpointSvm;
+        this.apiKey = apiKey;
+        this.client = new index_1.Client({
+            baseUrl: endpointExpressRelay,
+            apiKey,
+        }, undefined, () => {
+            return Promise.resolve();
+        }, this.bidStatusHandler.bind(this));
+        this.connectionSvm = new web3_js_1.Connection(endpointSvm, "confirmed");
+    }
+    async bidStatusHandler(bidStatus) {
+        let resultDetails = "";
+        if (bidStatus.type == "submitted" || bidStatus.type == "won") {
+            resultDetails = `, transaction ${bidStatus.result}`;
+        }
+        else if (bidStatus.type == "lost") {
+            if (bidStatus.result) {
+                resultDetails = `, transaction ${bidStatus.result}`;
+            }
+        }
+        console.log(`Bid status for bid ${bidStatus.id}: ${bidStatus.type}${resultDetails}`);
+    }
+    async dummyBid() {
+        const secretKey = anchor.utils.bytes.bs58.decode(this.privateKey);
+        const searcher = web3_js_1.Keypair.fromSecretKey(secretKey);
+        const provider = new anchor_1.AnchorProvider(this.connectionSvm, new anchor.Wallet(searcher), {});
+        const dummy = new anchor_1.Program(idlDummy_json_1.default, provider);
+        const permission = web3_js_1.PublicKey.default;
+        const router = web3_js_1.Keypair.generate().publicKey;
+        const svmConstants = const_1.SVM_CONSTANTS[this.chainId];
+        if (!(this.chainId in DUMMY_PIDS)) {
+            throw new Error(`Dummy program id not found for chain ${this.chainId}`);
+        }
+        const dummyPid = DUMMY_PIDS[this.chainId];
+        const configRouter = (0, svm_1.getConfigRouterPda)(this.chainId, router);
+        const expressRelayMetadata = (0, svm_1.getExpressRelayMetadataPda)(this.chainId);
+        const accounting = web3_js_1.PublicKey.findProgramAddressSync([anchor.utils.bytes.utf8.encode("accounting")], dummyPid)[0];
+        const bidAmount = new anchor.BN(argv.bid);
+        const ixDummy = await dummy.methods
+            .doNothing()
+            .accountsStrict({
+            payer: searcher.publicKey,
+            expressRelay: svmConstants.expressRelayProgram,
+            expressRelayMetadata,
+            sysvarInstructions: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY,
+            permission,
+            router,
+            configRouter,
+            accounting,
+            systemProgram: anchor.web3.SystemProgram.programId,
+        })
+            .instruction();
+        ixDummy.programId = dummyPid;
+        const txRaw = new anchor.web3.Transaction().add(ixDummy);
+        const expressRelayConfig = await this.client.getExpressRelaySvmConfig(this.chainId, this.connectionSvm);
+        const bid = await this.client.constructSvmBid(txRaw, searcher.publicKey, router, permission, bidAmount, new anchor.BN(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)), this.chainId, expressRelayConfig.relayerSigner, expressRelayConfig.feeReceiverRelayer);
+        try {
+            const { blockhash } = await this.connectionSvm.getLatestBlockhash();
+            bid.transaction.recentBlockhash = blockhash;
+            bid.transaction.sign(web3_js_1.Keypair.fromSecretKey(secretKey));
+            const bidId = await this.client.submitBid(bid);
+            console.log(`Successful bid. Bid id ${bidId}`);
+        }
+        catch (error) {
+            console.error(`Failed to bid: ${error}`);
+        }
+    }
+    async start() {
+        for (;;) {
+            await this.dummyBid();
+            await new Promise((resolve) => setTimeout(resolve, 2000));
+        }
+    }
+}
+const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
+    .option("endpoint-express-relay", {
+    description: "Express relay endpoint. e.g: https://per-staging.dourolabs.app/",
+    type: "string",
+    demandOption: true,
+})
+    .option("chain-id", {
+    description: "Chain id to fetch opportunities for. e.g: solana",
+    type: "string",
+    demandOption: true,
+})
+    .option("bid", {
+    description: "Bid amount in lamports",
+    type: "string",
+    default: "100",
+})
+    .option("private-key", {
+    description: "Private key to sign the bid with. In 64-byte base58 format",
+    type: "string",
+    demandOption: true,
+})
+    .option("api-key", {
+    description: "The API key of the searcher to authenticate with the server for fetching and submitting bids",
+    type: "string",
+    demandOption: false,
+})
+    .option("endpoint-svm", {
+    description: "SVM RPC endpoint",
+    type: "string",
+    demandOption: true,
+})
+    .help()
+    .alias("help", "h")
+    .parseSync();
+async function run() {
+    if (const_1.SVM_CONSTANTS[argv.chainId] === undefined) {
+        throw new Error(`SVM constants not found for chain ${argv.chainId}`);
+    }
+    const searcherSvm = web3_js_1.Keypair.fromSecretKey(anchor.utils.bytes.bs58.decode(argv.privateKey));
+    console.log(`Using searcher pubkey: ${searcherSvm.publicKey.toBase58()}`);
+    const simpleSearcher = new SimpleSearcherSvm(argv.endpointExpressRelay, argv.chainId, argv.privateKey, argv.endpointSvm, argv.apiKey);
+    await simpleSearcher.start();
+}
+run();

+ 525 - 0
express_relay/sdk/js/lib/idl/idlExpressRelay.json

@@ -0,0 +1,525 @@
+{
+    "address": "GwEtasTAxdS9neVE4GPUpcwR7DB7AizntQSPcG36ubZM",
+    "metadata": {
+        "name": "express_relay",
+        "version": "0.2.0",
+        "spec": "0.1.0",
+        "description": "Pyth Express Relay program for handling permissioning and bid distribution",
+        "repository": "https://github.com/pyth-network/per"
+    },
+    "instructions": [
+        {
+            "name": "check_permission",
+            "docs": [
+                "Checks if permissioning exists for a particular (permission, router) pair within the same transaction",
+                "Permissioning takes the form of a SubmitBid instruction with matching permission and router accounts",
+                "Returns the fees paid to the router in the matching instructions"
+            ],
+            "discriminator": [154, 199, 232, 242, 96, 72, 197, 236],
+            "accounts": [
+                {
+                    "name": "sysvar_instructions",
+                    "address": "Sysvar1nstructions1111111111111111111111111"
+                },
+                {
+                    "name": "permission"
+                },
+                {
+                    "name": "router"
+                },
+                {
+                    "name": "config_router",
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [
+                                    99, 111, 110, 102, 105, 103, 95, 114, 111, 117, 116, 101, 114
+                                ]
+                            },
+                            {
+                                "kind": "account",
+                                "path": "router"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "name": "express_relay_metadata",
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [109, 101, 116, 97, 100, 97, 116, 97]
+                            }
+                        ]
+                    }
+                }
+            ],
+            "args": [],
+            "returns": "u64"
+        },
+        {
+            "name": "initialize",
+            "discriminator": [175, 175, 109, 31, 13, 152, 155, 237],
+            "accounts": [
+                {
+                    "name": "payer",
+                    "writable": true,
+                    "signer": true
+                },
+                {
+                    "name": "express_relay_metadata",
+                    "writable": true,
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [109, 101, 116, 97, 100, 97, 116, 97]
+                            }
+                        ]
+                    }
+                },
+                {
+                    "name": "admin"
+                },
+                {
+                    "name": "relayer_signer"
+                },
+                {
+                    "name": "fee_receiver_relayer"
+                },
+                {
+                    "name": "system_program",
+                    "address": "11111111111111111111111111111111"
+                }
+            ],
+            "args": [
+                {
+                    "name": "data",
+                    "type": {
+                        "defined": {
+                            "name": "InitializeArgs"
+                        }
+                    }
+                }
+            ]
+        },
+        {
+            "name": "set_admin",
+            "discriminator": [251, 163, 0, 52, 91, 194, 187, 92],
+            "accounts": [
+                {
+                    "name": "admin",
+                    "writable": true,
+                    "signer": true,
+                    "relations": ["express_relay_metadata"]
+                },
+                {
+                    "name": "express_relay_metadata",
+                    "writable": true,
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [109, 101, 116, 97, 100, 97, 116, 97]
+                            }
+                        ]
+                    }
+                },
+                {
+                    "name": "admin_new"
+                }
+            ],
+            "args": []
+        },
+        {
+            "name": "set_relayer",
+            "discriminator": [23, 243, 33, 88, 110, 84, 196, 37],
+            "accounts": [
+                {
+                    "name": "admin",
+                    "writable": true,
+                    "signer": true,
+                    "relations": ["express_relay_metadata"]
+                },
+                {
+                    "name": "express_relay_metadata",
+                    "writable": true,
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [109, 101, 116, 97, 100, 97, 116, 97]
+                            }
+                        ]
+                    }
+                },
+                {
+                    "name": "relayer_signer"
+                },
+                {
+                    "name": "fee_receiver_relayer"
+                }
+            ],
+            "args": []
+        },
+        {
+            "name": "set_router_split",
+            "discriminator": [16, 150, 106, 13, 27, 191, 104, 8],
+            "accounts": [
+                {
+                    "name": "admin",
+                    "writable": true,
+                    "signer": true,
+                    "relations": ["express_relay_metadata"]
+                },
+                {
+                    "name": "config_router",
+                    "writable": true,
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [
+                                    99, 111, 110, 102, 105, 103, 95, 114, 111, 117, 116, 101, 114
+                                ]
+                            },
+                            {
+                                "kind": "account",
+                                "path": "router"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "name": "express_relay_metadata",
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [109, 101, 116, 97, 100, 97, 116, 97]
+                            }
+                        ]
+                    }
+                },
+                {
+                    "name": "router"
+                },
+                {
+                    "name": "system_program",
+                    "address": "11111111111111111111111111111111"
+                }
+            ],
+            "args": [
+                {
+                    "name": "data",
+                    "type": {
+                        "defined": {
+                            "name": "SetRouterSplitArgs"
+                        }
+                    }
+                }
+            ]
+        },
+        {
+            "name": "set_splits",
+            "discriminator": [175, 2, 86, 49, 225, 202, 232, 189],
+            "accounts": [
+                {
+                    "name": "admin",
+                    "writable": true,
+                    "signer": true,
+                    "relations": ["express_relay_metadata"]
+                },
+                {
+                    "name": "express_relay_metadata",
+                    "writable": true,
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [109, 101, 116, 97, 100, 97, 116, 97]
+                            }
+                        ]
+                    }
+                }
+            ],
+            "args": [
+                {
+                    "name": "data",
+                    "type": {
+                        "defined": {
+                            "name": "SetSplitsArgs"
+                        }
+                    }
+                }
+            ]
+        },
+        {
+            "name": "submit_bid",
+            "docs": [
+                "Submits a bid for a particular (permission, router) pair and distributes bids according to splits"
+            ],
+            "discriminator": [19, 164, 237, 254, 64, 139, 237, 93],
+            "accounts": [
+                {
+                    "name": "searcher",
+                    "writable": true,
+                    "signer": true
+                },
+                {
+                    "name": "relayer_signer",
+                    "signer": true,
+                    "relations": ["express_relay_metadata"]
+                },
+                {
+                    "name": "permission"
+                },
+                {
+                    "name": "router",
+                    "writable": true
+                },
+                {
+                    "name": "config_router",
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [
+                                    99, 111, 110, 102, 105, 103, 95, 114, 111, 117, 116, 101, 114
+                                ]
+                            },
+                            {
+                                "kind": "account",
+                                "path": "router"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "name": "express_relay_metadata",
+                    "writable": true,
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [109, 101, 116, 97, 100, 97, 116, 97]
+                            }
+                        ]
+                    }
+                },
+                {
+                    "name": "fee_receiver_relayer",
+                    "writable": true,
+                    "relations": ["express_relay_metadata"]
+                },
+                {
+                    "name": "system_program",
+                    "address": "11111111111111111111111111111111"
+                },
+                {
+                    "name": "sysvar_instructions",
+                    "address": "Sysvar1nstructions1111111111111111111111111"
+                }
+            ],
+            "args": [
+                {
+                    "name": "data",
+                    "type": {
+                        "defined": {
+                            "name": "SubmitBidArgs"
+                        }
+                    }
+                }
+            ]
+        },
+        {
+            "name": "withdraw_fees",
+            "discriminator": [198, 212, 171, 109, 144, 215, 174, 89],
+            "accounts": [
+                {
+                    "name": "admin",
+                    "writable": true,
+                    "signer": true,
+                    "relations": ["express_relay_metadata"]
+                },
+                {
+                    "name": "fee_receiver_admin",
+                    "writable": true
+                },
+                {
+                    "name": "express_relay_metadata",
+                    "writable": true,
+                    "pda": {
+                        "seeds": [
+                            {
+                                "kind": "const",
+                                "value": [109, 101, 116, 97, 100, 97, 116, 97]
+                            }
+                        ]
+                    }
+                }
+            ],
+            "args": []
+        }
+    ],
+    "accounts": [
+        {
+            "name": "ConfigRouter",
+            "discriminator": [135, 66, 240, 166, 94, 198, 187, 36]
+        },
+        {
+            "name": "ExpressRelayMetadata",
+            "discriminator": [204, 75, 133, 7, 175, 241, 130, 11]
+        }
+    ],
+    "errors": [
+        {
+            "code": 6000,
+            "name": "FeeSplitLargerThanPrecision",
+            "msg": "Fee split(s) larger than fee precision"
+        },
+        {
+            "code": 6001,
+            "name": "FeesHigherThanBid",
+            "msg": "Fees higher than bid"
+        },
+        {
+            "code": 6002,
+            "name": "DeadlinePassed",
+            "msg": "Deadline passed"
+        },
+        {
+            "code": 6003,
+            "name": "InvalidCPISubmitBid",
+            "msg": "Invalid CPI into submit bid instruction"
+        },
+        {
+            "code": 6004,
+            "name": "MissingPermission",
+            "msg": "Missing permission"
+        },
+        {
+            "code": 6005,
+            "name": "MultiplePermissions",
+            "msg": "Multiple permissions"
+        },
+        {
+            "code": 6006,
+            "name": "InsufficientSearcherFunds",
+            "msg": "Insufficient Searcher Funds"
+        },
+        {
+            "code": 6007,
+            "name": "InsufficientRent",
+            "msg": "Insufficient funds for rent"
+        }
+    ],
+    "types": [
+        {
+            "name": "ConfigRouter",
+            "type": {
+                "kind": "struct",
+                "fields": [
+                    {
+                        "name": "router",
+                        "type": "pubkey"
+                    },
+                    {
+                        "name": "split",
+                        "type": "u64"
+                    }
+                ]
+            }
+        },
+        {
+            "name": "ExpressRelayMetadata",
+            "type": {
+                "kind": "struct",
+                "fields": [
+                    {
+                        "name": "admin",
+                        "type": "pubkey"
+                    },
+                    {
+                        "name": "relayer_signer",
+                        "type": "pubkey"
+                    },
+                    {
+                        "name": "fee_receiver_relayer",
+                        "type": "pubkey"
+                    },
+                    {
+                        "name": "split_router_default",
+                        "type": "u64"
+                    },
+                    {
+                        "name": "split_relayer",
+                        "type": "u64"
+                    }
+                ]
+            }
+        },
+        {
+            "name": "InitializeArgs",
+            "type": {
+                "kind": "struct",
+                "fields": [
+                    {
+                        "name": "split_router_default",
+                        "type": "u64"
+                    },
+                    {
+                        "name": "split_relayer",
+                        "type": "u64"
+                    }
+                ]
+            }
+        },
+        {
+            "name": "SetRouterSplitArgs",
+            "type": {
+                "kind": "struct",
+                "fields": [
+                    {
+                        "name": "split_router",
+                        "type": "u64"
+                    }
+                ]
+            }
+        },
+        {
+            "name": "SetSplitsArgs",
+            "type": {
+                "kind": "struct",
+                "fields": [
+                    {
+                        "name": "split_router_default",
+                        "type": "u64"
+                    },
+                    {
+                        "name": "split_relayer",
+                        "type": "u64"
+                    }
+                ]
+            }
+        },
+        {
+            "name": "SubmitBidArgs",
+            "type": {
+                "kind": "struct",
+                "fields": [
+                    {
+                        "name": "deadline",
+                        "type": "i64"
+                    },
+                    {
+                        "name": "bid_amount",
+                        "type": "u64"
+                    }
+                ]
+            }
+        }
+    ]
+}

+ 145 - 0
express_relay/sdk/js/lib/index.d.ts

@@ -0,0 +1,145 @@
+import type { components } from "./serverTypes";
+import { ClientOptions as FetchClientOptions } from "openapi-fetch";
+import { Address, Hex } from "viem";
+import WebSocket from "isomorphic-ws";
+import { Bid, BidId, BidParams, BidsResponse, BidStatusUpdate, BidSvm, ExpressRelaySvmConfig, Opportunity, OpportunityBid, OpportunityEvm, OpportunityCreate, TokenAmount } from "./types";
+import { Connection, PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js";
+import * as anchor from "@coral-xyz/anchor";
+export * from "./types";
+export * from "./const";
+export declare class ClientError extends Error {
+}
+type ClientOptions = FetchClientOptions & {
+    baseUrl: string;
+    apiKey?: string;
+};
+export interface WsOptions {
+    /**
+     * Max time to wait for a response from the server in milliseconds
+     */
+    response_timeout: number;
+}
+export declare function checkHex(hex: string): Hex;
+export declare function checkAddress(address: string): Address;
+export declare function checkTokenQty(token: {
+    token: string;
+    amount: string;
+}): TokenAmount;
+export declare class Client {
+    clientOptions: ClientOptions;
+    wsOptions: WsOptions;
+    websocket?: WebSocket;
+    idCounter: number;
+    callbackRouter: Record<string, (response: components["schemas"]["ServerResultMessage"]) => void>;
+    private websocketOpportunityCallback?;
+    private websocketBidStatusCallback?;
+    private getAuthorization;
+    constructor(clientOptions: ClientOptions, wsOptions?: WsOptions, opportunityCallback?: (opportunity: Opportunity) => Promise<void>, bidStatusCallback?: (statusUpdate: BidStatusUpdate) => Promise<void>);
+    private connectWebsocket;
+    /**
+     * Subscribes to the specified chains
+     *
+     * The opportunity handler will be called for opportunities on the specified chains
+     * If the opportunity handler is not set, an error will be thrown
+     * @param chains
+     */
+    subscribeChains(chains: string[]): Promise<void>;
+    /**
+     * Unsubscribes from the specified chains
+     *
+     * The opportunity handler will no longer be called for opportunities on the specified chains
+     * @param chains
+     */
+    unsubscribeChains(chains: string[]): Promise<void>;
+    requestViaWebsocket(msg: components["schemas"]["ClientMessage"]): Promise<components["schemas"]["APIResponse"] | null>;
+    /**
+     * Fetches opportunities
+     * @param chainId Chain id to fetch opportunities for. e.g: sepolia
+     * @returns List of opportunities
+     */
+    getOpportunities(chainId?: string): Promise<Opportunity[]>;
+    /**
+     * Submits an opportunity to be exposed to searchers
+     * @param opportunity Opportunity to submit
+     */
+    submitOpportunity(opportunity: OpportunityCreate): Promise<void>;
+    /**
+     * Submits a raw bid for a permission key
+     * @param bid
+     * @param subscribeToUpdates If true, the client will subscribe to bid status updates via websocket and will call the bid status callback if set
+     * @returns The id of the submitted bid, you can use this id to track the status of the bid
+     */
+    submitBid(bid: Bid, subscribeToUpdates?: boolean): Promise<BidId>;
+    /**
+     * Get bids for an api key
+     * @param fromTime The datetime to fetch bids from. If undefined or null, fetches from the beginning of time.
+     * @returns The paginated bids response
+     */
+    getBids(fromTime?: Date): Promise<BidsResponse>;
+    private toServerBid;
+    /**
+     * Converts an opportunity from the server to the client format
+     * Returns undefined if the opportunity version is not supported
+     * @param opportunity
+     * @returns Opportunity in the converted client format
+     */
+    convertOpportunity(opportunity: components["schemas"]["Opportunity"]): Opportunity | undefined;
+    /**
+     * Creates a signed opportunity bid for an opportunity
+     * @param opportunity EVM Opportunity to bid on
+     * @param bidParams Bid amount and valid until timestamp
+     * @param privateKey Private key to sign the bid with
+     * @returns Signed opportunity bid
+     */
+    signOpportunityBid(opportunity: OpportunityEvm, bidParams: BidParams, privateKey: Hex): Promise<OpportunityBid>;
+    /**
+     * Creates a signed bid for an EVM opportunity
+     * @param opportunity EVM Opportunity to bid on
+     * @param bidParams Bid amount, nonce, and deadline timestamp
+     * @param privateKey Private key to sign the bid with
+     * @returns Signed bid
+     */
+    signBid(opportunity: OpportunityEvm, bidParams: BidParams, privateKey: Hex): Promise<Bid>;
+    /**
+     * Creates a signature for the bid and opportunity
+     * @param opportunity EVM Opportunity to bid on
+     * @param bidParams Bid amount, nonce, and deadline timestamp
+     * @param privateKey Private key to sign the bid with
+     * @returns Signature for the bid and opportunity
+     */
+    getSignature(opportunity: OpportunityEvm, bidParams: BidParams, privateKey: Hex): Promise<`0x${string}`>;
+    /**
+     * Fetches the Express Relay SVM config necessary for bidding
+     * @param chainId The id for the chain you want to fetch the config for
+     * @param connection The connection to use for fetching the config
+     */
+    getExpressRelaySvmConfig(chainId: string, connection: Connection): Promise<ExpressRelaySvmConfig>;
+    /**
+     * Constructs a SubmitBid instruction, which can be added to a transaction to permission it on the given permission key
+     * @param searcher The address of the searcher that is submitting the bid
+     * @param router The identifying address of the router that the permission key is for
+     * @param permissionKey The 32-byte permission key as an SVM PublicKey
+     * @param bidAmount The amount of the bid in lamports
+     * @param deadline The deadline for the bid in seconds since Unix epoch
+     * @param chainId The chain ID as a string, e.g. "solana"
+     * @param relayerSigner The address of the relayer that is submitting the bid
+     * @param feeReceiverRelayer The fee collection address of the relayer
+     * @returns The SubmitBid instruction
+     */
+    constructSubmitBidInstruction(searcher: PublicKey, router: PublicKey, permissionKey: PublicKey, bidAmount: anchor.BN, deadline: anchor.BN, chainId: string, relayerSigner: PublicKey, feeReceiverRelayer: PublicKey): Promise<TransactionInstruction>;
+    /**
+     * Constructs an SVM bid, by adding a SubmitBid instruction to a transaction
+     * @param tx The transaction to add a SubmitBid instruction to. This transaction should already check for the appropriate permissions.
+     * @param searcher The address of the searcher that is submitting the bid
+     * @param router The identifying address of the router that the permission key is for
+     * @param permissionKey The 32-byte permission key as an SVM PublicKey
+     * @param bidAmount The amount of the bid in lamports
+     * @param deadline The deadline for the bid in seconds since Unix epoch
+     * @param chainId The chain ID as a string, e.g. "solana"
+     * @param relayerSigner The address of the relayer that is submitting the bid
+     * @param feeReceiverRelayer The fee collection address of the relayer
+     * @returns The constructed SVM bid
+     */
+    constructSvmBid(tx: Transaction, searcher: PublicKey, router: PublicKey, permissionKey: PublicKey, bidAmount: anchor.BN, deadline: anchor.BN, chainId: string, relayerSigner: PublicKey, feeReceiverRelayer: PublicKey): Promise<BidSvm>;
+}
+//# sourceMappingURL=index.d.ts.map

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
express_relay/sdk/js/lib/index.d.ts.map


+ 465 - 0
express_relay/sdk/js/lib/index.js

@@ -0,0 +1,465 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    var desc = Object.getOwnPropertyDescriptor(m, k);
+    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+      desc = { enumerable: true, get: function() { return m[k]; } };
+    }
+    Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __exportStar = (this && this.__exportStar) || function(m, exports) {
+    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
+};
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Client = exports.ClientError = void 0;
+exports.checkHex = checkHex;
+exports.checkAddress = checkAddress;
+exports.checkTokenQty = checkTokenQty;
+const openapi_fetch_1 = __importDefault(require("openapi-fetch"));
+const viem_1 = require("viem");
+const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
+const web3_js_1 = require("@solana/web3.js");
+const limo_sdk_1 = require("@kamino-finance/limo-sdk");
+const utils_1 = require("@kamino-finance/limo-sdk/dist/utils");
+const evm = __importStar(require("./evm"));
+const svm = __importStar(require("./svm"));
+__exportStar(require("./types"), exports);
+__exportStar(require("./const"), exports);
+class ClientError extends Error {
+}
+exports.ClientError = ClientError;
+const DEFAULT_WS_OPTIONS = {
+    response_timeout: 10000,
+};
+function checkHex(hex) {
+    if ((0, viem_1.isHex)(hex)) {
+        return hex;
+    }
+    throw new ClientError(`Invalid hex: ${hex}`);
+}
+function checkAddress(address) {
+    if ((0, viem_1.isAddress)(address)) {
+        return address;
+    }
+    throw new ClientError(`Invalid address: ${address}`);
+}
+function checkTokenQty(token) {
+    return {
+        token: checkAddress(token.token),
+        amount: BigInt(token.amount),
+    };
+}
+class Client {
+    clientOptions;
+    wsOptions;
+    websocket;
+    idCounter = 0;
+    callbackRouter = {};
+    websocketOpportunityCallback;
+    websocketBidStatusCallback;
+    getAuthorization() {
+        return this.clientOptions.apiKey
+            ? {
+                Authorization: `Bearer ${this.clientOptions.apiKey}`,
+            }
+            : {};
+    }
+    constructor(clientOptions, wsOptions, opportunityCallback, bidStatusCallback) {
+        this.clientOptions = clientOptions;
+        this.clientOptions.headers = {
+            ...(this.clientOptions.headers ?? {}),
+            ...this.getAuthorization(),
+        };
+        this.wsOptions = { ...DEFAULT_WS_OPTIONS, ...wsOptions };
+        this.websocketOpportunityCallback = opportunityCallback;
+        this.websocketBidStatusCallback = bidStatusCallback;
+    }
+    connectWebsocket() {
+        const websocketEndpoint = new URL(this.clientOptions.baseUrl);
+        websocketEndpoint.protocol =
+            websocketEndpoint.protocol === "https:" ? "wss:" : "ws:";
+        websocketEndpoint.pathname = "/v1/ws";
+        this.websocket = new isomorphic_ws_1.default(websocketEndpoint.toString(), {
+            headers: this.getAuthorization(),
+        });
+        this.websocket.on("message", async (data) => {
+            const message = JSON.parse(data.toString());
+            if ("type" in message && message.type === "new_opportunity") {
+                if (this.websocketOpportunityCallback !== undefined) {
+                    const convertedOpportunity = this.convertOpportunity(message.opportunity);
+                    if (convertedOpportunity !== undefined) {
+                        await this.websocketOpportunityCallback(convertedOpportunity);
+                    }
+                }
+            }
+            else if ("type" in message && message.type === "bid_status_update") {
+                if (this.websocketBidStatusCallback !== undefined) {
+                    await this.websocketBidStatusCallback({
+                        id: message.status.id,
+                        ...message.status.bid_status,
+                    });
+                }
+            }
+            else if ("id" in message && message.id) {
+                // Response to a request sent earlier via the websocket with the same id
+                const callback = this.callbackRouter[message.id];
+                if (callback !== undefined) {
+                    callback(message);
+                    delete this.callbackRouter[message.id];
+                }
+            }
+            else if ("error" in message) {
+                // Can not route error messages to the callback router as they don't have an id
+                console.error(message.error);
+            }
+        });
+    }
+    /**
+     * Subscribes to the specified chains
+     *
+     * The opportunity handler will be called for opportunities on the specified chains
+     * If the opportunity handler is not set, an error will be thrown
+     * @param chains
+     */
+    async subscribeChains(chains) {
+        if (this.websocketOpportunityCallback === undefined) {
+            throw new ClientError("Opportunity handler not set");
+        }
+        await this.requestViaWebsocket({
+            method: "subscribe",
+            params: {
+                chain_ids: chains,
+            },
+        });
+    }
+    /**
+     * Unsubscribes from the specified chains
+     *
+     * The opportunity handler will no longer be called for opportunities on the specified chains
+     * @param chains
+     */
+    async unsubscribeChains(chains) {
+        await this.requestViaWebsocket({
+            method: "unsubscribe",
+            params: {
+                chain_ids: chains,
+            },
+        });
+    }
+    async requestViaWebsocket(msg) {
+        const msg_with_id = {
+            ...msg,
+            id: (this.idCounter++).toString(),
+        };
+        return new Promise((resolve, reject) => {
+            this.callbackRouter[msg_with_id.id] = (response) => {
+                if (response.status === "success") {
+                    resolve(response.result);
+                }
+                else {
+                    reject(response.result);
+                }
+            };
+            if (this.websocket === undefined) {
+                this.connectWebsocket();
+            }
+            if (this.websocket !== undefined) {
+                if (this.websocket.readyState === isomorphic_ws_1.default.CONNECTING) {
+                    this.websocket.on("open", () => {
+                        this.websocket?.send(JSON.stringify(msg_with_id));
+                    });
+                }
+                else if (this.websocket.readyState === isomorphic_ws_1.default.OPEN) {
+                    this.websocket.send(JSON.stringify(msg_with_id));
+                }
+                else {
+                    reject("Websocket connection closing or already closed");
+                }
+            }
+            setTimeout(() => {
+                delete this.callbackRouter[msg_with_id.id];
+                reject("Websocket response timeout");
+            }, this.wsOptions.response_timeout);
+        });
+    }
+    /**
+     * Fetches opportunities
+     * @param chainId Chain id to fetch opportunities for. e.g: sepolia
+     * @returns List of opportunities
+     */
+    async getOpportunities(chainId) {
+        const client = (0, openapi_fetch_1.default)(this.clientOptions);
+        const opportunities = await client.GET("/v1/opportunities", {
+            params: { query: { chain_id: chainId } },
+        });
+        if (opportunities.data === undefined) {
+            throw new ClientError("No opportunities found");
+        }
+        return opportunities.data.flatMap((opportunity) => {
+            const convertedOpportunity = this.convertOpportunity(opportunity);
+            if (convertedOpportunity === undefined) {
+                return [];
+            }
+            return convertedOpportunity;
+        });
+    }
+    /**
+     * Submits an opportunity to be exposed to searchers
+     * @param opportunity Opportunity to submit
+     */
+    async submitOpportunity(opportunity) {
+        const client = (0, openapi_fetch_1.default)(this.clientOptions);
+        let body;
+        if ("order" in opportunity) {
+            const encoded_order = Buffer.alloc(limo_sdk_1.Order.discriminator.length + limo_sdk_1.Order.layout.span);
+            limo_sdk_1.Order.discriminator.copy(encoded_order);
+            limo_sdk_1.Order.layout.encode(opportunity.order.state, encoded_order, limo_sdk_1.Order.discriminator.length);
+            body = {
+                chain_id: opportunity.chainId,
+                version: "v1",
+                program: opportunity.program,
+                order: encoded_order.toString("base64"),
+                slot: opportunity.slot,
+                block_hash: opportunity.blockHash,
+                order_address: opportunity.order.address.toBase58(),
+                buy_tokens: [
+                    {
+                        token: opportunity.order.state.inputMint.toBase58(),
+                        amount: opportunity.order.state.remainingInputAmount.toNumber(),
+                    },
+                ],
+                sell_tokens: [
+                    {
+                        token: opportunity.order.state.outputMint.toBase58(),
+                        amount: opportunity.order.state.expectedOutputAmount.toNumber(),
+                    },
+                ],
+                permission_account: opportunity.order.address.toBase58(),
+                router: (0, utils_1.getPdaAuthority)(limo_sdk_1.limoId, opportunity.order.state.globalConfig).toBase58(),
+            };
+        }
+        else {
+            body = {
+                chain_id: opportunity.chainId,
+                version: "v1",
+                permission_key: opportunity.permissionKey,
+                target_contract: opportunity.targetContract,
+                target_calldata: opportunity.targetCalldata,
+                target_call_value: opportunity.targetCallValue.toString(),
+                sell_tokens: opportunity.sellTokens.map(({ token, amount }) => ({
+                    token,
+                    amount: amount.toString(),
+                })),
+                buy_tokens: opportunity.buyTokens.map(({ token, amount }) => ({
+                    token,
+                    amount: amount.toString(),
+                })),
+            };
+        }
+        const response = await client.POST("/v1/opportunities", {
+            body: body,
+        });
+        if (response.error) {
+            throw new ClientError(response.error.error);
+        }
+    }
+    /**
+     * Submits a raw bid for a permission key
+     * @param bid
+     * @param subscribeToUpdates If true, the client will subscribe to bid status updates via websocket and will call the bid status callback if set
+     * @returns The id of the submitted bid, you can use this id to track the status of the bid
+     */
+    async submitBid(bid, subscribeToUpdates = true) {
+        const serverBid = this.toServerBid(bid);
+        if (subscribeToUpdates) {
+            const result = await this.requestViaWebsocket({
+                method: "post_bid",
+                params: {
+                    bid: serverBid,
+                },
+            });
+            if (result === null) {
+                throw new ClientError("Empty response in websocket for bid submission");
+            }
+            return result.id;
+        }
+        else {
+            const client = (0, openapi_fetch_1.default)(this.clientOptions);
+            const response = await client.POST("/v1/bids", {
+                body: serverBid,
+            });
+            if (response.error) {
+                throw new ClientError(response.error.error);
+            }
+            else if (response.data === undefined) {
+                throw new ClientError("No data returned");
+            }
+            else {
+                return response.data.id;
+            }
+        }
+    }
+    /**
+     * Get bids for an api key
+     * @param fromTime The datetime to fetch bids from. If undefined or null, fetches from the beginning of time.
+     * @returns The paginated bids response
+     */
+    async getBids(fromTime) {
+        const client = (0, openapi_fetch_1.default)(this.clientOptions);
+        const response = await client.GET("/v1/bids", {
+            params: { query: { from_time: fromTime?.toISOString() } },
+        });
+        if (response.error) {
+            throw new ClientError(response.error.error);
+        }
+        else if (response.data === undefined) {
+            throw new ClientError("No data returned");
+        }
+        else {
+            return response.data;
+        }
+    }
+    toServerBid(bid) {
+        if (bid.env === "evm") {
+            return {
+                amount: bid.amount.toString(),
+                target_calldata: bid.targetCalldata,
+                chain_id: bid.chainId,
+                target_contract: bid.targetContract,
+                permission_key: bid.permissionKey,
+            };
+        }
+        return {
+            chain_id: bid.chainId,
+            transaction: bid.transaction
+                .serialize({ requireAllSignatures: false })
+                .toString("base64"),
+        };
+    }
+    /**
+     * Converts an opportunity from the server to the client format
+     * Returns undefined if the opportunity version is not supported
+     * @param opportunity
+     * @returns Opportunity in the converted client format
+     */
+    convertOpportunity(opportunity) {
+        if (opportunity.version !== "v1") {
+            console.warn(`Can not handle opportunity version: ${opportunity.version}. Please upgrade your client.`);
+            return undefined;
+        }
+        if ("target_calldata" in opportunity) {
+            return {
+                chainId: opportunity.chain_id,
+                opportunityId: opportunity.opportunity_id,
+                permissionKey: checkHex(opportunity.permission_key),
+                targetContract: checkAddress(opportunity.target_contract),
+                targetCalldata: checkHex(opportunity.target_calldata),
+                targetCallValue: BigInt(opportunity.target_call_value),
+                sellTokens: opportunity.sell_tokens.map(checkTokenQty),
+                buyTokens: opportunity.buy_tokens.map(checkTokenQty),
+            };
+        }
+        const order = limo_sdk_1.Order.decode(Buffer.from(opportunity.order, "base64"));
+        return {
+            chainId: opportunity.chain_id,
+            slot: opportunity.slot,
+            blockHash: opportunity.block_hash,
+            opportunityId: opportunity.opportunity_id,
+            order: {
+                state: order,
+                address: new web3_js_1.PublicKey(opportunity.order_address),
+            },
+            program: "limo",
+        };
+    }
+    // EVM specific functions
+    /**
+     * Creates a signed opportunity bid for an opportunity
+     * @param opportunity EVM Opportunity to bid on
+     * @param bidParams Bid amount and valid until timestamp
+     * @param privateKey Private key to sign the bid with
+     * @returns Signed opportunity bid
+     */
+    async signOpportunityBid(opportunity, bidParams, privateKey) {
+        return evm.signOpportunityBid(opportunity, bidParams, privateKey);
+    }
+    /**
+     * Creates a signed bid for an EVM opportunity
+     * @param opportunity EVM Opportunity to bid on
+     * @param bidParams Bid amount, nonce, and deadline timestamp
+     * @param privateKey Private key to sign the bid with
+     * @returns Signed bid
+     */
+    async signBid(opportunity, bidParams, privateKey) {
+        return evm.signBid(opportunity, bidParams, privateKey);
+    }
+    /**
+     * Creates a signature for the bid and opportunity
+     * @param opportunity EVM Opportunity to bid on
+     * @param bidParams Bid amount, nonce, and deadline timestamp
+     * @param privateKey Private key to sign the bid with
+     * @returns Signature for the bid and opportunity
+     */
+    async getSignature(opportunity, bidParams, privateKey) {
+        return evm.getSignature(opportunity, bidParams, privateKey);
+    }
+    // SVM specific functions
+    /**
+     * Fetches the Express Relay SVM config necessary for bidding
+     * @param chainId The id for the chain you want to fetch the config for
+     * @param connection The connection to use for fetching the config
+     */
+    async getExpressRelaySvmConfig(chainId, connection) {
+        return svm.getExpressRelaySvmConfig(chainId, connection);
+    }
+    /**
+     * Constructs a SubmitBid instruction, which can be added to a transaction to permission it on the given permission key
+     * @param searcher The address of the searcher that is submitting the bid
+     * @param router The identifying address of the router that the permission key is for
+     * @param permissionKey The 32-byte permission key as an SVM PublicKey
+     * @param bidAmount The amount of the bid in lamports
+     * @param deadline The deadline for the bid in seconds since Unix epoch
+     * @param chainId The chain ID as a string, e.g. "solana"
+     * @param relayerSigner The address of the relayer that is submitting the bid
+     * @param feeReceiverRelayer The fee collection address of the relayer
+     * @returns The SubmitBid instruction
+     */
+    async constructSubmitBidInstruction(searcher, router, permissionKey, bidAmount, deadline, chainId, relayerSigner, feeReceiverRelayer) {
+        return svm.constructSubmitBidInstruction(searcher, router, permissionKey, bidAmount, deadline, chainId, relayerSigner, feeReceiverRelayer);
+    }
+    /**
+     * Constructs an SVM bid, by adding a SubmitBid instruction to a transaction
+     * @param tx The transaction to add a SubmitBid instruction to. This transaction should already check for the appropriate permissions.
+     * @param searcher The address of the searcher that is submitting the bid
+     * @param router The identifying address of the router that the permission key is for
+     * @param permissionKey The 32-byte permission key as an SVM PublicKey
+     * @param bidAmount The amount of the bid in lamports
+     * @param deadline The deadline for the bid in seconds since Unix epoch
+     * @param chainId The chain ID as a string, e.g. "solana"
+     * @param relayerSigner The address of the relayer that is submitting the bid
+     * @param feeReceiverRelayer The fee collection address of the relayer
+     * @returns The constructed SVM bid
+     */
+    async constructSvmBid(tx, searcher, router, permissionKey, bidAmount, deadline, chainId, relayerSigner, feeReceiverRelayer) {
+        return svm.constructSvmBid(tx, searcher, router, permissionKey, bidAmount, deadline, chainId, relayerSigner, feeReceiverRelayer);
+    }
+}
+exports.Client = Client;

+ 9 - 0
express_relay/sdk/js/lib/svm.d.ts

@@ -0,0 +1,9 @@
+import { Connection, PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js";
+import * as anchor from "@coral-xyz/anchor";
+import { BidSvm, ExpressRelaySvmConfig } from "./types";
+export declare function getConfigRouterPda(chain: string, router: PublicKey): PublicKey;
+export declare function getExpressRelayMetadataPda(chain: string): PublicKey;
+export declare function constructSubmitBidInstruction(searcher: PublicKey, router: PublicKey, permissionKey: PublicKey, bidAmount: anchor.BN, deadline: anchor.BN, chainId: string, relayerSigner: PublicKey, feeReceiverRelayer: PublicKey): Promise<TransactionInstruction>;
+export declare function constructSvmBid(tx: Transaction, searcher: PublicKey, router: PublicKey, permissionKey: PublicKey, bidAmount: anchor.BN, deadline: anchor.BN, chainId: string, relayerSigner: PublicKey, feeReceiverRelayer: PublicKey): Promise<BidSvm>;
+export declare function getExpressRelaySvmConfig(chainId: string, connection: Connection): Promise<ExpressRelaySvmConfig>;
+//# sourceMappingURL=svm.d.ts.map

+ 1 - 0
express_relay/sdk/js/lib/svm.d.ts.map

@@ -0,0 +1 @@
+{"version":3,"file":"svm.d.ts","sourceRoot":"","sources":["../src/svm.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,SAAS,EACT,WAAW,EACX,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAcxD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,GAChB,SAAS,CAOX;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAOnE;AAED,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,SAAS,EACxB,SAAS,EAAE,MAAM,CAAC,EAAE,EACpB,QAAQ,EAAE,MAAM,CAAC,EAAE,EACnB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,SAAS,EACxB,kBAAkB,EAAE,SAAS,GAC5B,OAAO,CAAC,sBAAsB,CAAC,CA8BjC;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,WAAW,EACf,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,SAAS,EACxB,SAAS,EAAE,MAAM,CAAC,EAAE,EACpB,QAAQ,EAAE,MAAM,CAAC,EAAE,EACnB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,SAAS,EACxB,kBAAkB,EAAE,SAAS,GAC5B,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,qBAAqB,CAAC,CAgBhC"}

+ 96 - 0
express_relay/sdk/js/lib/svm.js

@@ -0,0 +1,96 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    var desc = Object.getOwnPropertyDescriptor(m, k);
+    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+      desc = { enumerable: true, get: function() { return m[k]; } };
+    }
+    Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.getConfigRouterPda = getConfigRouterPda;
+exports.getExpressRelayMetadataPda = getExpressRelayMetadataPda;
+exports.constructSubmitBidInstruction = constructSubmitBidInstruction;
+exports.constructSvmBid = constructSvmBid;
+exports.getExpressRelaySvmConfig = getExpressRelaySvmConfig;
+const web3_js_1 = require("@solana/web3.js");
+const anchor = __importStar(require("@coral-xyz/anchor"));
+const anchor_1 = require("@coral-xyz/anchor");
+const idlExpressRelay_json_1 = __importDefault(require("./idl/idlExpressRelay.json"));
+const const_1 = require("./const");
+const nodewallet_1 = __importDefault(require("@coral-xyz/anchor/dist/cjs/nodewallet"));
+function getExpressRelayProgram(chain) {
+    if (!const_1.SVM_CONSTANTS[chain]) {
+        throw new Error(`Chain ${chain} not supported`);
+    }
+    return const_1.SVM_CONSTANTS[chain].expressRelayProgram;
+}
+function getConfigRouterPda(chain, router) {
+    const expressRelayProgram = getExpressRelayProgram(chain);
+    return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("config_router"), router.toBuffer()], expressRelayProgram)[0];
+}
+function getExpressRelayMetadataPda(chain) {
+    const expressRelayProgram = getExpressRelayProgram(chain);
+    return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("metadata")], expressRelayProgram)[0];
+}
+async function constructSubmitBidInstruction(searcher, router, permissionKey, bidAmount, deadline, chainId, relayerSigner, feeReceiverRelayer) {
+    const expressRelay = new anchor_1.Program(idlExpressRelay_json_1.default, {});
+    const configRouter = getConfigRouterPda(chainId, router);
+    const expressRelayMetadata = getExpressRelayMetadataPda(chainId);
+    const svmConstants = const_1.SVM_CONSTANTS[chainId];
+    const ixSubmitBid = await expressRelay.methods
+        .submitBid({
+        deadline,
+        bidAmount,
+    })
+        .accountsStrict({
+        searcher,
+        relayerSigner,
+        permission: permissionKey,
+        router,
+        configRouter,
+        expressRelayMetadata,
+        feeReceiverRelayer,
+        systemProgram: anchor.web3.SystemProgram.programId,
+        sysvarInstructions: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY,
+    })
+        .instruction();
+    ixSubmitBid.programId = svmConstants.expressRelayProgram;
+    return ixSubmitBid;
+}
+async function constructSvmBid(tx, searcher, router, permissionKey, bidAmount, deadline, chainId, relayerSigner, feeReceiverRelayer) {
+    const ixSubmitBid = await constructSubmitBidInstruction(searcher, router, permissionKey, bidAmount, deadline, chainId, relayerSigner, feeReceiverRelayer);
+    tx.instructions.unshift(ixSubmitBid);
+    return {
+        transaction: tx,
+        chainId: chainId,
+        env: "svm",
+    };
+}
+async function getExpressRelaySvmConfig(chainId, connection) {
+    const provider = new anchor_1.AnchorProvider(connection, new nodewallet_1.default(new web3_js_1.Keypair()));
+    const expressRelay = new anchor_1.Program(idlExpressRelay_json_1.default, provider);
+    const metadata = await expressRelay.account.expressRelayMetadata.fetch(getExpressRelayMetadataPda(chainId));
+    return {
+        feeReceiverRelayer: metadata.feeReceiverRelayer,
+        relayerSigner: metadata.relayerSigner,
+    };
+}

+ 231 - 0
express_relay/sdk/js/lib/types.d.ts

@@ -0,0 +1,231 @@
+import { Address, Hex } from "viem";
+import type { components } from "./serverTypes";
+import { Blockhash, PublicKey, Transaction } from "@solana/web3.js";
+import { OrderStateAndAddress } from "@kamino-finance/limo-sdk/dist/utils";
+/**
+ * ERC20 token with contract address and amount
+ */
+export type TokenAmount = {
+    token: Address;
+    amount: bigint;
+};
+/**
+ * TokenPermissions struct for permit2
+ */
+export type TokenPermissions = {
+    token: Address;
+    amount: bigint;
+};
+export type BidId = string;
+export type ChainId = string;
+/**
+ * Bid parameters
+ */
+export type BidParams = {
+    /**
+     * Bid amount in wei
+     */
+    amount: bigint;
+    /**
+     * Bid nonce, used to prevent replay of a submitted signature.
+     * This can be set to a random uint256 when creating a new signature
+     */
+    nonce: bigint;
+    /**
+     * Unix timestamp for when the bid is no longer valid in seconds
+     */
+    deadline: bigint;
+};
+export type OpportunityAdapterConfig = {
+    /**
+     * The chain id as a u64
+     */
+    chain_id: number;
+    /**
+     * The opportunity factory address
+     */
+    opportunity_adapter_factory: Address;
+    /**
+     * The hash of the bytecode used to initialize the opportunity adapter
+     */
+    opportunity_adapter_init_bytecode_hash: Hex;
+    /**
+     * The permit2 address
+     */
+    permit2: Address;
+    /**
+     * The weth address
+     */
+    weth: Address;
+};
+/**
+ * Represents a valid opportunity ready to be executed
+ */
+export type OpportunityEvm = {
+    /**
+     * The chain id where the opportunity will be executed.
+     */
+    chainId: ChainId;
+    /**
+     * Permission key required for successful execution of the opportunity.
+     */
+    permissionKey: Hex;
+    /**
+     * Contract address to call for execution of the opportunity.
+     */
+    targetContract: Address;
+    /**
+     * Calldata for the targetContract call.
+     */
+    targetCalldata: Hex;
+    /**
+     * Value to send with the targetContract call.
+     */
+    targetCallValue: bigint;
+    /**
+     * Tokens required to execute the opportunity
+     */
+    sellTokens: TokenAmount[];
+    /**
+     * Tokens to receive after the opportunity is executed
+     */
+    buyTokens: TokenAmount[];
+    /**
+     * Unique identifier for the opportunity
+     */
+    opportunityId: string;
+};
+export type OpportunitySvm = {
+    order: OrderStateAndAddress;
+    program: "limo";
+    /**
+     * The chain id where the opportunity will be executed.
+     */
+    chainId: ChainId;
+    /**
+     * Slot where the opportunity was found
+     */
+    slot: number;
+    /**
+     * Blockhash that can be used to sign transactions for this opportunity
+     */
+    blockHash: Blockhash;
+    /**
+     * Unique identifier for the opportunity
+     */
+    opportunityId: string;
+};
+export type OpportunityCreate = Omit<OpportunityEvm, "opportunityId"> | Omit<OpportunitySvm, "opportunityId">;
+export type Opportunity = OpportunityEvm | OpportunitySvm;
+/**
+ * Represents a bid for an opportunity
+ */
+export type OpportunityBid = {
+    /**
+     * Opportunity unique identifier in uuid format
+     */
+    opportunityId: string;
+    /**
+     * The permission key required for successful execution of the opportunity.
+     */
+    permissionKey: Hex;
+    /**
+     * Executor address
+     */
+    executor: Address;
+    /**
+     * Signature of the executor
+     */
+    signature: Hex;
+    bid: BidParams;
+};
+/**
+ * All the parameters necessary to represent an opportunity
+ */
+export type Bid = BidEvm | BidSvm;
+/**
+ * Represents a raw EVM bid on acquiring a permission key
+ */
+export type BidEvm = {
+    /**
+     * The permission key to bid on
+     * @example 0xc0ffeebabe
+     *
+     */
+    permissionKey: Hex;
+    /**
+     * @description Amount of bid in wei.
+     * @example 10
+     */
+    amount: bigint;
+    /**
+     * @description Calldata for the targetContract call.
+     * @example 0xdeadbeef
+     */
+    targetCalldata: Hex;
+    /**
+     * @description The chain id to bid on.
+     * @example sepolia
+     */
+    chainId: ChainId;
+    /**
+     * @description The targetContract address to call.
+     * @example 0xcA11bde05977b3631167028862bE2a173976CA11
+     */
+    targetContract: Address;
+    /**
+     * @description The execution environment for the bid.
+     */
+    env: "evm";
+};
+/**
+ * Necessary accounts for submitting a SVM bid. These can be fetched from on-chain program data.
+ */
+export type ExpressRelaySvmConfig = {
+    /**
+     * @description The relayer signer account. All submitted transactions will be signed by this account.
+     */
+    relayerSigner: PublicKey;
+    /**
+     * @description The fee collection account for the relayer.
+     */
+    feeReceiverRelayer: PublicKey;
+};
+/**
+ * Represents a raw SVM bid on acquiring a permission key
+ */
+export type BidSvm = {
+    /**
+     * @description Transaction object.
+     * @example SGVsbG8sIFdvcmxkIQ
+     */
+    transaction: Transaction;
+    /**
+     * @description The chain id to bid on.
+     * @example solana
+     */
+    chainId: ChainId;
+    /**
+     * @description The execution environment for the bid.
+     */
+    env: "svm";
+};
+export type BidStatusUpdate = {
+    id: BidId;
+} & components["schemas"]["BidStatus"];
+export type BidStatusUpdateSvm = {
+    id: BidId;
+} & components["schemas"]["BidStatusSvm"];
+export type BidStatusUpdateEvm = {
+    id: BidId;
+} & components["schemas"]["BidStatusEvm"];
+export type BidResponse = components["schemas"]["SimulatedBid"];
+export type BidResponseSvm = components["schemas"]["SimulatedBidSvm"];
+export type BidResponseEvm = components["schemas"]["SimulatedBidEvm"];
+export type BidsResponse = {
+    items: BidResponse[];
+};
+export type SvmConstantsConfig = {
+    expressRelayProgram: PublicKey;
+};
+//# sourceMappingURL=types.d.ts.map

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
express_relay/sdk/js/lib/types.d.ts.map


+ 2 - 0
express_relay/sdk/js/lib/types.js

@@ -0,0 +1,2 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });

+ 1 - 1
price_service/sdk/js/src/index.ts

@@ -16,7 +16,7 @@ export {
   AccumulatorUpdateData,
   parsePriceFeedMessage,
   parseTwapMessage,
-} from "./AccumulatorUpdateData";
+} from "@pythnetwork/hermes-client";
 
 /**
  * A Pyth Price represented as `${price} ± ${conf} * 10^${expo}` published at `publishTime`.

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác