Skip to content

Commit

Permalink
feat(cip-30): create cip-30 package
Browse files Browse the repository at this point in the history
  • Loading branch information
sweeetland committed Sep 2, 2021
1 parent 94c0188 commit 266e719
Show file tree
Hide file tree
Showing 31 changed files with 1,122 additions and 48 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ module.exports = {
"@typescript-eslint/ban-types": 0,
"template-tag-spacing": 0,
"no-magic-numbers": 0,
"camelcase": 0
"camelcase": 0,
"no-shadow": "off", // eslint compains about TS enums hence disable here and enable @typescript-eslint/no-shadow
"@typescript-eslint/no-shadow": ["error"],
"import/no-unresolved": 0
}
}
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ _In another terminal_
```console
yarn test
```
or

```console
yarn test:debug
```

### Lint
```console
yarn lint
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@
"docs": "typedoc --packages .",
"cleanup": "yarn workspaces run cleanup && shx rm -rf node_modules",
"lint": "yarn workspaces run lint",
"tsc": "yarn workspaces run tsc",
"prepare": "husky install",
"pre-commit": "lint-staged",
"mainnet:up": "DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker-compose -p sdk-mainnet up",
"mainnet:down": "docker-compose -p sdk-mainnet down",
"test": "yarn workspaces run test",
"test:debug": "DEBUG=true yarn workspaces run test",
"testnet:up": "DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 OGMIOS_PORT=1338 NETWORK=testnet docker-compose -p sdk-testnet up",
"testnet:down": "docker-compose -p sdk-testnet down"
},
"lint-staged": {
"*(apps/**/*.{js,ts}|packages/!(*golden-test-generator)/**/*.{js,ts})": [
"yarn lint"
"yarn lint",
"yarn tsc"
]
},
"repository": {
Expand Down Expand Up @@ -55,7 +58,7 @@
"eslint": "^7.32.0",
"eslint-import-resolver-typescript": "^2.4.0",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-jest": "^24.4.0",
"eslint-plugin-jsdoc": "^36.0.7",
"eslint-plugin-prettier": "^3.4.0",
Expand Down
4 changes: 3 additions & 1 deletion packages/blockfrost/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"license": "MPL-2.0",
"scripts": {
"build": "tsc --build ./src",
"tsc": "shx echo typescript --noEmit command not implemented yet",
"cleanup": "shx rm -rf dist node_modules",
"lint": "eslint --ignore-path ../../.eslintignore \"**/*.ts\"",
"test": "jest -c ./test/jest.config.js"
Expand All @@ -21,6 +22,7 @@
},
"dependencies": {
"@blockfrost/blockfrost-js": "^0.9.4",
"@cardano-ogmios/client": "^4.0.0-beta.6"
"@cardano-ogmios/client": "^4.0.0-beta.6",
"@cardano-sdk/core": "0.1.0"
}
}
5 changes: 4 additions & 1 deletion packages/blockfrost/test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
"@src/*": ["../src/*"]
}
},
"references": [{ "path": "../src" }]
"references": [
{ "path": "../../core/src" },
{ "path": "../src" }
]
}
1 change: 1 addition & 0 deletions packages/cardano-graphql-db-sync/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"license": "MPL-2.0",
"scripts": {
"build": "tsc --build ./src",
"tsc": "shx echo typescript --noEmit command not implemented yet",
"cleanup": "shx rm -rf dist node_modules",
"lint": "eslint --ignore-path ../../.eslintignore \"**/*.ts\"",
"test": "jest -c ./test/jest.config.js"
Expand Down
3 changes: 3 additions & 0 deletions packages/cip30/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
secrets
1 change: 1 addition & 0 deletions packages/cip30/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Cardano JS SDK | CIP30
29 changes: 29 additions & 0 deletions packages/cip30/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@cardano-sdk/cip30",
"version": "0.1.0",
"description": "TypeScript definitions for the dApp Connector standard CIP30",
"engines": {
"node": "^14"
},
"main": "dist/index.js",
"repository": "https://github.com/input-output-hk/cardano-js-sdk/packages/cip30",
"author": "James Sweetland",
"license": "MPL-2.0",
"scripts": {
"build": "tsc --build ./src",
"tsc": "shx echo typescript --noEmit command not implemented yet",
"cleanup": "shx rm -rf dist node_modules",
"lint": "eslint --ignore-path ../../.eslintignore \"**/*.ts\"",
"test": "jest -c ./test/jest.config.js",
"test:debug": "DEBUG=true yarn test"
},
"devDependencies": {
"mock-browser": "^0.92.14",
"shx": "^0.3.3"
},
"dependencies": {
"@cardano-ogmios/client": "^4.0.0-beta.6",
"ts-custom-error": "^3.2.0",
"ts-log": "^2.2.3"
}
}
7 changes: 7 additions & 0 deletions packages/cip30/src/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
"extends": ["../../../.eslintrc.js"],
"parserOptions": {
"project": "./tsconfig.json",
"tsconfigRootDir": __dirname
}
}
176 changes: 176 additions & 0 deletions packages/cip30/src/Wallet/Wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */

import { WalletApi } from './WalletApi';
import { ApiError, APIErrorCode } from '../errors';
import { dummyLogger, Logger } from 'ts-log';
import { WalletPublic } from './WalletPublic';

/**
* CIP30 Specification version
*/
export type SpecificationVersion = string;

/**
* Unique identifier, used to inject into the cardano namespace
*/
export type WalletName = string;

/**
* This is the entrypoint to start communication with the user's wallet.
*
* The wallet should request the user's permission to connect the web page to the user's wallet,
* and if permission has been granted, the full API will be returned to the dApp to use.
*
* The wallet can choose to maintain a whitelist to not necessarily ask the user's permission
* every time access is requested, but this behavior is up to the wallet and should be transparent
* to web pages using this API.
*
* If a wallet is already connected this function should not request access a second time,
* and instead just return the API object.
*
* Errors: `ApiError`
*/
export type Enable = () => Promise<WalletApi>;

/**
* Returns true if the dApp is already connected to the user's wallet, or if requesting access
* would return true without user confirmation (e.g. the dApp is whitelisted), and false otherwise.
*
* If this function returns true, then any subsequent calls to wallet.enable()
* during the current session should succeed and return the API object.
*
* Errors: `ApiError`
*/
export type IsEnabled = () => Promise<Boolean>;

export type WalletProperties = { name: WalletName; version: SpecificationVersion };

/**
* Resolve true to authorise access to the WalletAPI, or resolve false to deny.
*
* Errors: `ApiError`
*/
export type RequestAccess = () => Promise<boolean>;

export type WalletOptions = {
logger?: Logger;
persistAllowList?: boolean;
storage?: Storage;
};

export class Wallet {
readonly version: SpecificationVersion;
readonly name: WalletName;

private allowList: string[];
private logger: Logger;

constructor(
properties: WalletProperties,
private api: WalletApi,
private window: Window & { cardano?: Record<string, WalletPublic> },
private requestAccess: RequestAccess,
private options?: WalletOptions
) {
this.logger = options.logger ?? dummyLogger;
this.name = properties.name;
this.version = properties.version;

if (typeof options.persistAllowList === 'undefined') {
options.persistAllowList = false;
}

this.allowList = this.options.persistAllowList ? this.getAllowList() : [];

if (!this.window.cardano) {
this.logger.debug(
{
module: 'Wallet',
walletName: this.name
},
'Creating cardano global scope'
);
this.window.cardano = {};
} else {
this.logger.debug(
{
module: 'Wallet',
walletName: this.name
},
'Cardano global scope exists'
);
}

const walletPublic: WalletPublic = {
name: this.name,
version: this.version,
enable: this.enable,
isEnabled: this.isEnabled
};
this.window.cardano[properties.name] = this.window.cardano[properties.name] || walletPublic;

this.logger.debug(
{
module: 'Wallet',
walletName: this.name,
globalCardanoScope: this.window.cardano,
allowList: this.allowList
},
'Constructed'
);
}

private getAllowList(): string[] {
return JSON.parse(this.options.storage?.getItem(window.location.hostname)) || [];
}

private allowApplication(appName: string) {
this.allowList.push(appName);

if (this.options.persistAllowList) {
const currentList = this.getAllowList();
// Todo: Encrypt
this.options.storage?.setItem(window.location.hostname, JSON.stringify([...currentList, appName]));
this.logger.debug(
{
module: 'Wallet',
walletName: this.name,
allowList: this.getAllowList()
},
'Allow list persisted'
);
}
}

async isEnabled() {
const appName = this.window.location.hostname;

return this.allowList.includes(appName);
}

async enable() {
const appName = this.window.location.hostname;

if (this.options.persistAllowList && this.allowList.includes(appName)) {
this.logger.debug(
{
module: 'Wallet',
walletName: this.name
},
`${appName} has previously been allowed`
);
return this.api;
}

// gain authorization from wallet owner
const isAuthed = await this.requestAccess();

if (!isAuthed) {
throw new ApiError(APIErrorCode.Refused, 'wallet not authorized.');
}

this.allowApplication(appName);

return this.api;
}
}
Loading

0 comments on commit 266e719

Please sign in to comment.