Skip to content

Commit

Permalink
Merge pull request #3295 from osmosis-labs/stage
Browse files Browse the repository at this point in the history
Publish Stage
  • Loading branch information
sunnya97 authored Jun 5, 2024
2 parents 92f3acd + c3f4260 commit 121a3a2
Show file tree
Hide file tree
Showing 72 changed files with 959 additions and 1,288 deletions.
2 changes: 1 addition & 1 deletion packages/bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@types/jest-in-case": "^1.0.6",
"jest-in-case": "^1.0.2",
"ts-jest": "^29.1.0",
"typescript": "^5.4.3"
"typescript": "^5.4.5"
},
"lint-staged": {
"*": [
Expand Down
1 change: 1 addition & 0 deletions packages/bridge/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ export enum BridgeError {
CreateEVMTxError = "CreateEVMTxError",
NoQuotesError = "NoQuotesError",
UnsupportedQuoteError = "UnsupportedQuoteError",
InsufficientAmount = "InsufficientAmountError",
}
235 changes: 235 additions & 0 deletions packages/bridge/src/ibc/__tests__/ibc-bridge-provider.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import { Int } from "@keplr-wallet/unit";
import { estimateGasFee } from "@osmosis-labs/tx";
import { CacheEntry } from "cachified";
import { LRUCache } from "lru-cache";

import { MockAssetLists } from "../../__tests__/mock-asset-lists";
import { MockChains } from "../../__tests__/mock-chains";
import { BridgeQuoteError } from "../../errors";
import {
BridgeProviderContext,
BridgeQuote,
GetBridgeQuoteParams,
} from "../../interface";
import { IbcBridgeProvider } from "../index";

jest.mock("@osmosis-labs/tx", () => ({
estimateGasFee: jest.fn(),
}));

const mockContext: BridgeProviderContext = {
chainList: MockChains,
assetLists: MockAssetLists,
getTimeoutHeight: jest.fn().mockResolvedValue("1000-1000"),
env: "mainnet",
cache: new LRUCache<string, CacheEntry>({ max: 10 }),
};

// deposit of ATOM from Cosmos Hub to Osmosis
const mockAtomToOsmosis: GetBridgeQuoteParams = {
fromChain: {
chainId: "cosmoshub-4",
chainType: "cosmos",
},
toChain: {
chainId: "osmosis-1",
chainType: "cosmos",
},
fromAsset: {
address: "uatom",
sourceDenom: "uatom",
denom: "ATOM",
decimals: 6,
},
toAsset: {
address:
"ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
sourceDenom: "uatom",
denom: "ATOM",
decimals: 6,
},
fromAmount: "1000000", // 1 ATOM
fromAddress: "osmo1...",
toAddress: "cosmos1...",
};

const mockAtomFromOsmosis: GetBridgeQuoteParams = {
fromChain: {
chainId: "osmosis-1",
chainType: "cosmos",
},
toChain: {
chainId: "cosmoshub-4",
chainType: "cosmos",
},
fromAsset: {
address:
"ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
sourceDenom: "uatom",
denom: "ATOM",
decimals: 6,
},
toAsset: {
address: "uatom",
sourceDenom: "uatom",
denom: "ATOM",
decimals: 6,
},
fromAmount: "1000000", // 1 ATOM
fromAddress: "osmo1...",
toAddress: "cosmos1...",
};

describe("IbcBridgeProvider", () => {
let provider: IbcBridgeProvider;

beforeEach(() => {
provider = new IbcBridgeProvider(mockContext);
jest.clearAllMocks();
});

it("should throw an error if fromChain or toChain is not cosmos", async () => {
const invalidParams: GetBridgeQuoteParams = {
...mockAtomToOsmosis,
fromChain: { chainId: 1, chainType: "evm" },
};
await expect(provider.getQuote(invalidParams)).rejects.toThrow(
BridgeQuoteError
);
});

it("should throw an error if gas cost exceeds transfer amount", async () => {
(estimateGasFee as jest.Mock).mockResolvedValue({
amount: [
{
amount: new Int(mockAtomFromOsmosis.fromAmount)
.add(new Int(100))
.toString(),
denom: "uatom",
isNeededForTx: true,
},
],
});

await expect(provider.getQuote(mockAtomToOsmosis)).rejects.toThrow(
BridgeQuoteError
);
});

it("should return a valid BridgeQuote", async () => {
(estimateGasFee as jest.Mock).mockResolvedValue({
amount: [{ amount: "5000", denom: "uatom", isNeededForTx: true }],
});

const quote: BridgeQuote = await provider.getQuote(mockAtomToOsmosis);

expect(quote).toHaveProperty("input");
expect(quote).toHaveProperty("expectedOutput");
expect(quote).toHaveProperty("fromChain");
expect(quote.fromChain.chainId).toBe(mockAtomToOsmosis.fromChain.chainId);
expect(quote).toHaveProperty("toChain");
expect(quote.toChain.chainId).toBe(mockAtomToOsmosis.toChain.chainId);
expect(quote).toHaveProperty("transferFee");
expect(quote.transferFee.amount).toBe("0");
expect(quote).toHaveProperty("estimatedTime");
expect(quote).toHaveProperty("estimatedGasFee");
expect(quote).toHaveProperty("estimatedGasFee");
expect(quote.estimatedGasFee!.amount).toBe("5000");
expect(quote).toHaveProperty("transactionRequest");
});

it("should calculate the correct toAmount when gas fee is needed for tx", async () => {
(estimateGasFee as jest.Mock).mockResolvedValue({
amount: [{ amount: "5000", denom: "uatom", isNeededForTx: true }],
});

const quote: BridgeQuote = await provider.getQuote(mockAtomToOsmosis);

expect(quote.expectedOutput.amount).toBe(
new Int(mockAtomToOsmosis.fromAmount).sub(new Int("5000")).toString()
);
});

it("should calculate the correct toAmount when gas fee is not needed for tx", async () => {
(estimateGasFee as jest.Mock).mockResolvedValue({
amount: [{ amount: "5000", denom: "uatom", isNeededForTx: false }],
});

const quote: BridgeQuote = await provider.getQuote(mockAtomToOsmosis);

expect(quote.expectedOutput.amount).toBe(mockAtomToOsmosis.fromAmount);
});

describe("getIbcSource", () => {
// extend class to access protected method
class ExtendedIbcBridgeProvider extends IbcBridgeProvider {
public getIbcSourcePublic(params: GetBridgeQuoteParams): {
sourceChannel: string;
sourcePort: string;
sourceDenom: string;
} {
return this.getIbcSource(params);
}
}

let provider: ExtendedIbcBridgeProvider;

beforeEach(() => {
provider = new ExtendedIbcBridgeProvider(mockContext);
});

it("should return the correct channel, port and denom for transfer from source", () => {
const result = provider.getIbcSourcePublic(mockAtomFromOsmosis);

expect(result.sourceChannel).toBe("channel-0");
expect(result.sourcePort).toBe("transfer");
expect(result.sourceDenom).toBe(
"ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"
);
});

it("should return the correct channel, port and denom for transfer from counterparty", () => {
const result = provider.getIbcSourcePublic(mockAtomToOsmosis);

expect(result.sourceChannel).toBe("channel-141");
expect(result.sourcePort).toBe("transfer");
expect(result.sourceDenom).toBe("uatom");
});

it("should throw if asset not found", () => {
const invalidParams: GetBridgeQuoteParams = {
...mockAtomToOsmosis,
toAsset: {
...mockAtomToOsmosis.toAsset,
sourceDenom: "not-found",
},
fromAsset: {
...mockAtomToOsmosis.fromAsset,
sourceDenom: "not-found",
},
};

expect(() => provider.getIbcSourcePublic(invalidParams)).toThrow(
BridgeQuoteError
);
});

it("should throw if there's no transfer method", () => {
const invalidParams: GetBridgeQuoteParams = {
...mockAtomToOsmosis,
toAsset: {
...mockAtomToOsmosis.toAsset,
sourceDenom: "uosmo",
},
fromAsset: {
...mockAtomToOsmosis.fromAsset,
sourceDenom: "uosmo",
},
};

expect(() => provider.getIbcSourcePublic(invalidParams)).toThrow(
BridgeQuoteError
);
});
});
});
9 changes: 9 additions & 0 deletions packages/bridge/src/ibc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ export class IbcBridgeProvider implements BridgeProvider {
? new Int(params.fromAmount).sub(new Int(gasFee.amount)).toString()
: params.fromAmount;

if (new Int(toAmount).lte(new Int(0))) {
throw new BridgeQuoteError([
{
errorType: BridgeError.InsufficientAmount,
message: "Insufficient amount for fees",
},
]);
}

return {
input: {
amount: params.fromAmount,
Expand Down
2 changes: 1 addition & 1 deletion packages/proto-codecs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"devDependencies": {
"@cosmology/proto-parser": "^1.5.0",
"@cosmology/telescope": "^1.5.1",
"@types/node": "^18.16.3",
"@types/node": "^20.14.1",
"regenerator-runtime": "^0.13.11",
"rimraf": "^5.0.0",
"tsx": "^4.6.2"
Expand Down
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@types/jest-in-case": "^1.0.6",
"jest-in-case": "^1.0.2",
"ts-jest": "^29.1.0",
"typescript": "^5.4.3"
"typescript": "^5.4.5"
},
"lint-staged": {
"*": [
Expand Down
2 changes: 1 addition & 1 deletion packages/trpc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@types/jest-in-case": "^1.0.6",
"jest-in-case": "^1.0.2",
"ts-jest": "^29.1.0",
"typescript": "^5.4.3"
"typescript": "^5.4.5"
},
"lint-staged": {
"*": [
Expand Down
2 changes: 1 addition & 1 deletion packages/trpc/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { trpcMiddleware } from "./middleware";
/**
* Pass asset lists and chain list to be used cas context in backend service.
*/
export type CreateContextOptions = {
type CreateContextOptions = {
assetLists: AssetList[];
chainList: Chain[];
};
Expand Down
2 changes: 1 addition & 1 deletion packages/tx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"@types/jest-in-case": "^1.0.6",
"jest-in-case": "^1.0.2",
"ts-jest": "^29.1.0",
"typescript": "^5.4.3"
"typescript": "^5.4.5"
},
"lint-staged": {
"*": [
Expand Down
Loading

0 comments on commit 121a3a2

Please sign in to comment.