Skip to content

Commit

Permalink
feat: implement authorizations.getReceived in the Security Token
Browse files Browse the repository at this point in the history
Also updated `getTickerReservations` to the latest polkadot changes
  • Loading branch information
monitz87 committed Apr 1, 2020
1 parent 203a38b commit 1e92748
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 42 deletions.
26 changes: 15 additions & 11 deletions src/Polymesh.ts
Expand Up @@ -5,11 +5,12 @@ import { BigNumber } from 'bignumber.js';
import { polymesh } from 'polymesh-types/definitions';
import { Link } from 'polymesh-types/types';

import { TickerReservation } from '~/api/entities';
import { Identity, TickerReservation } from '~/api/entities';
import { reserveTicker, ReserveTickerParams } from '~/api/procedures';
import { PolymeshError, TransactionQueue } from '~/base';
import { Context } from '~/context';
import { ErrorCode } from '~/types';
import { tickerToString } from '~/utils';

/**
* Main entry point of the Polymesh SDK
Expand Down Expand Up @@ -138,18 +139,11 @@ export class Polymesh {

const tickers = await links.entries({ identity });

/*
NOTE: we have cast to Option<Link> to get access of link_data properties despite what the types say.
*/
const tickerReservations = tickers
.filter(([, data]) => ((data as unknown) as Option<Link>).unwrap().link_data.isTickerOwned)
.filter(([, data]) => data.link_data.isTickerOwned)
.map(([, data]) => {
const ticker = ((data as unknown) as Option<Link>).unwrap().link_data.asTickerOwned;
return new TickerReservation(
// eslint-disable-next-line no-control-regex
{ ticker: u8aToString(ticker).replace(/\u0000/g, '') },
context
);
const ticker = data.link_data.asTickerOwned;
return new TickerReservation({ ticker: tickerToString(ticker) }, context);
});

return tickerReservations;
Expand Down Expand Up @@ -182,4 +176,14 @@ export class Polymesh {
message: `There is no reservation for ${ticker} ticker`,
});
}

/**
* Create an identity instance from a DID. If no DID is passed, the current identity is returned
*/
public getIdentity(args?: { did: string }): Identity {
if (args) {
return new Identity(args, this.context);
}
return this.context.getCurrentIdentity();
}
}
88 changes: 88 additions & 0 deletions src/api/entities/AuthorizationRequest.ts
@@ -0,0 +1,88 @@
import BigNumber from 'bignumber.js';

import { Entity } from '~/base';
import { Context } from '~/context';
import { Authorization } from '~/types';

export interface UniqueIdentifiers {
authId: BigNumber;
}

export interface Params {
targetDid: string;
issuerDid: string;
expiry: Date | null;
data: Authorization;
}

/**
* Represents a request made by an identity to another identity for some sort of authorization. This has multiple uses. For example, if Alice
* wants to transfer ownership of her asset ALICETOKEN to Bob, an authorization request gets emitted to Bob,
* who then has to accept it in order for the ownership transfer to be complete
*/
export class AuthorizationRequest extends Entity<UniqueIdentifiers> {
/**
* @hidden
* Check if a value is of type [[UniqueIdentifiers]]
*/
public static isUniqueIdentifiers(identifier: unknown): identifier is UniqueIdentifiers {
const { authId } = identifier as UniqueIdentifiers;

return authId instanceof BigNumber;
}

/**
* ID of the identity to which the request was emitted
*/
public targetDid: string;

/**
* ID of the identity that emitted the request
*/
public issuerDid: string;

/**
* authorization request data corresponding to type of authorization
*
* | Type | Data |
* |----------------------------|--------|
* | Attest Master Key Rotation | DID |
* | Rotate Master Key | DID |
* | Transfer Ticker | Ticker |
* | Add MultiSig Signer | N/A |
* | Transfer Token Ownership | Ticker |
* | Join Identity | DID |
* | Custom | Custom |
* | No Data | N/A |
*/
public data: Authorization;

/**
* date at which the authorization request expires and can no longer be accepted.
* At this point, a new authorization request must be emitted. Null if the request never expires
*/
public expiry: Date | null;

/**
* @hidden
* internal identifier for the request (used to accept/reject/cancel)
*/
private authId: BigNumber;

/**
* @hidden
*/
public constructor(args: UniqueIdentifiers & Params, context: Context) {
const { targetDid, issuerDid, expiry, data, ...identifiers } = args;

super(identifiers, context);

const { authId } = identifiers;

this.targetDid = targetDid;
this.issuerDid = issuerDid;
this.authId = authId;
this.expiry = expiry;
this.data = data;
}
}
47 changes: 47 additions & 0 deletions src/api/entities/Identity/Authorizations.ts
@@ -0,0 +1,47 @@
import { AuthorizationRequest } from '~/api/entities/AuthorizationRequest';
import { Namespace } from '~/base';
import { SignerType } from '~/types/internal';
import {
authorizationDataToAuthorization,
momentToDate,
signatoryToSigner,
signerToSignatory,
u64ToBigNumber,
} from '~/utils';

import { Identity } from './';

/**
* Handles all Identity Authorization related functionality
*/
export class Authorizations extends Namespace<Identity> {
/**
* Fetch all authorization requests for which this identity is the target
*/
public async getReceived(): Promise<AuthorizationRequest[]> {
const {
context: { polymeshApi },
context,
parent: { did },
} = this;

const entries = await polymeshApi.query.identity.authorizations.entries(
signerToSignatory({ type: SignerType.Identity, value: did }, context)
);

return entries.map(([, auth]) => {
const { expiry, auth_id: authId, authorization_data: data, authorized_by: issuerDid } = auth;

return new AuthorizationRequest(
{
authId: u64ToBigNumber(authId),
expiry: expiry.isSome ? momentToDate(expiry.unwrap()) : null,
data: authorizationDataToAuthorization(data),
targetDid: did,
issuerDid: signatoryToSigner(issuerDid).value,
},
context
);
});
}
}
Expand Up @@ -5,7 +5,7 @@ import { Context } from '~/context';
import { entityMockUtils, polkadotMockUtils } from '~/testUtils/mocks';
import { RoleType } from '~/types';

import { Identity } from '../Identity';
import { Identity } from '../';

jest.mock(
'~/api/entities/TickerReservation',
Expand Down
@@ -1,5 +1,6 @@
import { BigNumber } from 'bignumber.js';

import { Authorizations } from '~/api/entities/Identity/Authorizations';
import { SecurityToken } from '~/api/entities/SecurityToken';
import { TickerReservation } from '~/api/entities/TickerReservation';
import { Entity, PolymeshError } from '~/base';
Expand Down Expand Up @@ -33,6 +34,9 @@ export class Identity extends Entity<UniqueIdentifiers> {
*/
public did: string;

// Namespaces
public authorizations: Authorizations;

/**
* Create an Identity entity
*/
Expand All @@ -42,6 +46,7 @@ export class Identity extends Entity<UniqueIdentifiers> {
const { did } = identifiers;

this.did = did;
this.authorizations = new Authorizations(this, context);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/api/procedures/__tests__/transferTokenOwnership.ts
Expand Up @@ -13,8 +13,8 @@ import {
import { Context } from '~/context';
import { entityMockUtils, polkadotMockUtils, procedureMockUtils } from '~/testUtils/mocks';
import { Mocked } from '~/testUtils/types';
import { RoleType, TickerReservationStatus } from '~/types';
import { Authorization, AuthorizationType, PolymeshTx, Signer, SignerType } from '~/types/internal';
import { Authorization, AuthorizationType, RoleType, TickerReservationStatus } from '~/types';
import { PolymeshTx, Signer, SignerType } from '~/types/internal';
import * as utilsModule from '~/utils';

describe('transferTokenOwnership procedure', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/api/procedures/transferTokenOwnership.ts
@@ -1,7 +1,7 @@
import { SecurityToken } from '~/api/entities';
import { Procedure } from '~/base';
import { Role, RoleType } from '~/types';
import { AuthorizationType, SignerType } from '~/types/internal';
import { AuthorizationType, Role, RoleType } from '~/types';
import { SignerType } from '~/types/internal';
import { authorizationToAuthorizationData, dateToMoment, signerToSignatory } from '~/utils';

export interface TransferTokenOwnershipParams {
Expand Down
27 changes: 27 additions & 0 deletions src/types/index.ts
Expand Up @@ -122,6 +122,33 @@ export interface TokenDocument {
contentHash: string;
}

/**
* Type of authorization request
*/
export enum AuthorizationType {
AttestMasterKeyRotation = 'attestMasterKeyRotation',
RotateMasterKey = 'rotateMasterKey',
TransferTicker = 'transferTicker',
AddMultiSigSigner = 'addMultiSigSigner',
TransferTokenOwnership = 'transferTokenOwnership',
JoinIdentity = 'joinIdentity',
Custom = 'custom',
NoData = 'noData',
}

/**
* Authorization request data corresponding to type
*/
export type Authorization =
| { type: AuthorizationType.NoData | AuthorizationType.AddMultiSigSigner }
| {
type: Exclude<
AuthorizationType,
AuthorizationType.NoData | AuthorizationType.AddMultiSigSigner
>;
value: string;
};

/**
* Specifies possible types of errors in the SDK
*/
Expand Down
21 changes: 0 additions & 21 deletions src/types/internal.ts
Expand Up @@ -98,24 +98,3 @@ export interface Signer {
type: SignerType;
value: string;
}

export enum AuthorizationType {
AttestMasterKeyRotation = 'attestMasterKeyRotation',
RotateMasterKey = 'rotateMasterKey',
TransferTicker = 'transferTicker',
AddMultiSigSigner = 'addMultiSigSigner',
TransferTokenOwnership = 'transferTokenOwnership',
JoinIdentity = 'joinIdentity',
Custom = 'custom',
NoData = 'noData',
}

export type Authorization =
| { type: AuthorizationType.NoData | AuthorizationType.AddMultiSigSigner }
| {
type: Exclude<
AuthorizationType,
AuthorizationType.NoData | AuthorizationType.AddMultiSigSigner
>;
value: string;
};
4 changes: 2 additions & 2 deletions src/utils/__tests__/index.ts
Expand Up @@ -22,8 +22,8 @@ import sinon, { SinonStub } from 'sinon';

import { PostTransactionValue } from '~/base';
import { polkadotMockUtils } from '~/testUtils/mocks';
import { KnownTokenType, TokenIdentifierType } from '~/types';
import { Authorization, AuthorizationType, SignerType } from '~/types/internal';
import { Authorization, AuthorizationType, KnownTokenType, TokenIdentifierType } from '~/types';
import { SignerType } from '~/types/internal';

import {
accountKeyToString,
Expand Down
27 changes: 24 additions & 3 deletions src/utils/index.ts
@@ -1,4 +1,4 @@
import { bool, Bytes } from '@polkadot/types';
import { bool, Bytes, u64 } from '@polkadot/types';
import { createType } from '@polkadot/types/create/createType';
import { Balance, EventRecord, Moment } from '@polkadot/types/interfaces';
import { ISubmittableResult } from '@polkadot/types/types';
Expand All @@ -25,10 +25,16 @@ import {

import { PolymeshError, PostTransactionValue } from '~/base';
import { Context } from '~/context';
import { ErrorCode, KnownTokenType, TokenDocument, TokenIdentifierType, TokenType } from '~/types';
import {
Authorization,
AuthorizationType,
ErrorCode,
KnownTokenType,
TokenDocument,
TokenIdentifierType,
TokenType,
} from '~/types';
import {
Extrinsics,
MapMaybePostTransactionValue,
MaybePostTransactionValue,
Expand Down Expand Up @@ -148,7 +154,8 @@ export function stringToTicker(ticker: string, context: Context): Ticker {
* @hidden
*/
export function tickerToString(ticker: Ticker): string {
return u8aToString(ticker);
// eslint-disable-next-line no-control-regex
return u8aToString(ticker).replace(/\u0000/g, '');
}

/**
Expand Down Expand Up @@ -308,6 +315,20 @@ export function balanceToBigNumber(balance: Balance): BigNumber {
return new BigNumber(balance.toString()).div(Math.pow(10, 6));
}

/**
* @hidden
*/
export function numberToU64(value: number | BigNumber, context: Context): u64 {
return createType<'u64'>(context.polymeshApi.registry, 'u64', new BigNumber(value).toString());
}

/**
* @hidden
*/
export function u64ToBigNumber(balance: u64): BigNumber {
return new BigNumber(balance.toString());
}

/**
* @hidden
*/
Expand Down

0 comments on commit 1e92748

Please sign in to comment.