generated from PolymeshAssociation/typescript-boilerplate
/
Settlements.ts
155 lines (139 loc) · 6.07 KB
/
Settlements.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
145
146
147
148
149
150
151
152
153
154
155
import BigNumber from 'bignumber.js';
import { CanTransferResult, GranularCanTransferResult } from 'polymesh-types/types';
import { assertPortfolioExists } from '~/api/procedures/utils';
import { Namespace, SecurityToken } from '~/internal';
import { PortfolioLike, TransferBreakdown, TransferStatus } from '~/types';
import { DUMMY_ACCOUNT_ID } from '~/utils/constants';
import {
canTransferResultToTransferStatus,
granularCanTransferResultToTransferBreakdown,
numberToBalance,
portfolioIdToMeshPortfolioId,
portfolioIdToPortfolio,
portfolioLikeToPortfolioId,
stringToAccountId,
stringToIdentityId,
stringToTicker,
} from '~/utils/conversion';
/**
* Handles all Security Token Settlements related functionality
*/
export class Settlements extends Namespace<SecurityToken> {
/**
* Check whether it is possible to create a settlement instruction to transfer a certain amount of this asset between two Portfolios.
*
* @note this takes locked tokens into account. For example, if portfolio A has 1000 tokens and this function is called to check if 700 of them can be
* transferred to portfolio B (assuming everything else checks out) the result will be success. If an instruction is created and authorized to transfer those 700 tokens,
* they would become locked. From that point, further calls to this function would yield failed results because of the funds being locked, even though they haven't been
* transferred yet
*
* @param args.from - sender Portfolio (optional, defaults to the current Identity's Default Portfolio)
* @param args.to - receiver Portfolio
* @param args.amount - amount of tokens to transfer
*
* @deprecated in favor of [[canTransfer]]
*/
public async canSettle(args: {
from?: PortfolioLike;
to: PortfolioLike;
amount: BigNumber;
}): Promise<TransferStatus> {
const {
parent: { ticker },
context: {
polymeshApi: { rpc },
},
context,
parent,
} = this;
const { to, amount } = args;
let { from } = args;
let isDivisible;
if (!from) {
[{ isDivisible }, from] = await Promise.all([parent.details(), context.getCurrentIdentity()]);
} else {
({ isDivisible } = await parent.details());
}
/*
* The RPC requires a sender account ID (although it's not being used at the moment). We use the current account
* or a dummy account (Alice's in testnet) if the SDK was instanced without one
*/
const senderAddress = context.currentPair?.address || DUMMY_ACCOUNT_ID;
const fromPortfolioId = portfolioLikeToPortfolioId(from);
const toPortfolioId = portfolioLikeToPortfolioId(to);
const fromPortfolio = portfolioIdToPortfolio(fromPortfolioId, context);
const toPortfolio = portfolioIdToPortfolio(toPortfolioId, context);
const [, , fromCustodian, toCustodian] = await Promise.all([
assertPortfolioExists(fromPortfolioId, context),
assertPortfolioExists(toPortfolioId, context),
fromPortfolio.getCustodian(),
toPortfolio.getCustodian(),
]);
const res: CanTransferResult = await rpc.asset.canTransfer(
stringToAccountId(senderAddress, context),
stringToIdentityId(fromCustodian.did, context),
portfolioIdToMeshPortfolioId(fromPortfolioId, context),
stringToIdentityId(toCustodian.did, context),
portfolioIdToMeshPortfolioId(toPortfolioId, context),
stringToTicker(ticker, context),
numberToBalance(amount, context, isDivisible)
);
return canTransferResultToTransferStatus(res);
}
/**
* Check whether it is possible to create a settlement instruction to transfer a certain amount of this asset between two Portfolios. Returns a breakdown of
* the transaction containing general errors (such as insufficient balance or invalid receiver), any broken transfer restrictions, and any compliance
* failures
*
* @note this takes locked tokens into account. For example, if portfolio A has 1000 tokens and this function is called to check if 700 of them can be
* transferred to portfolio B (assuming everything else checks out) the result will be success. If an instruction is created and authorized to transfer those 700 tokens,
* they would become locked. From that point, further calls to this function would yield failed results because of the funds being locked, even though they haven't been
* transferred yet
*
* @param args.from - sender Portfolio (optional, defaults to the current Identity's Default Portfolio)
* @param args.to - receiver Portfolio
* @param args.amount - amount of tokens to transfer
*
*/
public async canTransfer(args: {
from?: PortfolioLike;
to: PortfolioLike;
amount: BigNumber;
}): Promise<TransferBreakdown> {
const {
parent: { ticker },
context: {
polymeshApi: { rpc },
},
context,
parent,
} = this;
const { to, amount } = args;
let { from } = args;
let isDivisible;
if (!from) {
[{ isDivisible }, from] = await Promise.all([parent.details(), context.getCurrentIdentity()]);
} else {
({ isDivisible } = await parent.details());
}
const fromPortfolioId = portfolioLikeToPortfolioId(from);
const toPortfolioId = portfolioLikeToPortfolioId(to);
const fromPortfolio = portfolioIdToPortfolio(fromPortfolioId, context);
const toPortfolio = portfolioIdToPortfolio(toPortfolioId, context);
const [, , fromCustodian, toCustodian] = await Promise.all([
assertPortfolioExists(fromPortfolioId, context),
assertPortfolioExists(toPortfolioId, context),
fromPortfolio.getCustodian(),
toPortfolio.getCustodian(),
]);
const res: GranularCanTransferResult = await rpc.asset.canTransferGranular(
stringToIdentityId(fromCustodian.did, context),
portfolioIdToMeshPortfolioId(fromPortfolioId, context),
stringToIdentityId(toCustodian.did, context),
portfolioIdToMeshPortfolioId(toPortfolioId, context),
stringToTicker(ticker, context),
numberToBalance(amount, context, isDivisible)
);
return granularCanTransferResultToTransferBreakdown(res, context);
}
}