-
Notifications
You must be signed in to change notification settings - Fork 7
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
add polling to sep6 #79
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,10 @@ import { AxiosInstance } from "axios"; | |
import queryString from "query-string"; | ||
|
||
import { Anchor } from "../Anchor"; | ||
import { ServerRequestFailedError } from "../Exceptions"; | ||
import { | ||
ServerRequestFailedError, | ||
MissingTransactionIdError, | ||
} from "../Exceptions"; | ||
import { | ||
Sep6Info, | ||
Sep6Params, | ||
|
@@ -11,7 +14,17 @@ import { | |
Sep6DepositResponse, | ||
Sep6WithdrawResponse, | ||
Sep6ExchangeParams, | ||
Sep6Transaction, | ||
GetTransactionParams, | ||
GetTransactionsParams, | ||
WatcherSepType, | ||
} from "../Types"; | ||
import { | ||
Watcher, | ||
_getTransactionsForAsset, | ||
_getTransactionBy, | ||
} from "../Watcher"; | ||
import { camelToSnakeCaseObject } from "../Utils"; | ||
|
||
/** | ||
* Flow for creating deposits and withdrawals with an anchor using SEP-6. | ||
|
@@ -177,4 +190,104 @@ export class Sep6 { | |
throw e; | ||
} | ||
} | ||
|
||
/** | ||
* Creates a new instance of the Watcher class, to watch sep6 transactions. | ||
* @returns {Watcher} A new Watcher instance. | ||
*/ | ||
watcher(): Watcher { | ||
return new Watcher(this.anchor, WatcherSepType.SEP6); | ||
} | ||
|
||
/** | ||
* Get account's sep6 transactions specified by asset and other params. | ||
* @param {GetTransactionsParams} params - The Get Transactions params. | ||
* @param {AuthToken} params.authToken - The authentication token for the account authenticated with the anchor. | ||
* @param {string} params.assetCode - The target asset to query for. | ||
* @param {string} params.account - The stellar account public key involved in the transactions. If the service requires SEP-10 | ||
* authentication, this parameter must match the authenticated account. | ||
* @param {string} [params.noOlderThan] - The response should contain transactions starting on or after this date & time. | ||
* @param {string} [params.limit] - The response should contain at most 'limit' transactions. | ||
* @param {string} [params.kind] - The kind of transaction that is desired. E.g.: 'deposit', 'withdrawal', 'depo | ||
* -exchange', 'withdrawal-exchange'. | ||
* @param {string} [params.pagingId] - The response should contain transactions starting prior to this ID (exclusive). | ||
* @param {string} [params.lang] - The desired language (localization), it can also accept locale in the format 'en-US'. | ||
* @returns {Promise<Sep6Transaction[]>} A list of transactions as requested by the client, sorted in time-descending order. | ||
* @throws {InvalidTransactionsResponseError} Anchor returns an invalid response. | ||
* @throws {ServerRequestFailedError} If server request fails. | ||
*/ | ||
async getTransactionsForAsset({ | ||
authToken, | ||
assetCode, | ||
account, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need an account here? Can't we get it from the auth token? I think in real implementations there are no use cases where account != SEP-10 account There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sep 6 requires it. If you don't give an account that matches the auth token account it gives a 403. I'm not with my laptop right now but I can link to it on the sep 6 spec tonight There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm suggesting to omit it in the method parameter, and instead always pass account from the auth token to the anchor. (that's how anchor platform and polaris work, as they require authentication in /transaction endpoint, therefore account must always match authenticated account) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ahh I see. Ya I agree that's better. We have this ticket for tying the account to the auth token (right now it's just a string) https://stellarorg.atlassian.net/browse/WAL-1152?atlOrigin=eyJpIjoiYjMxYWY2NDRkY2IyNGE4MGE4MjlkNWM3NDFmNzVlMDQiLCJwIjoiaiJ9 I'll make a note on the ticket to change this as well to always pull from the auth token |
||
noOlderThan, | ||
limit, | ||
kind, | ||
pagingId, | ||
lang = this.anchor.language, | ||
}: GetTransactionsParams & { account: string }): Promise<Sep6Transaction[]> { | ||
const toml = await this.anchor.sep1(); | ||
const transferServerEndpoint = toml.transferServer; | ||
|
||
// Let's convert all params to snake case for the API call | ||
const apiParams = camelToSnakeCaseObject({ | ||
assetCode, | ||
account, | ||
noOlderThan, | ||
limit, | ||
kind, | ||
pagingId, | ||
lang, | ||
}); | ||
|
||
return _getTransactionsForAsset<Sep6Transaction>( | ||
authToken, | ||
apiParams, | ||
transferServerEndpoint, | ||
this.httpClient, | ||
); | ||
} | ||
|
||
/** | ||
* Get single sep6 transaction's current status and details from the anchor. | ||
* @param {GetTransactionParams} params - The Get Transactions params. | ||
* @param {AuthToken} params.authToken - The authentication token for the account authenticated with the anchor. | ||
* @param {string} [params.id] - The transaction ID. | ||
* @param {string} [params.stellarTransactionId] - The Stellar transaction ID. | ||
* @param {string} [params.externalTransactionId] - The external transaction ID. | ||
* @param {string} [params.lang] - The language setting. | ||
* @returns {Promise<Sep6Transaction>} The transaction object. | ||
* @throws {MissingTransactionIdError} If none of the ID parameters is provided. | ||
* @throws {InvalidTransactionResponseError} If the anchor returns an invalid transaction response. | ||
* @throws {ServerRequestFailedError} If the server request fails. | ||
*/ | ||
async getTransactionBy({ | ||
authToken, | ||
id, | ||
stellarTransactionId, | ||
externalTransactionId, | ||
lang = this.anchor.language, | ||
}: GetTransactionParams): Promise<Sep6Transaction> { | ||
if (!id && !stellarTransactionId && !externalTransactionId) { | ||
throw new MissingTransactionIdError(); | ||
} | ||
|
||
const toml = await this.anchor.sep1(); | ||
const transferServerEndpoint = toml.transferServer; | ||
|
||
// Let's convert all params to snake case for the API call | ||
const apiParams = camelToSnakeCaseObject({ | ||
id, | ||
stellarTransactionId, | ||
externalTransactionId, | ||
lang, | ||
}); | ||
|
||
return _getTransactionBy<Sep6Transaction>( | ||
authToken, | ||
apiParams, | ||
transferServerEndpoint, | ||
this.httpClient, | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,7 @@ export interface ProcessingAnchorTransaction extends BaseTransaction { | |
amount_out_asset?: string; | ||
amount_out: string; | ||
amount_fee_asset?: string; | ||
quote_id?: string; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note sure why this was left off (I may have just missed it before) |
||
amount_fee: string; | ||
completed_at?: string; | ||
stellar_transaction_id?: string; | ||
|
@@ -60,6 +61,23 @@ export interface WithdrawTransaction extends ProcessingAnchorTransaction { | |
withdraw_anchor_account: string; | ||
} | ||
|
||
export type Sep6Transaction = DepositTransaction & | ||
WithdrawTransaction & { | ||
from?: string; | ||
external_extra?: string; | ||
external_extra_text?: string; | ||
required_info_message?: string; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
required_info_updates?: any; | ||
required_customer_info_message?: string; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
required_customer_info_updates?: any; | ||
instructions?: { | ||
value: string; | ||
description: string; | ||
}; | ||
}; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-empty-interface | ||
export interface ErrorTransaction | ||
extends Optional<DepositTransaction & WithdrawTransaction> {} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { AxiosInstance } from "axios"; | ||
import queryString from "query-string"; | ||
|
||
import { | ||
ServerRequestFailedError, | ||
InvalidTransactionsResponseError, | ||
InvalidTransactionResponseError, | ||
} from "../Exceptions"; | ||
|
||
export const _getTransactionsForAsset = async <T>( | ||
authToken: string, | ||
params: { [key: string]: string | number }, | ||
endpoint: string, | ||
client: AxiosInstance, | ||
): Promise<T[]> => { | ||
try { | ||
const resp = await client.get( | ||
`${endpoint}/transactions?${queryString.stringify(params)}`, | ||
{ | ||
headers: { | ||
Authorization: `Bearer ${authToken}`, | ||
}, | ||
}, | ||
); | ||
|
||
const transactions: T[] = resp.data.transactions; | ||
|
||
if (!transactions || !Array.isArray(transactions)) { | ||
throw new InvalidTransactionsResponseError(transactions); | ||
} | ||
|
||
return transactions; | ||
} catch (e) { | ||
throw new ServerRequestFailedError(e); | ||
} | ||
}; | ||
|
||
export const _getTransactionBy = async <T>( | ||
authToken: string, | ||
params: { [key: string]: string | number }, | ||
endpoint: string, | ||
client: AxiosInstance, | ||
): Promise<T> => { | ||
try { | ||
const resp = await client.get( | ||
`${endpoint}/transaction?${queryString.stringify(params)}`, | ||
{ | ||
headers: { | ||
Authorization: `Bearer ${authToken}`, | ||
}, | ||
}, | ||
); | ||
|
||
const transaction: T = resp.data.transaction; | ||
|
||
if (!transaction || Object.keys(transaction).length === 0) { | ||
throw new InvalidTransactionResponseError(transaction); | ||
} | ||
|
||
return transaction; | ||
} catch (e) { | ||
throw new ServerRequestFailedError(e); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
basically just moving this code to a helper function so can be used for both seps