Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 52 additions & 32 deletions packages/sdk/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
ChainID,
ContractSolutionOptions,
FailedSolution,
FetchOptions,
FungibleToken,
FungibleTokenBalance,
Solution,
Expand All @@ -21,9 +22,12 @@ export function setBaseUrl(url: string): void {
BASE_URL = url;
}

export async function getSupportedChains(): Promise<Chain[]> {
const url = new URL("/networks", BASE_URL);
const response = await fetch(url).then(
export async function getSupportedChains({
baseUrl,
signal,
}: FetchOptions = {}): Promise<Chain[]> {
const url = new URL("/networks", baseUrl || BASE_URL);
const response = await fetch(url, { signal }).then(
(response) => response.json() as unknown as { data: Chain[] },
);

Expand All @@ -32,18 +36,25 @@ export async function getSupportedChains(): Promise<Chain[]> {

export async function getChainTokens(
chainID: ChainID,
{ baseUrl, signal }: FetchOptions = {},
): Promise<FungibleToken[]> {
const url = new URL(`/networks/${chainID}/assets/fungible`, BASE_URL);
const response = await fetch(url).then(
const url = new URL(
`/networks/${chainID}/assets/fungible`,
baseUrl || BASE_URL,
);
const response = await fetch(url, { signal }).then(
(response) => response.json() as unknown as { data: FungibleToken[] },
);

return response.data;
}

export async function getFungibleTokens(): Promise<FungibleToken[]> {
const url = new URL("/assets/fungible", BASE_URL);
const response = await fetch(url).then(
export async function getFungibleTokens({
baseUrl,
signal,
}: FetchOptions = {}): Promise<FungibleToken[]> {
const url = new URL("/assets/fungible", baseUrl || BASE_URL);
const response = await fetch(url, { signal }).then(
(response) => response.json() as unknown as { data: FungibleToken[] },
);

Expand All @@ -52,38 +63,43 @@ export async function getFungibleTokens(): Promise<FungibleToken[]> {

export async function getFungibleToken(
token: TokenSymbol,
{ baseUrl, signal }: FetchOptions = {},
): Promise<FungibleToken> {
const url = new URL(`/assets/fungible/${token}`, BASE_URL);
return await fetch(url).then(
const url = new URL(`/assets/fungible/${token}`, baseUrl || BASE_URL);
return await fetch(url, { signal }).then(
(response) => response.json() as unknown as FungibleToken,
);
}

export async function getUserFungibleTokens(
address: Address,
token: TokenSymbol,
{ baseUrl, signal }: FetchOptions = {},
): Promise<FungibleTokenBalance[]> {
const url = new URL(
`/accounts/${address}/assets/fungible/${token}`,
BASE_URL,
baseUrl || BASE_URL,
);
const response = await fetch(url).then(
const response = await fetch(url, { signal }).then(
(response) =>
response.json() as unknown as { data: FungibleTokenBalance[] },
);

return response.data;
}

export async function getSolution({
account,
destinationChain,
token,
amount,
threshold,
whitelistedSourceChains,
}: SolutionOptions): Promise<SolutionResponse> {
const url = new URL("/solutions/aggregation", BASE_URL);
export async function getSolution(
{
account,
destinationChain,
token,
amount,
threshold,
whitelistedSourceChains,
}: SolutionOptions,
{ baseUrl, signal }: FetchOptions = {},
): Promise<SolutionResponse> {
const url = new URL("/solutions/aggregation", baseUrl || BASE_URL);

url.searchParams.set("account", account);
url.searchParams.set("destination", String(destinationChain));
Expand All @@ -97,7 +113,7 @@ export async function getSolution({
whitelistedSourceChains.join(","),
);

const response = await fetch(url).then(
const response = await fetch(url, { signal }).then(
(response) =>
response.json() as unknown as { data: Solution[] } | FailedSolution,
);
Expand All @@ -106,18 +122,22 @@ export async function getSolution({
return response.data;
}

export async function getContractSolution({
account,
destinationChain,
token,
amount,
contractCall,
threshold,
whitelistedSourceChains,
}: ContractSolutionOptions): Promise<SolutionResponse> {
const url = new URL("/solutions/aggregation", BASE_URL);
export async function getContractSolution(
{
account,
destinationChain,
token,
amount,
contractCall,
threshold,
whitelistedSourceChains,
}: ContractSolutionOptions,
{ baseUrl, signal }: FetchOptions = {},
): Promise<SolutionResponse> {
const url = new URL("/solutions/aggregation", baseUrl || BASE_URL);

const response = await fetch(url, {
signal,
method: "POST",
body: JSON.stringify({
account,
Expand Down
62 changes: 40 additions & 22 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
Address,
Chain,
ContractSolutionOptions,
FetchOptions,
FungibleToken,
FungibleTokenBalance,
SolutionOptions,
Expand All @@ -31,26 +32,41 @@ class Sprinter {
#tokens?: FungibleToken[];
#chains?: Chain[];

constructor(provider: EIP1193Provider) {
#fetchOptions: Omit<FetchOptions, "signal">;

constructor(
provider: EIP1193Provider,
fetchOptions: Omit<FetchOptions, "signal"> = {},
) {
this.#provider = provider;
this.#fetchOptions = fetchOptions;
}

public async getAvailableTokens(): Promise<FungibleToken[]> {
if (!this.#tokens) this.#tokens = await getFungibleTokens();
public async getAvailableTokens(
options: FetchOptions = {},
): Promise<FungibleToken[]> {
if (!this.#tokens)
this.#tokens = await getFungibleTokens(this.makeFetchOptions(options));
return this.#tokens;
}

public async getAvailableChains(): Promise<Chain[]> {
if (!this.#chains) this.#chains = await getSupportedChains();
public async getAvailableChains(
options: FetchOptions = {},
): Promise<Chain[]> {
if (!this.#chains)
this.#chains = await getSupportedChains(this.makeFetchOptions(options));
return this.#chains;
}

public async getUserBalances(tokens?: FungibleToken[]): Promise<{
public async getUserBalances(
tokens?: FungibleToken[],
options: FetchOptions = {},
): Promise<{
[sybol: TokenSymbol]: { balances: FungibleTokenBalance[]; total: string };
}> {
const account = await this.getAccount();

const tokenList = tokens || (await this.getAvailableTokens());
const tokenList = tokens || (await this.getAvailableTokens(options));

const balances = await Promise.all(
tokenList.map((token) =>
Expand Down Expand Up @@ -83,36 +99,34 @@ class Sprinter {
public async getSolution(
settings: Omit<ContractSolutionOptions, "account">,
targetAccount?: Address,
options?: FetchOptions,
): Promise<SolutionResponse>;
public async getSolution(
settings: Omit<SolutionOptions, "account">,
targetAccount?: Address,
options?: FetchOptions,
): Promise<SolutionResponse>;
public async getSolution(
settings: unknown,
targetAccount?: Address,
options?: FetchOptions,
): Promise<SolutionResponse> {
const account = targetAccount || (await this.getAccount());

if (typeof settings !== "object" || settings === null)
throw new Error("Missing settings object");

if ("contractCall" in settings)
return await getContractSolution(<ContractSolutionOptions>{
...settings,
account,
});
return await getSolution(<SolutionOptions>{ ...settings, account });
}

private isContractSolutionOptions(
settings: unknown,
): settings is Omit<ContractSolutionOptions, "account"> {
return (
settings != undefined &&
typeof settings === "object" &&
"contractCall" in settings &&
typeof settings.contractCall === "object"
return await getContractSolution(
<ContractSolutionOptions>{
...settings,
account,
},
this.makeFetchOptions(options || {}),
);
return await getSolution(
<SolutionOptions>{ ...settings, account },
this.makeFetchOptions(options || {}),
);
}

Expand All @@ -126,6 +140,10 @@ class Sprinter {

return account;
}

private makeFetchOptions(options: FetchOptions): FetchOptions {
return { ...this.#fetchOptions, ...options };
}
}

export { Sprinter, setBaseUrl, BASE_URL, EIP1193Provider };
5 changes: 5 additions & 0 deletions packages/sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,8 @@ export interface Transaction {
to: Address;
value: string;
}

export interface FetchOptions {
signal?: AbortSignal;
baseUrl?: string;
}