Skip to content

Commit 27656f9

Browse files
authored
fix(express-relay): Restore opportunity bid (#1760)
1 parent aac0584 commit 27656f9

File tree

5 files changed

+172
-26
lines changed

5 files changed

+172
-26
lines changed

express_relay/sdk/js/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/express-relay-evm-js",
3-
"version": "0.8.0",
3+
"version": "0.8.1",
44
"description": "Utilities for interacting with the express relay protocol",
55
"homepage": "https://github.com/pyth-network/pyth-crosschain/tree/main/express_relay/sdk/js",
66
"author": "Douro Labs",

express_relay/sdk/js/src/index.ts

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
TokenAmount,
2323
BidsResponse,
2424
TokenPermissions,
25+
OpportunityBid,
2526
} from "./types";
2627
import { executeOpportunityAbi } from "./abi";
2728
import { OPPORTUNITY_ADAPTER_CONFIGS } from "./const";
@@ -67,6 +68,16 @@ export function checkTokenQty(token: {
6768
};
6869
}
6970

71+
function getOpportunityConfig(chainId: string) {
72+
const opportunityAdapterConfig = OPPORTUNITY_ADAPTER_CONFIGS[chainId];
73+
if (!opportunityAdapterConfig) {
74+
throw new ClientError(
75+
`Opportunity adapter config not found for chain id: ${chainId}`
76+
);
77+
}
78+
return opportunityAdapterConfig;
79+
}
80+
7081
/**
7182
* Converts sellTokens, bidAmount, and callValue to permitted tokens
7283
* @param tokens List of sellTokens
@@ -367,17 +378,17 @@ export class Client {
367378
}
368379

369380
/**
370-
* Creates a signed bid for an opportunity
381+
* Creates a signature for the bid and opportunity
371382
* @param opportunity Opportunity to bid on
372383
* @param bidParams Bid amount, nonce, and deadline timestamp
373384
* @param privateKey Private key to sign the bid with
374-
* @returns Signed bid
385+
* @returns Signature for the bid and opportunity
375386
*/
376-
async signBid(
387+
async getSignature(
377388
opportunity: Opportunity,
378389
bidParams: BidParams,
379390
privateKey: Hex
380-
): Promise<Bid> {
391+
): Promise<`0x${string}`> {
381392
const types = {
382393
PermitBatchWitnessTransferFrom: [
383394
{ name: "permitted", type: "TokenPermissions[]" },
@@ -406,13 +417,7 @@ export class Client {
406417

407418
const account = privateKeyToAccount(privateKey);
408419
const executor = account.address;
409-
const opportunityAdapterConfig =
410-
OPPORTUNITY_ADAPTER_CONFIGS[opportunity.chainId];
411-
if (!opportunityAdapterConfig) {
412-
throw new ClientError(
413-
`Opportunity adapter config not found for chain id: ${opportunity.chainId}`
414-
);
415-
}
420+
const opportunityAdapterConfig = getOpportunityConfig(opportunity.chainId);
416421
const permitted = getPermittedTokens(
417422
opportunity.sellTokens,
418423
bidParams.amount,
@@ -427,7 +432,7 @@ export class Client {
427432
salt: `0x${executor.replace("0x", "").padStart(64, "0")}`,
428433
});
429434

430-
const signature = await signTypedData({
435+
return signTypedData({
431436
privateKey,
432437
domain: {
433438
name: "Permit2",
@@ -451,6 +456,61 @@ export class Client {
451456
},
452457
},
453458
});
459+
}
460+
461+
/**
462+
* Creates a signed opportunity bid for an opportunity
463+
* @param opportunity Opportunity to bid on
464+
* @param bidParams Bid amount and valid until timestamp
465+
* @param privateKey Private key to sign the bid with
466+
* @returns Signed opportunity bid
467+
*/
468+
async signOpportunityBid(
469+
opportunity: Opportunity,
470+
bidParams: BidParams,
471+
privateKey: Hex
472+
): Promise<OpportunityBid> {
473+
const account = privateKeyToAccount(privateKey);
474+
const signature = await this.getSignature(
475+
opportunity,
476+
bidParams,
477+
privateKey
478+
);
479+
480+
return {
481+
permissionKey: opportunity.permissionKey,
482+
bid: bidParams,
483+
executor: account.address,
484+
signature,
485+
opportunityId: opportunity.opportunityId,
486+
};
487+
}
488+
489+
/**
490+
* Creates a signed bid for an opportunity
491+
* @param opportunity Opportunity to bid on
492+
* @param bidParams Bid amount, nonce, and deadline timestamp
493+
* @param privateKey Private key to sign the bid with
494+
* @returns Signed bid
495+
*/
496+
async signBid(
497+
opportunity: Opportunity,
498+
bidParams: BidParams,
499+
privateKey: Hex
500+
): Promise<Bid> {
501+
const opportunityAdapterConfig = getOpportunityConfig(opportunity.chainId);
502+
const executor = privateKeyToAccount(privateKey).address;
503+
const permitted = getPermittedTokens(
504+
opportunity.sellTokens,
505+
bidParams.amount,
506+
opportunity.targetCallValue,
507+
checkAddress(opportunityAdapterConfig.weth)
508+
);
509+
const signature = await this.getSignature(
510+
opportunity,
511+
bidParams,
512+
privateKey
513+
);
454514

455515
const calldata = this.makeAdapterCalldata(
456516
opportunity,

express_relay/sdk/js/src/types.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,29 @@ export type Opportunity = {
9696
*/
9797
buyTokens: TokenAmount[];
9898
};
99+
/**
100+
* Represents a bid for an opportunity
101+
*/
102+
export type OpportunityBid = {
103+
/**
104+
* Opportunity unique identifier in uuid format
105+
*/
106+
opportunityId: string;
107+
/**
108+
* The permission key required for successful execution of the opportunity.
109+
*/
110+
permissionKey: Hex;
111+
/**
112+
* Executor address
113+
*/
114+
executor: Address;
115+
/**
116+
* Signature of the executor
117+
*/
118+
signature: Hex;
119+
120+
bid: BidParams;
121+
};
99122
/**
100123
* All the parameters necessary to represent an opportunity
101124
*/

express_relay/sdk/python/express_relay/client.py

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
BidStatusUpdate,
2121
ClientMessage,
2222
Bid,
23+
OpportunityBid,
2324
OpportunityParams,
2425
Address,
2526
Bytes32,
2627
TokenAmount,
2728
OpportunityBidParams,
2829
)
30+
from eth_account.datastructures import SignedMessage
2931
from express_relay.constants import (
3032
OPPORTUNITY_ADAPTER_CONFIGS,
3133
EXECUTION_PARAMS_TYPESTRING,
@@ -491,25 +493,31 @@ def make_adapter_calldata(
491493
return calldata
492494

493495

494-
def sign_bid(
495-
opportunity: Opportunity, bid_params: OpportunityBidParams, private_key: str
496-
) -> Bid:
496+
def get_opportunity_adapter_config(chain_id: str):
497+
opportunity_adapter_config = OPPORTUNITY_ADAPTER_CONFIGS.get(chain_id)
498+
if not opportunity_adapter_config:
499+
raise ExpressRelayClientException(
500+
f"Opportunity adapter config not found for chain id {chain_id}"
501+
)
502+
return opportunity_adapter_config
503+
504+
505+
def get_signature(
506+
opportunity: Opportunity,
507+
bid_params: OpportunityBidParams,
508+
private_key: str,
509+
) -> SignedMessage:
497510
"""
498-
Constructs a signature for a searcher's bid and returns the Bid object to be submitted to the server.
511+
Constructs a signature for a searcher's bid and opportunity.
499512
500513
Args:
501514
opportunity: An object representing the opportunity, of type Opportunity.
502515
bid_params: An object representing the bid parameters, of type OpportunityBidParams.
503516
private_key: A 0x-prefixed hex string representing the searcher's private key.
504517
Returns:
505-
A Bid object, representing the transaction to submit to the server. This object contains the searcher's signature.
518+
A SignedMessage object, representing the signature of the searcher's bid.
506519
"""
507-
508-
opportunity_adapter_config = OPPORTUNITY_ADAPTER_CONFIGS.get(opportunity.chain_id)
509-
if not opportunity_adapter_config:
510-
raise ExpressRelayClientException(
511-
f"Opportunity adapter config not found for chain id {opportunity.chain_id}"
512-
)
520+
opportunity_adapter_config = get_opportunity_adapter_config(opportunity.chain_id)
513521
domain_data = {
514522
"name": "Permit2",
515523
"chainId": opportunity_adapter_config.chain_id,
@@ -582,8 +590,63 @@ def sign_bid(
582590
private_key, domain_data, message_types, message_data
583591
)
584592

593+
return signed_typed_data
594+
595+
596+
def sign_opportunity_bid(
597+
opportunity: Opportunity,
598+
bid_params: OpportunityBidParams,
599+
private_key: str,
600+
) -> OpportunityBid:
601+
"""
602+
Constructs a signature for a searcher's bid and returns the OpportunityBid object to be submitted to the server.
603+
604+
Args:
605+
opportunity: An object representing the opportunity, of type Opportunity.
606+
bid_params: An object representing the bid parameters, of type OpportunityBidParams.
607+
private_key: A 0x-prefixed hex string representing the searcher's private key.
608+
Returns:
609+
A OpportunityBid object, representing the transaction to submit to the server. This object contains the searcher's signature.
610+
"""
611+
executor = Account.from_key(private_key).address
612+
opportunity_bid = OpportunityBid(
613+
opportunity_id=opportunity.opportunity_id,
614+
permission_key=opportunity.permission_key,
615+
amount=bid_params.amount,
616+
deadline=bid_params.deadline,
617+
nonce=bid_params.nonce,
618+
executor=executor,
619+
signature=get_signature(opportunity, bid_params, private_key),
620+
)
621+
622+
return opportunity_bid
623+
624+
625+
def sign_bid(
626+
opportunity: Opportunity, bid_params: OpportunityBidParams, private_key: str
627+
) -> Bid:
628+
"""
629+
Constructs a signature for a searcher's bid and returns the Bid object to be submitted to the server.
630+
631+
Args:
632+
opportunity: An object representing the opportunity, of type Opportunity.
633+
bid_params: An object representing the bid parameters, of type OpportunityBidParams.
634+
private_key: A 0x-prefixed hex string representing the searcher's private key.
635+
Returns:
636+
A Bid object, representing the transaction to submit to the server. This object contains the searcher's signature.
637+
"""
638+
opportunity_adapter_config = get_opportunity_adapter_config(opportunity.chain_id)
639+
permitted = _get_permitted_tokens(
640+
opportunity.sell_tokens,
641+
bid_params.amount,
642+
opportunity.target_call_value,
643+
opportunity_adapter_config.weth,
644+
)
645+
executor = Account.from_key(private_key).address
646+
647+
signature = get_signature(opportunity, bid_params, private_key).signature
585648
calldata = make_adapter_calldata(
586-
opportunity, permitted, executor, bid_params, signed_typed_data.signature
649+
opportunity, permitted, executor, bid_params, signature
587650
)
588651

589652
return Bid(

express_relay/sdk/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "express-relay"
3-
version = "0.8.0"
3+
version = "0.8.1"
44
description = "Utilities for searchers and protocols to interact with the Express Relay protocol."
55
authors = ["dourolabs"]
66
license = "Apache-2.0"

0 commit comments

Comments
 (0)