Skip to content

Commit

Permalink
feat: added boilerplate code for xrp app
Browse files Browse the repository at this point in the history
  • Loading branch information
muzaffarbhat07 committed Sep 25, 2024
1 parent cc6bdb6 commit 8dc80be
Show file tree
Hide file tree
Showing 27 changed files with 6,936 additions and 4,699 deletions.
1 change: 1 addition & 0 deletions apps/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@cypherock/sdk-app-evm": "workspace:^",
"@cypherock/sdk-app-manager": "workspace:^",
"@cypherock/sdk-app-near": "workspace:^",
"@cypherock/sdk-app-xrp": "workspace:^",
"@cypherock/sdk-app-solana": "workspace:^",
"@cypherock/sdk-app-tron": "workspace:^",
"@cypherock/sdk-core": "workspace:^",
Expand Down
8 changes: 8 additions & 0 deletions packages/app-xrp/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
root: true,
extends: ['@cypherock/eslint-config'],
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.eslint.json'],
},
};
7 changes: 7 additions & 0 deletions packages/app-xrp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.turbo
dist

# stryker temp files
.stryker-tmp
reports
src/proto
1 change: 1 addition & 0 deletions packages/app-xrp/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/proto/generated
1 change: 1 addition & 0 deletions packages/app-xrp/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"@cypherock/prettier-config"
3 changes: 3 additions & 0 deletions packages/app-xrp/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Introduction

Documentation pending
22 changes: 22 additions & 0 deletions packages/app-xrp/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module.exports = {
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.test.ts',
'!src/**/__fixtures__/*',
'!src/proto/generated/**/*',
],
testTimeout: 500,
preset: 'ts-jest',
testEnvironment: 'node',
rootDir: '.',
testPathIgnorePatterns: ['/node_modules/', '/__fixtures__/', '/dist/'],
testMatch: [
'**/tests/**/*.[jt]s?(x)',
'**/__tests__/**/*.[jt]s?(x)',
'!**/__mocks__/**/*.[jt]s?(x)',
'!**/__helpers__/**/*.[jt]s?(x)',
'!**/.stryker-tmp/**/*.[jt]s?(x)',
],
modulePathIgnorePatterns: ['<rootDir>/.stryker-tmp'],
};
21 changes: 21 additions & 0 deletions packages/app-xrp/jest.stryker.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.test.ts',
'!src/**/__fixtures__/*',
'!src/proto/generated/**/*',
],
testTimeout: 500,
preset: 'ts-jest',
testEnvironment: 'node',
rootDir: '.',
testPathIgnorePatterns: ['/node_modules/', '/__fixtures__/', '/dist/'],
testMatch: [
'**/tests/**/*.[jt]s?(x)',
'**/__tests__/**/*.[jt]s?(x)',
'!**/__mocks__/**/*.[jt]s?(x)',
'!**/__helpers__/**/*.[jt]s?(x)',
],
modulePathIgnorePatterns: ['<rootDir>/.stryker-tmp'],
};
1 change: 1 addition & 0 deletions packages/app-xrp/mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
site_name: '@cypherock/sdk-app-xrp'
53 changes: 53 additions & 0 deletions packages/app-xrp/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "@cypherock/sdk-app-xrp",
"version": "0.0.1",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "MIT",
"scripts": {
"lint": "eslint src/ tests/ --fix",
"lint:check": "eslint --ext .ts,tsx,js,jsx,js src/",
"pretty": "prettier --write 'src/**/*.ts' 'tests/**/*.ts'",
"pretty:check": "prettier --check 'src/**/*.ts' 'tests/**/*.ts'",
"prebuild": "bash ./scripts/prebuild.sh",
"build": "rimraf dist && tsc -p tsconfig.json",
"test": "cross-env LOG_LEVEL=error jest",
"test:mutation": "stryker run",
"pre-commit": "lint-staged"
},
"devDependencies": {
"@cypherock/eslint-config": "workspace:*",
"@cypherock/prettier-config": "workspace:^0.0.7",
"@cypherock/tsconfig": "workspace:*",
"@jest/globals": "^29.4.1",
"@stryker-mutator/core": "^6.4.1",
"@stryker-mutator/jest-runner": "^6.4.1",
"@stryker-mutator/typescript-checker": "^6.4.1",
"@types/jest": "^29.4.0",
"@types/node": "18.11.18",
"cross-env": "^7.0.3",
"eslint": "^7.32.0",
"jest": "^29.4.1",
"lint-staged": "^13.2.0",
"rimraf": "^4.1.2",
"ts-jest": "^29.0.5",
"ts-proto": "^1.139.0",
"typescript": "^4.5.2"
},
"dependencies": {
"@cypherock/sdk-core": "workspace:^0.0.25",
"@cypherock/sdk-interfaces": "workspace:^0.0.15",
"@cypherock/sdk-utils": "workspace:^0.0.18",
"long": "^5.2.1",
"protobufjs": "^7.2.2"
},
"lint-staged": {
"*.{ts,tsx}": [
"eslint --ext ts,tsx --quiet --fix --",
"prettier --write"
],
"*.{js,jsx,md,mdx,mjs,yml,yaml,css,json}": [
"prettier --write"
]
}
}
16 changes: 16 additions & 0 deletions packages/app-xrp/scripts/prebuild.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

set -e

rm -rf ./src/proto/generated/*.ts || true
rm -rf ./src/proto/generated/xrp || true

mkdir -p src/proto/generated

if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
protoc --plugin=protoc-gen-ts_proto=$(pwd)/node_modules/.bin/protoc-gen-ts_proto.cmd --ts_proto_out=./src/proto/generated ../../submodules/common/proto/xrp/*.proto ../../submodules/common/proto/common.proto -I../../submodules/common/proto --ts_proto_opt=forceLong=string --ts_proto_opt=esModuleInterop=true
else
protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./src/proto/generated ../../submodules/common/proto/xrp/*.proto ../../submodules/common/proto/common.proto -I../../submodules/common/proto --ts_proto_opt=forceLong=string --ts_proto_opt=esModuleInterop=true
fi

node ../../scripts/extractTypes/index.js ./src/proto/generated ./src/proto/generated/types.ts
60 changes: 60 additions & 0 deletions packages/app-xrp/src/__mocks__/sdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ISDK } from '@cypherock/sdk-core';
import { DeviceState } from '@cypherock/sdk-interfaces';
import { jest } from '@jest/globals';

let sequenceNumber = 0;

export const getStatus = jest.fn<ISDK['getStatus']>();
export const sendAbort = jest.fn<ISDK['sendAbort']>();
export const getResult = jest.fn<ISDK['getResult']>();
export const sendQuery = jest.fn<ISDK['sendQuery']>();

export const configureAppletId = jest.fn<ISDK['configureAppletId']>();
export const checkAppCompatibility = jest.fn<ISDK['checkAppCompatibility']>();

export const waitForResult = jest.fn<ISDK['waitForResult']>();
export const getSequenceNumber = jest.fn<ISDK['getSequenceNumber']>(
async () => sequenceNumber,
);
export const getNewSequenceNumber = jest.fn<ISDK['getNewSequenceNumber']>(
async () => {
sequenceNumber += 1;
return sequenceNumber;
},
);

export const runOperation = jest.fn<ISDK['runOperation']>(func => func());

export const destroy = jest.fn<ISDK['destroy']>();
export const getDeviceState = jest.fn<ISDK['getDeviceState']>(
async () => DeviceState.MAIN,
);

export const create = jest.fn(async () =>
Promise.resolve({
configureAppletId,
checkAppCompatibility,
sendAbort,
getResult,
getStatus,
sendQuery,
waitForResult,
getSequenceNumber,
getNewSequenceNumber,
runOperation,
destroy,
getDeviceState,
}),
);

jest.mock('@cypherock/sdk-core', () => {
const originalModule: any = jest.requireActual('@cypherock/sdk-core');

return {
__esModule: true,
...originalModule,
SDK: {
create,
},
};
});
25 changes: 25 additions & 0 deletions packages/app-xrp/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { IDeviceConnection } from '@cypherock/sdk-interfaces';
import { SDK } from '@cypherock/sdk-core';

export class XrpApp {
private readonly sdk: SDK;

private static readonly APPLET_ID = 21;

private constructor(sdk: SDK) {
this.sdk = sdk;
}

public static async create(connection: IDeviceConnection) {
const sdk = await SDK.create(connection, XrpApp.APPLET_ID);
return new XrpApp(sdk);
}

public async destroy() {
return this.sdk.destroy();
}

public async abort() {
await this.sdk.sendAbort();
}
}
4 changes: 4 additions & 0 deletions packages/app-xrp/src/constants/appId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const APP_VERSION = {
from: '1.0.0',
to: '2.0.0',
};
3 changes: 3 additions & 0 deletions packages/app-xrp/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './app';
export * from './types';
export { updateLogger } from './utils';
1 change: 1 addition & 0 deletions packages/app-xrp/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './proto/generated/types';
35 changes: 35 additions & 0 deletions packages/app-xrp/src/utils/asserts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { DeviceAppError, DeviceAppErrorType } from '@cypherock/sdk-interfaces';
import { assert } from '@cypherock/sdk-utils';
import { ICommonError } from '../proto/generated/types';

export function assertOrThrowInvalidResult<T>(
condition: T,
): asserts condition is Exclude<T, null | undefined> {
assert(
condition,
new DeviceAppError(DeviceAppErrorType.INVALID_MSG_FROM_DEVICE),
);
}

export function parseCommonError(error?: ICommonError) {
if (error === undefined) return;

type CommonErrorKey = keyof ICommonError;
const keys = Object.keys(error) as CommonErrorKey[];

const errorTypesMap: Record<CommonErrorKey, DeviceAppErrorType> = {
unknownError: DeviceAppErrorType.UNKNOWN_ERROR,
deviceSetupRequired: DeviceAppErrorType.DEVICE_SETUP_REQUIRED,
walletNotFound: DeviceAppErrorType.WALLET_NOT_FOUND,
walletPartialState: DeviceAppErrorType.WALLET_PARTIAL_STATE,
cardError: DeviceAppErrorType.CARD_OPERATION_FAILED,
userRejection: DeviceAppErrorType.USER_REJECTION,
corruptData: DeviceAppErrorType.CORRUPT_DATA,
};

for (const key of keys) {
if (error[key] !== undefined) {
throw new DeviceAppError(errorTypesMap[key], error[key]);
}
}
}
3 changes: 3 additions & 0 deletions packages/app-xrp/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './operationHelper';
export * from './asserts';
export * from './logger';
22 changes: 22 additions & 0 deletions packages/app-xrp/src/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { updateLogger as updateLoggerCore } from '@cypherock/sdk-core';
import { ILogger, LogCreator } from '@cypherock/sdk-interfaces';
import {
createDefaultConsoleLogger,
updateLoggerObject,
} from '@cypherock/sdk-utils';

export const loggerServiceName = 'sdk-app-xrp';

export const logger: ILogger = {
...createDefaultConsoleLogger(loggerServiceName),
};

export const updateLogger = (createLogger: LogCreator) => {
updateLoggerCore(createLogger);
updateLoggerObject({
currentLogger: logger,
newLogger: createLogger(loggerServiceName),
});
};

export default logger;
67 changes: 67 additions & 0 deletions packages/app-xrp/src/utils/operationHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ISDK } from '@cypherock/sdk-core';
import { DeviceAppError, DeviceAppErrorType } from '@cypherock/sdk-interfaces';
import { OnStatus } from '@cypherock/sdk-utils';
import { DeepPartial, Exact, Query, Result } from '../proto/generated/xrp/core';
import { assertOrThrowInvalidResult, parseCommonError } from './asserts';

export function decodeResult(data: Uint8Array) {
let result: Result;

try {
result = Result.decode(data);
} catch (error) {
throw new DeviceAppError(DeviceAppErrorType.INVALID_MSG_FROM_DEVICE);
}

return result;
}

export function encodeQuery<I extends Exact<DeepPartial<Query>, I>>(query: I) {
return Uint8Array.from(Query.encode(Query.create(query)).finish());
}

type QueryKey = keyof Query;

type ResultKey = keyof Result;

export class OperationHelper<Q extends QueryKey, R extends ResultKey> {
public readonly sdk: ISDK;

private readonly queryKey: QueryKey;

private readonly resultKey: ResultKey;

private readonly onStatus?: OnStatus;

constructor(params: {
sdk: ISDK;
queryKey: Q;
resultKey: R;
onStatus?: OnStatus;
}) {
this.sdk = params.sdk;

this.queryKey = params.queryKey;

this.resultKey = params.resultKey;

this.onStatus = params.onStatus;
}

public async sendQuery<I extends Query[Q]>(query: I) {
return this.sdk.sendQuery(encodeQuery({ [this.queryKey]: query } as any));
}

public async waitForResult() {
const result = decodeResult(
await this.sdk.waitForResult({ onStatus: this.onStatus }),
);

const resultData = result[this.resultKey] as Result[R];
parseCommonError(result.commonError);
assertOrThrowInvalidResult(resultData);
parseCommonError((result[this.resultKey] as any).commonError);

return resultData;
}
}
Loading

0 comments on commit 8dc80be

Please sign in to comment.