-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
724 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
node_modules | ||
dist | ||
**/node_modules/** | ||
*.d.ts | ||
*.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { of, defer, tap, first, } from 'rxjs'; | ||
import { SecretNetworkClient, } from 'secretjs'; | ||
import { createFetchClient } from '~/client/services/createFetch'; | ||
import { DEFAULT_SECRET_LCD_ENDPOINT, SECRET_MAINNET_CHAIN_ID, } from '~/config'; | ||
/** | ||
* Create and returns Secret Network client | ||
* @param walletAccount not required for making public queries | ||
*/ | ||
const getSecretNetworkClient$ = ({ walletAccount, lcdEndpoint, chainId, }) => createFetchClient(defer(() => { | ||
if (walletAccount) { | ||
return of({ | ||
client: new SecretNetworkClient({ | ||
url: lcdEndpoint, | ||
wallet: walletAccount.signer, | ||
walletAddress: walletAccount.walletAddress, | ||
chainId, | ||
encryptionUtils: walletAccount.encryptionUtils, | ||
encryptionSeed: walletAccount.encryptionSeed, | ||
}), | ||
endpoint: lcdEndpoint, | ||
chainId, | ||
}); | ||
} | ||
return of({ | ||
client: new SecretNetworkClient({ | ||
url: lcdEndpoint, | ||
chainId, | ||
}), | ||
endpoint: lcdEndpoint, | ||
chainId, | ||
}); | ||
})); | ||
let activeClient; | ||
/** | ||
* Gets the active query client. If one does not exist, initialize it and stores it for | ||
* future use. | ||
* @param lcdEndpoint uses a default mainnet endpoint if one is not provided | ||
* @param chainId uses a default mainnet chainID if one is not provided | ||
*/ | ||
function getActiveQueryClient$(lcdEndpoint, chainId) { | ||
// check if a client exists and return it if it does | ||
// as long as the endpoint and chain Id haven't changed from previous. | ||
if (activeClient | ||
&& lcdEndpoint | ||
&& lcdEndpoint === activeClient.endpoint | ||
&& chainId | ||
&& chainId === activeClient.chainId) { | ||
return of(activeClient); | ||
} | ||
// if no endpoint/chainId is provided, assume mainnet and use defaults | ||
return getSecretNetworkClient$({ | ||
lcdEndpoint: lcdEndpoint !== null && lcdEndpoint !== void 0 ? lcdEndpoint : DEFAULT_SECRET_LCD_ENDPOINT, | ||
chainId: chainId !== null && chainId !== void 0 ? chainId : SECRET_MAINNET_CHAIN_ID, | ||
}).pipe(tap(({ client }) => { | ||
activeClient = { | ||
client, | ||
endpoint: lcdEndpoint !== null && lcdEndpoint !== void 0 ? lcdEndpoint : DEFAULT_SECRET_LCD_ENDPOINT, | ||
chainId: chainId !== null && chainId !== void 0 ? chainId : SECRET_MAINNET_CHAIN_ID, | ||
}; | ||
}), first()); | ||
} | ||
export { getSecretNetworkClient$, getActiveQueryClient$, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { first, defer, from, tap, } from 'rxjs'; | ||
import { createFetchClient } from '~/client/services/createFetch'; | ||
import { identifyQueryResponseErrors } from '~/errors'; | ||
/** | ||
* query the contract using a secret client | ||
*/ | ||
const secretClientContractQuery$ = ({ queryMsg, client, contractAddress, codeHash, }) => createFetchClient(defer(() => from(client.query.compute.queryContract({ | ||
contract_address: contractAddress, | ||
code_hash: codeHash, | ||
query: queryMsg, | ||
})))); | ||
/** | ||
* sets up the service observable for calling the querying with the secret client | ||
*/ | ||
const sendSecretClientContractQuery$ = ({ queryMsg, client, contractAddress, codeHash, }) => secretClientContractQuery$({ | ||
queryMsg, | ||
client, | ||
contractAddress, | ||
codeHash, | ||
}) | ||
.pipe(tap((response) => identifyQueryResponseErrors(response)), first()); | ||
export { sendSecretClientContractQuery$, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
import { catchError, of, switchMap, first, } from 'rxjs'; | ||
function createFetchClient(data$) { | ||
return data$.pipe(switchMap((response) => of(response)), first(), catchError((err) => { | ||
throw err; | ||
})); | ||
} | ||
function createFetch(data$) { | ||
return data$.pipe(switchMap((response) => __awaiter(this, void 0, void 0, function* () { | ||
if (response.ok) { | ||
return response.json(); | ||
} | ||
throw new Error('Fetch Error'); | ||
}))); | ||
} | ||
export { createFetchClient, createFetch, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// default endpoint to be used when alternative endpoint is not provided | ||
// this is a public endpoint and not guarantees can be provided about the performance | ||
const DEFAULT_SECRET_LCD_ENDPOINT = 'https://lcd.secret.express/'; | ||
const SECRET_MAINNET_CHAIN_ID = 'secret-4'; | ||
export { DEFAULT_SECRET_LCD_ENDPOINT, SECRET_MAINNET_CHAIN_ID, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* Query single price from the oracle contract | ||
*/ | ||
const msgQueryOraclePrice = (oracleKey) => ({ | ||
get_price: { | ||
key: oracleKey, | ||
}, | ||
}); | ||
/** | ||
* Query muliple prices from the oracle contract | ||
*/ | ||
const msgQueryOraclePrices = (oracleKeys) => ({ | ||
get_prices: { | ||
keys: oracleKeys, | ||
}, | ||
}); | ||
export { msgQueryOraclePrice, msgQueryOraclePrices, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { test, expect, } from 'vitest'; | ||
import { msgQueryOraclePrice, msgQueryOraclePrices, } from '~/contracts/definitions/oracle'; | ||
test('it test the form of the query oracle msg', () => { | ||
const output = { | ||
get_price: { | ||
key: 'MOCK_KEY', | ||
}, | ||
}; | ||
expect(msgQueryOraclePrice('MOCK_KEY')).toStrictEqual(output); | ||
}); | ||
test('it test the form of the query oracle msg', () => { | ||
const output = { | ||
get_prices: { | ||
keys: ['MOCK_KEY_1', 'MOCK_KEY_2'], | ||
}, | ||
}; | ||
expect(msgQueryOraclePrices(['MOCK_KEY_1', 'MOCK_KEY_2'])).toStrictEqual(output); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { switchMap, first, map, } from 'rxjs'; | ||
import { sendSecretClientContractQuery$ } from '~/client/services/clientServices'; | ||
import { getActiveQueryClient$ } from '~/client'; | ||
import { convertCoinFromUDenom } from '~/lib/utils'; | ||
import { msgQueryOraclePrice, msgQueryOraclePrices } from '~/contracts/definitions/oracle'; | ||
/** | ||
* Parses the contract price query into the app data model | ||
*/ | ||
const parsePriceFromContract = (response) => ({ | ||
oracleKey: response.key, | ||
rate: convertCoinFromUDenom(response.data.rate, 18), | ||
lastUpdatedBase: response.data.last_updated_base, | ||
lastUpdatedQuote: response.data.last_updated_quote, | ||
}); | ||
/** | ||
* Parses the contract prices query into the app data model | ||
*/ | ||
function parsePricesFromContract(pricesResponse) { | ||
return pricesResponse.reduce((prev, curr) => (Object.assign(Object.assign({}, prev), { [curr.key]: { | ||
oracleKey: curr.key, | ||
rate: convertCoinFromUDenom(curr.data.rate, 18), | ||
lastUpdatedBase: curr.data.last_updated_base, | ||
lastUpdatedQuote: curr.data.last_updated_quote, | ||
} })), {}); | ||
} | ||
/** | ||
* query the price of an asset using the oracle key | ||
*/ | ||
const queryPrice$ = ({ contractAddress, codeHash, oracleKey, lcdEndpoint, chainId, }) => getActiveQueryClient$(lcdEndpoint, chainId).pipe(switchMap(({ client }) => sendSecretClientContractQuery$({ | ||
queryMsg: msgQueryOraclePrice(oracleKey), | ||
client, | ||
contractAddress, | ||
codeHash, | ||
})), map((response) => parsePriceFromContract(response)), first()); | ||
/** | ||
* query multiple asset prices using oracle keys | ||
*/ | ||
const queryPrices$ = ({ contractAddress, codeHash, oracleKeys, lcdEndpoint, chainId, }) => getActiveQueryClient$(lcdEndpoint, chainId).pipe(switchMap(({ client }) => sendSecretClientContractQuery$({ | ||
queryMsg: msgQueryOraclePrices(oracleKeys), | ||
client, | ||
contractAddress, | ||
codeHash, | ||
})), map((response) => parsePricesFromContract(response)), first()); | ||
export { parsePriceFromContract, parsePricesFromContract, queryPrice$, queryPrices$, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { test, expect, vi, beforeAll, afterAll, } from 'vitest'; | ||
import { parsePriceFromContract, parsePricesFromContract, } from '~/contracts/services/oracle'; | ||
import priceResponse from '~/test/mocks/oracle/priceResponse.json'; | ||
import pricesResponse from '~/test/mocks/oracle/pricesResponse.json'; | ||
import BigNumber from 'bignumber.js'; | ||
import { of } from 'rxjs'; | ||
beforeAll(() => { | ||
vi.mock('~/contracts/definitions/oracle', () => ({ | ||
msgQueryOraclePrice: vi.fn(() => 'MSG_QUERY_ORACLE_PRICE'), | ||
msgQueryOraclePrices: vi.fn(() => 'MSG_QUERY_ORACLE_PRICES'), | ||
})); | ||
vi.mock('~/client/index', () => ({ | ||
getActiveQueryClient$: vi.fn(() => of('CLIENT')), | ||
})); | ||
vi.mock('~/client/services/clientServices', () => ({ | ||
sendSecretClientContractQuery$: vi.fn(() => of()), | ||
})); | ||
vi.mock('~/client/services/clientServices', () => ({ | ||
sendSecretClientContractQuery$: vi.fn(() => of()), | ||
})); | ||
}); | ||
afterAll(() => { | ||
vi.clearAllMocks(); | ||
}); | ||
test('it can parse the price response', () => { | ||
expect(parsePriceFromContract(priceResponse)).toStrictEqual({ | ||
oracleKey: 'BTC', | ||
rate: BigNumber('27917.2071556'), | ||
lastUpdatedBase: 1696644063, | ||
lastUpdatedQuote: 18446744073709552000, | ||
}); | ||
}); | ||
test('it can parse the prices response', () => { | ||
const parsedOutput = { | ||
BTC: { | ||
oracleKey: 'BTC', | ||
rate: BigNumber('27917.2071556'), | ||
lastUpdatedBase: 1696644063, | ||
lastUpdatedQuote: 18446744073709552000, | ||
}, | ||
ETH: { | ||
oracleKey: 'ETH', | ||
rate: BigNumber('1644.0836829'), | ||
lastUpdatedBase: 1696644063, | ||
lastUpdatedQuote: 18446744073709552000, | ||
}, | ||
}; | ||
expect(parsePricesFromContract(pricesResponse)).toStrictEqual(parsedOutput); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* Parse contract query responses to determine if an error has occured | ||
*/ | ||
function identifyQueryResponseErrors(response) { | ||
if (typeof response === 'string' | ||
&& (response.includes('error') || response.includes('Error'))) { | ||
throw new Error(response); | ||
} | ||
if (typeof response === 'object' | ||
&& 'includes' in response | ||
&& response.includes('parse_err')) { | ||
throw new Error(response); | ||
} | ||
} | ||
export { identifyQueryResponseErrors, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { getSecretNetworkClient$, getActiveQueryClient$ } from '~/client'; | ||
export { getSecretNetworkClient$, getActiveQueryClient$, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { getSecretNetworkClient$, getActiveQueryClient$ } from '~/client'; | ||
|
||
export { | ||
getSecretNetworkClient$, | ||
getActiveQueryClient$, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import BigNumber from 'bignumber.js'; | ||
/** | ||
* Convert from uDenom to the human readable equivalent as BigNumber type | ||
*/ | ||
const convertCoinFromUDenom = (amount, decimals) => { | ||
BigNumber.config({ DECIMAL_PLACES: 18 }); | ||
return BigNumber(amount).dividedBy(BigNumber(10).pow(decimals)); | ||
}; | ||
/** | ||
* Convert BigNumber to the uDenom string type | ||
*/ | ||
const convertCoinToUDenom = (amount, decimals) => { | ||
BigNumber.config({ DECIMAL_PLACES: 18 }); | ||
if (typeof amount === 'string' || typeof amount === 'number') { | ||
return BigNumber(amount).multipliedBy(BigNumber(10).pow(decimals)).toFixed(0); | ||
} | ||
return amount.multipliedBy(BigNumber(10).pow(decimals)).toFixed(0); | ||
}; | ||
export { convertCoinToUDenom, convertCoinFromUDenom, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { test, expect, } from 'vitest'; | ||
import { BigNumber } from 'bignumber.js'; | ||
import { convertCoinFromUDenom, convertCoinToUDenom, } from './utils'; | ||
test('It converts token from U denom V2', () => { | ||
expect(convertCoinFromUDenom(1000, 2)).toStrictEqual(BigNumber(10)); | ||
expect(convertCoinFromUDenom('1000', 2)).toStrictEqual(BigNumber(10)); | ||
expect(convertCoinFromUDenom('1000000000000000000000000000', 2)).toStrictEqual(BigNumber('10000000000000000000000000')); | ||
expect(convertCoinFromUDenom(1e100, 2)).toStrictEqual(BigNumber(1e98)); | ||
expect(convertCoinFromUDenom('1e100', 2)).toStrictEqual(BigNumber(1e98)); | ||
expect(convertCoinFromUDenom('100000000000000000000005555.123456789123456789', 2)).toStrictEqual(BigNumber('1000000000000000000000055.551234567891234568')); | ||
expect(convertCoinFromUDenom('987654321987654321987654321', 18)).toStrictEqual(BigNumber('987654321.987654321987654321')); | ||
}); | ||
test('It converts token to U denom V2', () => { | ||
const testBigNumber1 = BigNumber(1000); | ||
expect(convertCoinToUDenom(testBigNumber1, 2)).toBe('100000'); | ||
const testBigNumber2 = BigNumber('0.123456789123456789'); | ||
expect(convertCoinToUDenom(testBigNumber2, 18)).toBe('123456789123456789'); | ||
const testBigNumber3 = BigNumber('123456789123456789.123456789123456789'); | ||
expect(convertCoinToUDenom(testBigNumber3, 18)).toBe('123456789123456789123456789123456789'); | ||
expect(convertCoinToUDenom('1111.123456789101213141', 18)).toBe('1111123456789101213141'); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export {}; |
Oops, something went wrong.