Skip to content

Commit

Permalink
feat: make sharable package
Browse files Browse the repository at this point in the history
  • Loading branch information
cmartinho committed Oct 10, 2023
1 parent 0409fa8 commit 01bd2d6
Show file tree
Hide file tree
Showing 22 changed files with 724 additions and 12 deletions.
5 changes: 3 additions & 2 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
dist
**/node_modules/**
*.d.ts
*.js
20 changes: 17 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
{
"name": "shadejs",
"version": "0.0.1",
"version": "0.0.2",
"author": "Secure Secrets",
"license": "MIT",
"type": "module",
"main": "./dist/my-lib.umd.cjs",
"module": "./dist/my-lib.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/my-lib.js",
"require": "./dist/my-lib.umd.cjs"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc && vite build",
"commitizen": "cz",
"lint": "eslint \"**/*.{ts,js}\"",
"lint:fix": "eslint \"**/*.{vue,ts,js}\" --fix",
Expand All @@ -12,7 +26,6 @@
"test:watch": "vitest watch",
"typecheck": "tsc --noEmit"
},
"type": "module",
"dependencies": {
"@arkweid/lefthook": "^0.7.7",
"@commitlint/cli": "^17.7.2",
Expand All @@ -31,7 +44,8 @@
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4"
"@typescript-eslint/parser": "^6.7.4",
"vite-plugin-dts": "^3.6.0"
},
"config": {
"commitizen": {
Expand Down
Binary file added shadejs-v0.0.2.tgz
Binary file not shown.
62 changes: 62 additions & 0 deletions src/client/index.js
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$, };
22 changes: 22 additions & 0 deletions src/client/services/clientServices.js
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$, };
24 changes: 24 additions & 0 deletions src/client/services/createFetch.js
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, };
5 changes: 5 additions & 0 deletions src/config.js
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, };
17 changes: 17 additions & 0 deletions src/contracts/definitions/oracle.js
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, };
18 changes: 18 additions & 0 deletions src/contracts/definitions/oracle.test.js
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);
});
44 changes: 44 additions & 0 deletions src/contracts/services/oracle.js
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$, };
49 changes: 49 additions & 0 deletions src/contracts/services/oracle.test.js
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);
});
15 changes: 15 additions & 0 deletions src/errors/index.js
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, };
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { getSecretNetworkClient$, getActiveQueryClient$ } from '~/client';
export { getSecretNetworkClient$, getActiveQueryClient$, };
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { getSecretNetworkClient$, getActiveQueryClient$ } from '~/client';

export {
getSecretNetworkClient$,
getActiveQueryClient$,
};
19 changes: 19 additions & 0 deletions src/lib/utils.js
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, };
21 changes: 21 additions & 0 deletions src/lib/utils.test.js
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');
});
1 change: 1 addition & 0 deletions src/types/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
1 change: 1 addition & 0 deletions src/types/contracts/oracle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
1 change: 1 addition & 0 deletions src/types/wallet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
Loading

0 comments on commit 01bd2d6

Please sign in to comment.