Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: move sdk code to limit number of new deps #3259

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## 7.0.1

### Chores

- Removed number of unnecessary dependencies ([PR 3259](https://github.com/input-output-hk/daedalus/pull/3259))

- Fixed crashing of Daedalus 7.0.0 on Ubuntu ([PR 3257](https://github.com/input-output-hk/daedalus/pull/3257))

## 7.0.0
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@
},
"dependencies": {
"@cardano-foundation/ledgerjs-hw-app-cardano": "7.1.3",
"@cardano-sdk/core": "^0.41.4",
"@iohk-jormungandr/wallet-js": "0.5.0-pre7",
"@ledgerhq/hw-transport-node-hid": "6.27.15",
"@scure/base": "^1.1.1",
"@trezor/connect": "9.3.0",
"@trezor/transport": "1.1.12",
"aes-js": "3.1.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { Button } from 'react-polymorph/lib/components/Button';
import { Link } from 'react-polymorph/lib/components/Link';

import BigNumber from 'bignumber.js';
import { Cardano } from '@cardano-sdk/core';
import { Decoded, bech32 } from 'bech32';
import * as BaseEncoding from '@scure/base';
import BorderedBox from '../../widgets/BorderedBox';
import { messages } from './VotingPowerDelegation.messages';
import styles from './VotingPowerDelegation.scss';
Expand Down Expand Up @@ -72,13 +73,167 @@ type StateConfirmation = Omit<FormData, 'fee'> & {

type State = Form | FormWithError | StateFormComplete | StateConfirmation;

// TODO discuss if we need to restrict the length
const isDrepIdValid = (drepId: string) => {
const isDRepId = (value: string): value is Cardano.DRepID =>
Cardano.DRepID.isValid(value);
return isDRepId(drepId) && Cardano.DRepID.toCip105DRepID(drepId) === drepId;
const MAX_BECH32_LENGTH_LIMIT = 1023;
const CIP_105_DREP_ID_LENGTH = 28;
const CIP_129_DREP_ID_LENGTH = 29;

const isOneOf = <T,>(target: T, options: T | T[]) =>
(Array.isArray(options) && options.includes(target)) || target === options;

export const assertIsBech32WithPrefix = (
target: string,
prefix: string | string[],
expectedDecodedLength?: number | number[]
): void => {
let decoded: Decoded;
try {
decoded = bech32.decode(target, MAX_BECH32_LENGTH_LIMIT);
} catch (error) {
throw new Error(`expected bech32-encoded string with '${prefix}' prefix`);
}
if (!isOneOf(decoded.prefix, prefix)) {
throw new Error(
`expected bech32 prefix '${prefix}', got '${decoded.prefix}''`
);
}
if (
expectedDecodedLength &&
!isOneOf(decoded.words.length, expectedDecodedLength)
) {
throw new Error(
`expected decoded length of '${expectedDecodedLength}', got '${decoded.words.length}'`
);
}
};

export const typedBech32 = <T,>(
target: string,
prefix: string | string[],
expectedDecodedLength?: number | number[]
) => {
assertIsBech32WithPrefix(target, prefix, expectedDecodedLength);
return (target as unknown) as T;
};

export const DRepID = (value: string): string => {
try {
return typedBech32(value, ['drep'], 47);
} catch {
return typedBech32(value, ['drep', 'drep_script'], 45);
}
};

const assertLength = (expectedLength: number | undefined, target: string) => {
if (expectedLength && target.length !== expectedLength) {
throw new Error(
`expected length '${expectedLength}', got ${target.length}`
);
}
};

export const assertIsHexString = (
target: string,
expectedLength?: number
): void => {
assertLength(expectedLength, target);
// eslint-disable-next-line wrap-regex
if (target.length > 0 && !/^[\da-f]+$/i.test(target)) {
throw new Error('expected hex string');
}
Comment on lines +138 to +142
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code more readable and removes need for eslint comment

Suggested change
assertLength(expectedLength, target);
// eslint-disable-next-line wrap-regex
if (target.length > 0 && !/^[\da-f]+$/i.test(target)) {
throw new Error('expected hex string');
}
assertLength(expectedLength, target);
const isHexString = /^[\da-f]+$/i;
if (target.length > 0 && !isHexString.test(target)) {
throw new Error('expected hex string');
}

};

export const typedHex = <T,>(value: string, length?: number): T => {
assertIsHexString(value, length);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (value as any) as T;
};
Comment on lines +145 to +149
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to use unknown for this case imo, also complies with eslint requirements

Suggested change
export const typedHex = <T,>(value: string, length?: number): T => {
assertIsHexString(value, length);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (value as any) as T;
};
export const typedHex = <T>(value: string, length?: number): T => {
assertIsHexString(value, length);
return value as unknown as T;
};


export const Hash28ByteBase16 = (value: string): string =>
typedHex<string>(value, 56);

DRepID.isValid = (value: string): boolean => {
try {
DRepID(value);
return true;
} catch {
return false;
}
};

export enum CredentialType {
KeyHash = 0,
ScriptHash = 1,
}

export type Credential = {
type: CredentialType;
hash: string;
};

DRepID.toCredential = (drepId: string): Credential => {
const { words } = BaseEncoding.bech32.decode(
drepId as any,
MAX_BECH32_LENGTH_LIMIT
);
const payload = BaseEncoding.bech32.fromWords(words);

if (
payload.length !== CIP_105_DREP_ID_LENGTH &&
payload.length !== CIP_129_DREP_ID_LENGTH
) {
throw new Error('Invalid DRepID payload');
}

if (payload.length === CIP_105_DREP_ID_LENGTH) {
const isScriptHash = drepId.includes('drep_script');

return {
hash: Hash28ByteBase16(Buffer.from(payload).toString('hex')),
type: isScriptHash ? CredentialType.ScriptHash : CredentialType.KeyHash,
};
}

// CIP-129
const header = payload[0];
const hash = payload.slice(1);
const isDrepGovCred = (header & 0x20) === 0x20; // 0b00100000
const isScriptHash = (header & 0x03) === 0x03; // 0b00000011

if (!isDrepGovCred) {
throw new Error('Invalid governance credential type');
}

return {
hash: Hash28ByteBase16(Buffer.from(hash).toString('hex')),
type: isScriptHash ? CredentialType.ScriptHash : CredentialType.KeyHash,
};
};

DRepID.cip105FromCredential = (credential: Credential): string => {
let prefix = 'drep';
if (credential.type === CredentialType.ScriptHash) {
prefix = 'drep_script';
}

const words = BaseEncoding.bech32.toWords(
Buffer.from(credential.hash, 'hex')
);

return BaseEncoding.bech32.encode(
prefix,
words,
MAX_BECH32_LENGTH_LIMIT
) as string;
};

DRepID.toCip105DRepID = (drepId: string): string => {
const credential = DRepID.toCredential(drepId);
return DRepID.cip105FromCredential(credential);
};

const isDrepIdValid = (drepId: string) =>
DRepID.isValid(drepId) && DRepID.toCip105DRepID(drepId) === drepId;

const mapOfTxErrorCodeToIntl: Record<
InitializeVPDelegationTxError,
typeof messages[keyof typeof messages]
Expand Down
Loading
Loading