Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix OKX integration approve transaction #104

Merged
merged 12 commits into from
Jan 27, 2025

Conversation

mstrug
Copy link
Collaborator

@mstrug mstrug commented Jan 24, 2025

Description

Added implementation for getting OKX smart contract address which is used during swap of a particular token handled by the OKX dex. This address needs to be used to set allowances (returned in dex::Swap::allowance).

Implementation

OKX smart contract address is taken from /approve-transaction endpoint (documentation) for particular sell token. From my observations I saw at least two different smart contract addresses returned by this endpoint (for different tokens, for one token always same address was returned).

Added generic function send() which handles preparation, http roundtrip and errors handling for different OKX API calls.

Map of: token address <=> OKX smart contract address for that token is stored in non-persistent LRU cache (size of 1000 items). Rust interior mutability pattern was used to simplify the implementation over the whole application (only changes to the okx module was needed).

Tests

Updated tests with additional endpoint requests mocks, added assert to api call test which verifies that allowance address.

@mstrug mstrug marked this pull request as ready for review January 24, 2025 14:21
@mstrug
Copy link
Collaborator Author

mstrug commented Jan 24, 2025

Was tested on base-staging (image sha256:33c21d18e9e59bc6f681639307d38c8d7540829468e2a33ac54068cd470c17b0).

Comment on lines 129 to 152
let existing_dex_contract_address = self
.dex_approved_addresses
.write()
.await
.get(&order.sell)
.cloned();

let dex_contract_address = match existing_dex_contract_address {
Some(value) => value,
None => {
let query_approve_transaction =
dto::ApproveTransactionRequest::with_domain(self.defaults.chain_id, order);

let approve_transaction: dto::ApproveTransactionResponse = self
.send("approve-transaction", &query_approve_transaction)
.instrument(tracing::trace_span!("approve_transaction", id = %id))
.await?;

let address = eth::ContractAddress(approve_transaction.dex_contract_address);

self.dex_approved_addresses
.write()
.await
.put(order.sell, address);
Copy link
Contributor

@squadgazzz squadgazzz Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache update needs to be done atomically, doesn't it? This will prevent sending duplicated approve-transaction calls. If so, blocking the whole collection should be avoided and a thread-safe LRU cache with segmentation blocking should be considered(moka).

})
}

async fn send<T, U>(&self, endpoint: &str, query: &T) -> Result<U, Error>
Copy link
Contributor

@squadgazzz squadgazzz Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The function signature can be improved to clearly state what it really does without requiring the reader to dive deeper into the implementation details.

Suggested change
async fn send<T, U>(&self, endpoint: &str, query: &T) -> Result<U, Error>
async fn send_get_request<T, U>(&self, api_path: &str, query: &T) -> Result<U, Error>

dto::ApproveTransactionRequest::with_domain(self.defaults.chain_id, order);

let approve_transaction: dto::ApproveTransactionResponse = self
.send("approve-transaction", &query_approve_transaction)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we'd be sending the /approve and /swap requests in parallel to reduce latency. Will not implement that in this PR to bring it to prod earlier.

src/infra/dex/okx/mod.rs Outdated Show resolved Hide resolved
self.quote(&query)

let quote: dto::SwapResponse = self
.send("swap", &query)
.instrument(tracing::trace_span!("quote", id = %id))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: instead of instrumenting the /swap and /approve call task separately they could just be put into the same function which gets instrumented as a whole. Would likely also help with readability.

@MartinquaXD MartinquaXD enabled auto-merge (squash) January 27, 2025 12:40
@MartinquaXD MartinquaXD merged commit b9b2fb8 into main Jan 27, 2025
3 checks passed
@MartinquaXD MartinquaXD deleted the fix/okx-integration-approve-transaction branch January 27, 2025 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants