Skip to content

Commit

Permalink
feat: get bridge history for an address (#68)
Browse files Browse the repository at this point in the history
# Summary
Added method to fetch bridge transactions history of an address

# Closes: #67 

# Description
The method should return aggregated data from all bridges that are
supported by Sprinter API. Right now, only Sygma is added but later
other bridge will be added

# Chores:
- [ ] Unit tests
- [x] TS Docs / Comments

---------

Co-authored-by: Saad Ahmed Siddiqui <[email protected]>
  • Loading branch information
BeroBurny and saadahmsiddiqui authored Oct 24, 2024
1 parent d6b1e9c commit 4d524c6
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 31 deletions.
52 changes: 37 additions & 15 deletions packages/react/lib/context.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {createContext, ReactNode, useEffect, useState} from "react";
import {Sprinter} from "@chainsafe/sprinter-sdk";
import {useTokens} from "./internal/useTokens.ts";
import {useChains} from "./internal/useChains.ts";
import {useBalances} from "./internal/useBalances.ts";
import {useTransfers} from "./internal/useTransfers.ts";
import { createContext, ReactNode, useEffect, useState } from "react";
import { Sprinter } from "@chainsafe/sprinter-sdk";
import { useTokens } from "./internal/useTokens.ts";
import { useChains } from "./internal/useChains.ts";
import { useBalances } from "./internal/useBalances.ts";
import { useTransfers } from "./internal/useTransfers.ts";

type SprinterContext = ReturnType<typeof useBalances> & ReturnType<typeof useTokens> & ReturnType<typeof useChains> & ReturnType<typeof useTransfers>;
type SprinterContext = ReturnType<typeof useBalances> &
ReturnType<typeof useTokens> &
ReturnType<typeof useChains> &
ReturnType<typeof useTransfers>;

export const Context = createContext<SprinterContext | null>(null);

Expand Down Expand Up @@ -42,7 +45,7 @@ interface SprinterContextProps {
* ```
*/
export function SprinterContext({ children, baseUrl }: SprinterContextProps) {
const [sprinter] = useState(new Sprinter({baseUrl}));
const [sprinter] = useState(new Sprinter({ baseUrl }));

/** Balances */
const { balances, getUserBalances } = useBalances(sprinter);
Expand All @@ -54,18 +57,37 @@ export function SprinterContext({ children, baseUrl }: SprinterContextProps) {
const { chains, getAvailableChains } = useChains(sprinter);

/** Solutions */
const { solution, getTransfer, getTransferWithHook, getPoolAssetOnDestination, getPoolAssetOnDestinationWithHook } = useTransfers(sprinter);
const {
solution,
getTransfer,
getTransferWithHook,
getPoolAssetOnDestination,
getPoolAssetOnDestinationWithHook,
} = useTransfers(sprinter);

/** Initialization */
useEffect(() => {
getAvailableTokens();
getAvailableChains();
}, [sprinter]);

return <Context.Provider value={{
balances, getUserBalances,
tokens, getAvailableTokens,
chains, getAvailableChains,
solution, getTransfer, getTransferWithHook, getPoolAssetOnDestination, getPoolAssetOnDestinationWithHook,
}}>{children}</Context.Provider>;
return (
<Context.Provider
value={{
balances,
getUserBalances,
tokens,
getAvailableTokens,
chains,
getAvailableChains,
solution,
getTransfer,
getTransferWithHook,
getPoolAssetOnDestination,
getPoolAssetOnDestinationWithHook,
}}
>
{children}
</Context.Provider>
);
}
34 changes: 25 additions & 9 deletions packages/react/lib/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useCallback, useContext} from "react";
import {Context} from "./context.tsx";
import {Address} from "@chainsafe/sprinter-sdk";
import {BalancesEntry} from "./internal/useBalances.ts";
import { useCallback, useContext } from "react";
import { Context } from "./context.tsx";
import { Address } from "@chainsafe/sprinter-sdk";
import { BalancesEntry } from "./internal/useBalances.ts";

/**
* A hook to access the full Sprinter context, including balances, tokens, chains, and transfer solutions.
Expand All @@ -20,7 +20,7 @@ import {BalancesEntry} from "./internal/useBalances.ts";
export function useSprinter() {
const context = useContext(Context);

if (!context) throw new Error('Sprinter Context is not defined');
if (!context) throw new Error("Sprinter Context is not defined");

return context;
}
Expand Down Expand Up @@ -77,10 +77,14 @@ const balancesEmptyState = {
* ```
*/
export function useSprinterBalances(account: Address) {
const { balances: _balances, getUserBalances: _getUserBalances } = useSprinter();
const { balances: _balances, getUserBalances: _getUserBalances } =
useSprinter();

const balances: BalancesEntry = _balances[account] || balancesEmptyState;
const getUserBalances = useCallback(() => _getUserBalances(account), [account]);
const getUserBalances = useCallback(
() => _getUserBalances(account),
[account]
);

return { balances, getUserBalances };
}
Expand Down Expand Up @@ -259,6 +263,18 @@ export function useSprinterChains() {
* ```
*/
export function useSprinterTransfers() {
const { solution, getTransfer, getTransferWithHook, getPoolAssetOnDestination, getPoolAssetOnDestinationWithHook } = useSprinter();
return { solution, getTransfer, getTransferWithHook, getPoolAssetOnDestination, getPoolAssetOnDestinationWithHook };
const {
solution,
getTransfer,
getTransferWithHook,
getPoolAssetOnDestination,
getPoolAssetOnDestinationWithHook,
} = useSprinter();
return {
solution,
getTransfer,
getTransferWithHook,
getPoolAssetOnDestination,
getPoolAssetOnDestinationWithHook,
};
}
21 changes: 14 additions & 7 deletions packages/react/src/Action.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import {useSprinterBalances} from "../lib/hooks.ts";
import { useSprinterBalances } from "../lib/hooks.ts";

export function Action() {
const hook = useSprinterBalances("0x3E101Ec02e7A48D16DADE204C96bFF842E7E2519");
const hook = useSprinterBalances(
"0x3E101Ec02e7A48D16DADE204C96bFF842E7E2519"
);

return (<button onClick={() => {
hook.getUserBalances();
hook.getUserBalances();
}}>Action</button>)
}
return (
<button
onClick={() => {
hook.getUserBalances();
}}
>
Action
</button>
);
}
46 changes: 46 additions & 0 deletions packages/sdk/src/helpers/getBridgeHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Environment } from "../enums";
import { getTransfers } from "../sygma/api";
import type { Status, SygmaTransfer } from "../sygma/types";
import type { Address } from "../types";

interface History {
originTx: string;
originName: string;
destinationTx?: string;
destinationName: string;
amount: string;
tokenSymbol: string;
status: Status;
}

function handleSygmaResponseEntry(entry: SygmaTransfer): History {
return {
originTx: entry.deposit?.txHash || "0x0",
originName: entry.fromDomain.name,
destinationTx: entry.execution?.txHash,
destinationName: entry.toDomain.name,
amount: entry.amount,
tokenSymbol: entry.fee.tokenSymbol,
status: entry.status,
};
}

/**
* Returns bridging history
* for an address
* @param {Address} address
* @param {Environment} environment
* @returns {Promise<History[]>}
*/
export async function getBridgeHistory(
address: Address,
environment: Environment = Environment.MAINNET,
): Promise<History[]> {
// TODO: add logic for all supported bridges
const transactions = await getTransfers(address, environment).then(
(sygmaTransfers) =>
sygmaTransfers.map((transfer) => handleSygmaResponseEntry(transfer)),
);

return transactions;
}
1 change: 1 addition & 0 deletions packages/sdk/src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { experimental_getTrackingUrl } from "./getTrackingUrl";
export { getBridgeHistory } from "./getBridgeHistory";
1 change: 1 addition & 0 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { setBaseUrl, BASE_URL } from "./api";
export * as api from "./api";
export { ChainType, Environment } from "./enums";
export { Sprinter } from "./sprinter";
export * from "./helpers";
29 changes: 29 additions & 0 deletions packages/sdk/src/sygma/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Environment } from "../enums";

import type { SygmaTransfer } from "./types";

const SYGMA_API_ENDPOINT: Record<Environment, string> = {
[Environment.MAINNET]: "https://api.buildwithsygma.com/",
[Environment.TESTNET]: "https://api.test.buildwithsygma.com/",
};

/**
* Returns list of sygma transfers for an address
* @param {`0x${string}`} address EVM address
* @param {Environment} environment TESTNET or MAINNET
* @returns {Promise<Array<SygmaTransfer>>}
*/
export async function getTransfers(
address: string,
environment: Environment,
): Promise<Array<SygmaTransfer>> {
const transfersPath = `/api/sender/${address}/transfers`;
const url = new URL(transfersPath, SYGMA_API_ENDPOINT[environment]);
url.searchParams.set("limit", "100");

const response: SygmaTransfer[] = await fetch(url.toString()).then(
(response): Promise<SygmaTransfer[]> => response.json(),
);

return response;
}
2 changes: 2 additions & 0 deletions packages/sdk/src/sygma/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./api";
export * from "./types";
67 changes: 67 additions & 0 deletions packages/sdk/src/sygma/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
export enum Status {
pending = "pending",
executed = "executed",
failed = "failed",
}

export interface SygmaTransfer {
id: string;
depositNonce: number;
resource: Resource;
fromDomain: Domain;
fromDomainId: number;
toDomain: Domain;
toDomainId: number;
sender: string;
destination: string;
amount: string;
timestamp?: string;
status: Status;
deposit?: Deposit;
execution?: Execution;
fee: Fee;
resourceID: string;
usdValue: number;
accountId: string;
}

export interface Resource {
id: string;
type: string;
}

export interface Domain {
id: string;
name: string;
lastIndexedBlock: string;
}

export interface Deposit {
id: string;
transferId: string;
type: string;
txHash: string;
blockNumber: string;
depositData: string;
handlerResponse: string;
timestamp: string;
}

export interface Execution {
id: string;
transferId: string;
type: string;
txHash: string;
blockNumber: string;
executionEvent: string;
timestamp: string;
}

export interface Fee {
amount: string;
id: string;
tokenAddress: string;
tokenSymbol: string;
transferId: string;
decimals: number;
}

0 comments on commit 4d524c6

Please sign in to comment.