generated from PolymeshAssociation/typescript-boilerplate
/
claimClassicTicker.ts
144 lines (125 loc) · 4.11 KB
/
claimClassicTicker.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { stringToHex, u8aToString } from '@polkadot/util';
import { recoverPersonalSignature } from 'eth-sig-util';
import { PolymeshError, Procedure, TickerReservation } from '~/internal';
import { ErrorCode, TxTags } from '~/types';
import { CLASSIC_CLAIM_SIGNATURE_PREFIX, CLASSIC_TICKER_OWNER_DID } from '~/utils/constants';
import {
identityIdToString,
momentToDate,
stringToEcdsaSignature,
stringToTicker,
} from '~/utils/conversion';
export interface ClaimClassicTickerParams {
/**
* signature generated by signing a message formed by the DID (hex without the leading 0x),
* prefixed by `classic_claim`. Example: if the DID is `0x1230000000000000000000000000000000000000000000000000000000000000`,
* the message would be `classic_claim1230000000000000000000000000000000000000000000000000000000000000`
*/
ethereumSignature: string;
/**
* ticker symbol to claim
*/
ticker: string;
}
/**
* @hidden
* Generate the message a classic user has to sign to claim their ticker. This is used to validate
* if the DID that is claiming the ticker in Polymesh is the actual owner of the ticker in Ethereum. The message format is
* `classic_claim` followed by the DID in hex format (stripped of the leading 0x). So, if the DID is
* `0x1230000000000000000000000000000000000000000000000000000000000000`, the message will be
* `classic_claim1230000000000000000000000000000000000000000000000000000000000000`
*/
function generateClassicSigneableMessage(did: string): string {
const strippedDid = did.replace('0x', '');
return stringToHex(`${CLASSIC_CLAIM_SIGNATURE_PREFIX}${strippedDid}`);
}
/**
* @hidden
*/
export async function prepareClaimClassicTicker(
this: Procedure<ClaimClassicTickerParams, TickerReservation>,
args: ClaimClassicTickerParams
): Promise<TickerReservation> {
const {
context: {
polymeshApi: {
tx,
query: { asset },
},
},
context,
} = this;
const { ticker, ethereumSignature } = args;
const rawTicker = stringToTicker(ticker, context);
const [classicReservation, { expiry, owner: reservationOwner }, { did }] = await Promise.all([
asset.classicTickers(rawTicker),
asset.tickers(rawTicker),
context.getCurrentIdentity(),
]);
if (classicReservation.isNone) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'The supplied ticker is not in the reserved classic ticker list',
});
}
const reservationExpired = expiry.isSome && momentToDate(expiry.unwrap()) < new Date();
if (reservationExpired) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: "The Ticker's claiming period has already expired",
});
}
const ownerDid = identityIdToString(reservationOwner);
if (ownerDid !== CLASSIC_TICKER_OWNER_DID) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'Ticker already claimed',
data: {
ownerDid,
},
});
}
const { eth_owner: ethOwner } = classicReservation.unwrap();
let signerAddress: string;
try {
signerAddress = recoverPersonalSignature({
data: generateClassicSigneableMessage(did),
sig: ethereumSignature,
}).toLowerCase();
} catch (err) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'Ethereum signature not valid',
data: {
error: err.message,
},
});
}
if (signerAddress !== u8aToString(ethOwner).toLowerCase()) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'The account that signed the message is not the classic Ticker owner',
data: {
signerAddress,
},
});
}
this.addTransaction(
tx.asset.claimClassicTicker,
{},
rawTicker,
stringToEcdsaSignature(ethereumSignature, context)
);
return new TickerReservation({ ticker }, context);
}
/**
* @hidden
*/
export const claimClassicTicker = (): Procedure<ClaimClassicTickerParams, TickerReservation> =>
new Procedure(prepareClaimClassicTicker, {
permissions: {
tokens: [],
transactions: [TxTags.asset.ClaimClassicTicker],
portfolios: [],
},
});