@v3finance/core-sdk 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/README.md +6 -2
- package/dist/cjs/core/gated.d.ts +9 -3
- package/dist/cjs/core/gated.d.ts.map +1 -1
- package/dist/cjs/core/gated.js +9 -3
- package/dist/cjs/core/gated.js.map +1 -1
- package/dist/cjs/core/http.d.ts.map +1 -1
- package/dist/cjs/core/http.js +17 -3
- package/dist/cjs/core/http.js.map +1 -1
- package/dist/cjs/generated/operations.js +1 -1
- package/dist/cjs/generated/types.d.ts +30 -14
- package/dist/cjs/generated/types.d.ts.map +1 -1
- package/dist/cjs/resources/addresses.d.ts +4 -3
- package/dist/cjs/resources/addresses.d.ts.map +1 -1
- package/dist/cjs/resources/addresses.js +4 -3
- package/dist/cjs/resources/addresses.js.map +1 -1
- package/dist/cjs/resources/approvals.d.ts +22 -0
- package/dist/cjs/resources/approvals.d.ts.map +1 -1
- package/dist/cjs/resources/approvals.js +22 -0
- package/dist/cjs/resources/approvals.js.map +1 -1
- package/dist/cjs/resources/transfers.d.ts +81 -17
- package/dist/cjs/resources/transfers.d.ts.map +1 -1
- package/dist/cjs/resources/transfers.js +81 -17
- package/dist/cjs/resources/transfers.js.map +1 -1
- package/dist/cjs/resources/types.d.ts +0 -4
- package/dist/cjs/resources/types.d.ts.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/core/gated.d.ts +9 -3
- package/dist/esm/core/gated.d.ts.map +1 -1
- package/dist/esm/core/gated.js +9 -3
- package/dist/esm/core/gated.js.map +1 -1
- package/dist/esm/core/http.d.ts.map +1 -1
- package/dist/esm/core/http.js +17 -3
- package/dist/esm/core/http.js.map +1 -1
- package/dist/esm/generated/operations.js +1 -1
- package/dist/esm/generated/types.d.ts +30 -14
- package/dist/esm/generated/types.d.ts.map +1 -1
- package/dist/esm/resources/addresses.d.ts +4 -3
- package/dist/esm/resources/addresses.d.ts.map +1 -1
- package/dist/esm/resources/addresses.js +4 -3
- package/dist/esm/resources/addresses.js.map +1 -1
- package/dist/esm/resources/approvals.d.ts +22 -0
- package/dist/esm/resources/approvals.d.ts.map +1 -1
- package/dist/esm/resources/approvals.js +22 -0
- package/dist/esm/resources/approvals.js.map +1 -1
- package/dist/esm/resources/transfers.d.ts +81 -17
- package/dist/esm/resources/transfers.d.ts.map +1 -1
- package/dist/esm/resources/transfers.js +81 -17
- package/dist/esm/resources/transfers.js.map +1 -1
- package/dist/esm/resources/types.d.ts +0 -4
- package/dist/esm/resources/types.d.ts.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/docs/concepts.md +7 -4
- package/docs/errors.md +54 -16
- package/package.json +1 -1
- package/src/core/gated.ts +9 -3
- package/src/core/http.ts +17 -3
- package/src/generated/operations.ts +1 -1
- package/src/generated/types.ts +31 -14
- package/src/resources/addresses.ts +4 -3
- package/src/resources/approvals.ts +22 -0
- package/src/resources/transfers.ts +81 -17
- package/src/resources/types.ts +0 -4
- package/src/version.ts +1 -1
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// the API does not slip in unclassified.
|
|
10
10
|
//
|
|
11
11
|
// Operations: 171 · admin: 40 · both: 1 · member: 127 · service: 3
|
|
12
|
-
// Spec sha256:
|
|
12
|
+
// Spec sha256: 30be5f9b9733d1d1d67eebf21e0bfaa457651e951676211ec9fa464cca5d3795
|
|
13
13
|
|
|
14
14
|
export type Audience = 'member' | 'admin' | 'service' | 'both' | 'unknown'
|
|
15
15
|
|
package/src/generated/types.ts
CHANGED
|
@@ -250,8 +250,8 @@ export interface AddressDetail {
|
|
|
250
250
|
tokenId: string | null
|
|
251
251
|
/** format: date-time */
|
|
252
252
|
createdAt: string
|
|
253
|
-
/** Сводка совместного доступа. Приходит в
|
|
254
|
-
sharing
|
|
253
|
+
/** Сводка совместного доступа. Приходит всегда: в списке, ответе PATCH и детали адреса */
|
|
254
|
+
sharing: SharingSummary
|
|
255
255
|
/** Код токена адреса (из segmentsJson + asset route) */
|
|
256
256
|
asset: string | null
|
|
257
257
|
assetName: string | null
|
|
@@ -322,6 +322,7 @@ export interface OutgoingTxSummary {
|
|
|
322
322
|
status: string
|
|
323
323
|
txHash: string | null
|
|
324
324
|
fee: string | null
|
|
325
|
+
/** Причина закрытия — только у failed/rejected/cancelled. У остальных статусов (awaiting_approval, pending, submitted, confirmed) — ВСЕГДА null: у незакрытого перевода причины отказа нет. Неясный исход исполнения (pending, транзакция могла уйти в сеть) здесь не показывается — его признак pending дольше 10 минут по updatedAt («ждёт сверки администратором») */
|
|
325
326
|
failureReason: string | null
|
|
326
327
|
/** format: date-time */
|
|
327
328
|
createdAt: string
|
|
@@ -491,8 +492,8 @@ export interface AddressListItem {
|
|
|
491
492
|
assetSymbol: string | null
|
|
492
493
|
/** Баланс в decimal (по token.decimals). NULL — ledger-счёта ещё нет */
|
|
493
494
|
balanceDecimal: string | null
|
|
494
|
-
/** Сводка совместного доступа. Приходит в
|
|
495
|
-
sharing
|
|
495
|
+
/** Сводка совместного доступа. Приходит всегда: в списке, ответе PATCH и детали адреса */
|
|
496
|
+
sharing: SharingSummary
|
|
496
497
|
/** Владелец адреса (создатель) с UI-метаданными */
|
|
497
498
|
owner: MemberCard | null
|
|
498
499
|
access: AddressAccess
|
|
@@ -791,11 +792,11 @@ export interface ApprovalRequestDetail {
|
|
|
791
792
|
|
|
792
793
|
export interface ApprovalRequestEvent {
|
|
793
794
|
id: string
|
|
794
|
-
/** created|voted|approved|rejected|cancelled|applied|apply_failed|stale */
|
|
795
|
+
/** created|voted|approved|rejected|cancelled|applied|apply_failed|stale|folder_skipped. folder_skipped идёт ПОСЛЕ applied: адрес по заявке create_address создан, но в папку, выбранную при подаче, не положен (папку удалили или у инициатора отозвали право) — причина в comment */
|
|
795
796
|
type: string
|
|
796
797
|
actorId: string | null
|
|
797
798
|
decision: string | null
|
|
798
|
-
/**
|
|
799
|
+
/** У voted — пояснение ПОДПИСАНТА; у stale, apply_failed и folder_skipped — системное пояснение для человека. У события applied всегда null — id созданного объекта лежит в resultId, чтобы клиент не напечатал его как комментарий */
|
|
799
800
|
comment: string | null
|
|
800
801
|
/** Только у applied: id созданного/изменённого объекта */
|
|
801
802
|
resultId: string | null
|
|
@@ -1328,7 +1329,7 @@ export interface CreateAddressBody {
|
|
|
1328
1329
|
* length: 1..16
|
|
1329
1330
|
*/
|
|
1330
1331
|
emoji?: string
|
|
1331
|
-
/** Создать адрес В группу адресов (нужно право create_address на группе). Новый адрес — member группы, доступен её
|
|
1332
|
+
/** Создать адрес В группу адресов (нужно право create_address на группе). Новый адрес — member группы, доступен её участникам. Если создание ушло на подпись (202), папка сохраняется в заявке, а право на неё перепроверяется при применении: отозвали — адрес создаётся без папки, с оговоркой applyWarnings в ответе голосования. При прямом создании (201) то же — оговорка `warnings` в теле ответа */
|
|
1332
1333
|
groupId?: string
|
|
1333
1334
|
}
|
|
1334
1335
|
|
|
@@ -1354,6 +1355,15 @@ export interface CreateAddressResult {
|
|
|
1354
1355
|
label: string | null
|
|
1355
1356
|
description: string | null
|
|
1356
1357
|
}
|
|
1358
|
+
/** Создано, но не всё: адрес есть, а в папку из `groupId` не положен. Право на папку роут проверяет до создания (иначе 403), а запись членства — уже после Wallet Service и мониторинга; право могли отозвать в этом окне или запись могла не пройти. Есть только когда непусто; не показывать адрес в папке, если оговорка пришла */
|
|
1359
|
+
warnings?: FolderSkipWarning[]
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
export interface FolderSkipWarning {
|
|
1363
|
+
/** FOLDER_NOT_FOUND — папку удалили; FOLDER_FORBIDDEN — у создателя адреса больше нет права заводить в неё адреса; FOLDER_WRITE_FAILED — сбой записи */
|
|
1364
|
+
code: "FOLDER_NOT_FOUND" | "FOLDER_FORBIDDEN" | "FOLDER_WRITE_FAILED"
|
|
1365
|
+
/** Готовая фраза для человека */
|
|
1366
|
+
message: string
|
|
1357
1367
|
}
|
|
1358
1368
|
|
|
1359
1369
|
export interface CreateCompositeBody {
|
|
@@ -1717,7 +1727,7 @@ export interface CreateTransactionResult {
|
|
|
1717
1727
|
amountDecimal: string
|
|
1718
1728
|
asset: string
|
|
1719
1729
|
network: string
|
|
1720
|
-
/**
|
|
1730
|
+
/** 201 — confirmed (исполнен сейчас); редко pending с заполненным txHash (транзакция ушла в сеть, а учёт не записался — доводит сверка, повторять перевод нельзя); 202 — awaiting_approval; 200 (replay по idempotencyKey) — ТЕКУЩИЙ статус перевода, любой: pending (первый запрос ещё исполняется — или его исход неизвестен: первый ответ был 502 TRANSFER_OUTCOME_UNKNOWN; повтор НЕ исполняет перевод заново, исход устанавливает сверка), awaiting_approval, confirmed, failed, rejected, cancelled */
|
|
1721
1731
|
status: string
|
|
1722
1732
|
txHash: string | null
|
|
1723
1733
|
fee: string | null
|
|
@@ -1732,12 +1742,13 @@ export interface CreateTransactionResult {
|
|
|
1732
1742
|
|
|
1733
1743
|
export interface CreateTransferBody {
|
|
1734
1744
|
fromAddressId: string
|
|
1745
|
+
/** Адрес получателя. Формат проверяется по типу сети адреса-отправителя ДО резерва средств: TRON — base58check (34 символа, «T…», контрольная сумма), EVM — 0x + 40 hex (смешанный регистр — контрольная сумма EIP-55). Не подошёл — 400 INVALID_DESTINATION_ADDRESS. Строка проверяется как есть, пробелы по краям — ошибка формата */
|
|
1735
1746
|
toAddress: string
|
|
1736
1747
|
/** DECIMAL строка — конвертируется по token.decimals */
|
|
1737
1748
|
amount: string
|
|
1738
1749
|
asset: string
|
|
1739
1750
|
/**
|
|
1740
|
-
* Уникальный ключ попытки перевода (рекомендуется: uuid). Повторный запрос с тем же ключом вернёт
|
|
1751
|
+
* Уникальный ключ попытки перевода (рекомендуется: uuid). Повторный запрос с тем же ключом и теми же параметрами вернёт 200 с существующим переводом — без второго broadcast, без повторной оценки политик. Тот же ключ с другими параметрами — 409 IDEMPOTENCY_KEY_REUSED. Для новой попытки после failed — новый ключ.
|
|
1741
1752
|
*
|
|
1742
1753
|
* length: 8..128
|
|
1743
1754
|
*/
|
|
@@ -2024,6 +2035,7 @@ export interface HistoryOutItem {
|
|
|
2024
2035
|
asset: string
|
|
2025
2036
|
assetSymbol: string
|
|
2026
2037
|
fee: string | null
|
|
2038
|
+
/** Причина закрытия — только у failed/rejected/cancelled. У остальных статусов (awaiting_approval, pending, submitted, confirmed) — ВСЕГДА null: у незакрытого перевода причины отказа нет. Неясный исход исполнения (pending, транзакция могла уйти в сеть) здесь не показывается — его признак pending дольше 10 минут по updatedAt («ждёт сверки администратором») */
|
|
2027
2039
|
failureReason: string | null
|
|
2028
2040
|
fromAddress: {
|
|
2029
2041
|
id: string
|
|
@@ -3164,7 +3176,7 @@ export interface RatesResponse {
|
|
|
3164
3176
|
}
|
|
3165
3177
|
|
|
3166
3178
|
export interface ReconcileTransferBody {
|
|
3167
|
-
/** Хеш от подписывающего сервиса. ЕСТЬ — транзакция ушла в сеть, перевод доводится до confirmed. НЕТ (или null) —
|
|
3179
|
+
/** Хеш от подписывающего сервиса. ЕСТЬ — транзакция ушла в сеть, перевод доводится до confirmed. НЕТ (или null) — сверяющий утверждает, что broadcast'а не было, и резерв возвращается (failed). Для pending с неясным исходом (у pending без txHash его не отличить по ответам API — failureReason у незакрытого перевода null) это утверждение нужно ПРОВЕРИТЬ по сети: возврат за ушедшую транзакцию печатает деньги. Если у pending-перевода хеш уже сохранён (broadcast прошёл), он обязателен: без него — 409 TRANSFER_BROADCAST, с другим — 409 TXHASH_MISMATCH (сохранённый — в error.details.txHash) */
|
|
3168
3180
|
txHash?: string | null
|
|
3169
3181
|
/** Комиссия, если известна */
|
|
3170
3182
|
fee?: string | null
|
|
@@ -3173,7 +3185,8 @@ export interface ReconcileTransferBody {
|
|
|
3173
3185
|
}
|
|
3174
3186
|
|
|
3175
3187
|
export interface ReconcileTransferResult {
|
|
3176
|
-
|
|
3188
|
+
/** Статус перевода после сверки: confirmed (по хешу) | failed (резерв возвращён). rejected/cancelled — у закрытого перевода с невозвращённым резервом: резерв возвращён, статус прежний */
|
|
3189
|
+
status: "confirmed" | "failed" | "rejected" | "cancelled"
|
|
3177
3190
|
txHash: string | null
|
|
3178
3191
|
}
|
|
3179
3192
|
|
|
@@ -3194,8 +3207,9 @@ export interface RequestVoteResult {
|
|
|
3194
3207
|
approvals: number
|
|
3195
3208
|
/** range: -9007199254740991..9007199254740991 */
|
|
3196
3209
|
threshold: number
|
|
3197
|
-
/** true — этот голос финализировал заявку (применена/отклонена) */
|
|
3210
|
+
/** true — этот голос финализировал заявку (применена/отклонена). У заявки на перевод status=APPROVED при finalized=false значит, что исход рассчитал НЕ этот вызов: applyError пуст — перевод исполняет (исполнил) параллельный запрос ЛИБО исполнение, начатое другим вызовом, оборвалось (сам перевод тогда не закроется); исход — только по GET /transfers/{id}: confirmed/failed, а pending дольше 10 минут по updatedAt — ждёт сверки (admin). applyError TRANSFER_EXECUTION_FAILED — к моменту одобрения перевод уже не ждал подписей. finalized=false с пустым applyError приходит и тогда, когда одобрил ЭТОТ голос, но исход перевода им не установлен (перевод pending: подписывающий сервис не дал определённого ответа, транзакция могла уйти в сеть, а могла и нет): показывайте «голос учтён», не «применено». finalized=true с пустым applyError у перевода — исполнен этим вызовом (confirmed) */
|
|
3198
3211
|
finalized: boolean
|
|
3212
|
+
/** Голос финализировал заявку, а применение не дало результата; status при этом итоговый. Коды: STALE — цель изменилась (status=REJECTED); APPLY_FAILED / APPLY_FAILED_ROLLED_BACK / APPLY_FAILED_PARTIAL — мутация не применена (целиком / цепочка откатана / частично). У action=transfer финальный approve ИСПОЛНЯЕТ перевод, status=APPROVED: TRANSFER_EXECUTION_FAILED — перевод не исполнен (обычно failed, резерв возвращён; точный статус — GET /transfers/{id}; при finalized=false — перевод уже не ждал подписей, одобрение его не исполняло). Неясный исход перевода (подписывающий сервис не дал определённого ответа, перевод pending, средства зарезервированы, транзакция могла уйти в сеть, исход установит сверка, admin) ошибкой НЕ отдаётся: applyError null, status APPROVED, finalized=false. Кода TRANSFER_OUTCOME_UNKNOWN здесь нет — он только у POST /transfers */
|
|
3199
3213
|
applyError: {
|
|
3200
3214
|
code: string
|
|
3201
3215
|
message: string
|
|
@@ -3203,6 +3217,8 @@ export interface RequestVoteResult {
|
|
|
3203
3217
|
resolvedResultId: string | null
|
|
3204
3218
|
/** Только для композитных заявок: прогресс по шагам */
|
|
3205
3219
|
stepsProgress?: StepProgress[]
|
|
3220
|
+
/** Применено, но не всё: адрес по заявке create_address создан (resolvedResultId), а в папку, выбранную при подаче, не положен. Право на папку проверяется при ПРИМЕНЕНИИ, а не при подаче. Есть только когда непусто; в истории заявки — событие folder_skipped */
|
|
3221
|
+
applyWarnings?: FolderSkipWarning[]
|
|
3206
3222
|
}
|
|
3207
3223
|
|
|
3208
3224
|
export interface StepProgress {
|
|
@@ -3365,6 +3381,7 @@ export interface TransactionDetail {
|
|
|
3365
3381
|
decisionPolicyCode: string | null
|
|
3366
3382
|
/** Человеческое название решающей политики (для UI вместо кода) */
|
|
3367
3383
|
decisionPolicyName: string | null
|
|
3384
|
+
/** Причина закрытия — только у failed/rejected/cancelled. У остальных статусов (awaiting_approval, pending, submitted, confirmed) — ВСЕГДА null: у незакрытого перевода причины отказа нет. Неясный исход исполнения (pending, транзакция могла уйти в сеть) здесь не показывается — его признак pending дольше 10 минут по updatedAt («ждёт сверки администратором») */
|
|
3368
3385
|
failureReason: string | null
|
|
3369
3386
|
/** format: date-time */
|
|
3370
3387
|
createdAt: string
|
|
@@ -3698,7 +3715,7 @@ export interface VoteRequestBody {
|
|
|
3698
3715
|
}
|
|
3699
3716
|
|
|
3700
3717
|
export interface VoteResult {
|
|
3701
|
-
/** Статус перевода ПОСЛЕ голоса: awaiting_approval (голосов мало) | rejected | confirmed/failed (финальный approve исполняет сразу) */
|
|
3718
|
+
/** Статус перевода ПОСЛЕ голоса: awaiting_approval (голосов мало) | rejected | confirmed/failed (финальный approve исполняет сразу). pending — исполнение не закончено, повторять перевод нельзя; executionError у pending всегда null, исход этим ответом НЕ установлен, ждать его «через секунды» нельзя. Так отдаётся и неясный исход (подписывающий сервис не дал определённого ответа, транзакция могла уйти в сеть, резерв стоит — доводит сверка, admin), и: txHash в GET /transfers/{id} есть — транзакция ушла в сеть, а учёт не записался, доводит сверка (admin) по этому хешу; перевод исполняет параллельный запрос (одновременный голос другого подписанта или исходный вызов при повторе голоса) ЛИБО исполнение оборвалось (клиент, который его начал, отключился — сам перевод тогда не закроется). Различает только время: перечитывайте GET; confirmed/failed — исход; pending дольше 10 минут по updatedAt — «ждёт сверки администратором» */
|
|
3702
3719
|
status: string
|
|
3703
3720
|
/**
|
|
3704
3721
|
* Собрано approve-голосов
|
|
@@ -3708,7 +3725,7 @@ export interface VoteResult {
|
|
|
3708
3725
|
approvals: number
|
|
3709
3726
|
/** range: -9007199254740991..9007199254740991 */
|
|
3710
3727
|
threshold: number
|
|
3711
|
-
/**
|
|
3728
|
+
/** Финальный approve запустил исполнение, и оно перевод НЕ исполнило. code: TRANSFER_EXECUTION_FAILED — не исполнен (status=failed, средства возвращены; исключения: failed с txHash у перевода, см. описание ручки, и status=pending, если сам возврат резерва не записался — тогда перевод доводит сверка). Тот же код — если к моменту одобрения перевод уже не ждал подписей (status — его фактический: failed/rejected/cancelled): одобрение его не исполнило. Неясный исход (подписывающий сервис не дал определённого ответа, транзакция могла уйти в сеть) ошибкой НЕ отдаётся: status=pending, executionError=null — показывайте «исполняется, исход уточняется». Кода TRANSFER_OUTCOME_UNKNOWN в голосе нет, он только у POST /transfers */
|
|
3712
3729
|
executionError: {
|
|
3713
3730
|
code: string
|
|
3714
3731
|
message: string
|
|
@@ -73,9 +73,10 @@ export class AddressesResource {
|
|
|
73
73
|
/**
|
|
74
74
|
* Metadata only: this touches neither money nor the derivation path.
|
|
75
75
|
*
|
|
76
|
-
* Answers with the
|
|
77
|
-
*
|
|
78
|
-
*
|
|
76
|
+
* Answers with the same row as the listing — `owner`, `access` and
|
|
77
|
+
* `sharing` included — so it can replace the address in a list without a
|
|
78
|
+
* refetch. Recent operations and `shares`, which `get()` carries, are not
|
|
79
|
+
* in it.
|
|
79
80
|
*/
|
|
80
81
|
update(addressId: string, input: PatchAddressBody): Promise<Address> {
|
|
81
82
|
return this.#http.request<Address>('PATCH', `${this.#base}/${seg(addressId)}`, { body: input })
|
|
@@ -79,6 +79,28 @@ export class ApprovalsResource {
|
|
|
79
79
|
*
|
|
80
80
|
* Read `finalized` with `applyError`: a vote can finalize a request AND
|
|
81
81
|
* fail to apply it, and then the money has not moved.
|
|
82
|
+
*
|
|
83
|
+
* On a transfer request the approve that completes the threshold EXECUTES
|
|
84
|
+
* the transfer inside this call, and `status` is `APPROVED` whatever
|
|
85
|
+
* happened to the transfer:
|
|
86
|
+
*
|
|
87
|
+
* finalized, no applyError — executed by this call;
|
|
88
|
+
* applyError — `TRANSFER_EXECUTION_FAILED`, not executed
|
|
89
|
+
* (usually `failed`, reserve returned);
|
|
90
|
+
* not finalized, no applyError — the outcome is NOT established by this
|
|
91
|
+
* answer: the signing service gave no
|
|
92
|
+
* definite answer (the transaction may
|
|
93
|
+
* have left; an administrator's
|
|
94
|
+
* reconciliation settles it), another call
|
|
95
|
+
* is executing the transfer, or the
|
|
96
|
+
* execution broke off. Read the outcome
|
|
97
|
+
* from the transfer (`transfers.get`), and
|
|
98
|
+
* never resubmit it.
|
|
99
|
+
*
|
|
100
|
+
* Repeating the same decision on a request that is still open finishes what
|
|
101
|
+
* an earlier call left undone (it recorded the vote and broke off) — a veto,
|
|
102
|
+
* or an approval once the threshold is met — instead of 409
|
|
103
|
+
* `ALREADY_VOTED`.
|
|
82
104
|
*/
|
|
83
105
|
vote(requestId: string, input: VoteBody): Promise<RequestVoteResult> {
|
|
84
106
|
return this.#http.request<RequestVoteResult>(
|
|
@@ -21,12 +21,28 @@ export interface VoteInput {
|
|
|
21
21
|
* The result of sending is `Gated`, and that is the main thing to understand
|
|
22
22
|
* here. The same `{transaction}` body comes back in three situations:
|
|
23
23
|
*
|
|
24
|
-
* applied (201) — executed, the money left
|
|
24
|
+
* applied (201) — executed, the money left (rarely the status is still
|
|
25
|
+
* `pending` with a `txHash`: it went out, but its
|
|
26
|
+
* accounting was not recorded, and an administrator's
|
|
27
|
+
* reconciliation finishes it — do not send it again);
|
|
25
28
|
* pending (202) — awaiting signatures, funds RESERVED but not sent;
|
|
26
|
-
* replayed (200) — a
|
|
29
|
+
* replayed (200) — a transfer with this `idempotencyKey` already existed,
|
|
30
|
+
* and this is that transfer as it stands NOW.
|
|
27
31
|
*
|
|
28
32
|
* They cannot be told apart by the body. Showing "sent" on `pending` tells
|
|
29
33
|
* the person money has gone when it is frozen.
|
|
34
|
+
*
|
|
35
|
+
* `replayed` does not mean "sent" either. It says only that the request was
|
|
36
|
+
* seen before; what became of it is in `data.transaction.status`:
|
|
37
|
+
*
|
|
38
|
+
* confirmed, submitted — it left;
|
|
39
|
+
* awaiting_approval — waiting for signatures, funds reserved;
|
|
40
|
+
* pending — still executing, or its outcome is unknown
|
|
41
|
+
* (the first answer was 502
|
|
42
|
+
* `TRANSFER_OUTCOME_UNKNOWN`) until an
|
|
43
|
+
* administrator reconciles it;
|
|
44
|
+
* failed, rejected,
|
|
45
|
+
* cancelled — it did not leave.
|
|
30
46
|
*/
|
|
31
47
|
export class TransfersResource {
|
|
32
48
|
readonly #http: HttpClient
|
|
@@ -37,7 +53,19 @@ export class TransfersResource {
|
|
|
37
53
|
this.#base = `/api/v1/vaults/${seg(vaultCode)}/transfers`
|
|
38
54
|
}
|
|
39
55
|
|
|
40
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Sends a transfer. The timeout is long: the path calls the wallet service.
|
|
58
|
+
*
|
|
59
|
+
* On `outcome: 'replayed'` read `data.transaction.status` before telling
|
|
60
|
+
* anyone anything — a replay of an attempt that failed, or that is still
|
|
61
|
+
* waiting for signatures, comes back the same way as one that went out.
|
|
62
|
+
*
|
|
63
|
+
* A thrown 502 `TRANSFER_OUTCOME_UNKNOWN` is not a failure: the signing
|
|
64
|
+
* service gave no definite answer, the transaction may have left, and the
|
|
65
|
+
* transfer stays `pending` with its funds reserved (`details.transactionId`).
|
|
66
|
+
* Retry it only with the SAME key — that returns the transfer without
|
|
67
|
+
* executing it again; a new key can send the money twice.
|
|
68
|
+
*/
|
|
41
69
|
async send(
|
|
42
70
|
input: CreateTransferBody,
|
|
43
71
|
options: { signal?: AbortSignal; timeoutMs?: number } = {},
|
|
@@ -56,7 +84,9 @@ export class TransfersResource {
|
|
|
56
84
|
/**
|
|
57
85
|
* One transfer in full. Note the shape: this is the transaction itself, not
|
|
58
86
|
* the `{ transaction }` wrapper that `send()` returns — and it carries more,
|
|
59
|
-
* including `amlLevel`, `approvers`, `usdRate` and `failureReason
|
|
87
|
+
* including `amlLevel`, `approvers`, `usdRate` and `failureReason` — the
|
|
88
|
+
* last only on `failed`, `rejected` and `cancelled`, `null` on any other
|
|
89
|
+
* status.
|
|
60
90
|
*/
|
|
61
91
|
get(transferId: string): Promise<TransactionDetail> {
|
|
62
92
|
return this.#http.request<TransactionDetail>('GET', `${this.#base}/${seg(transferId)}`)
|
|
@@ -75,11 +105,30 @@ export class TransfersResource {
|
|
|
75
105
|
* awaiting_approval — counted, more signatures still needed;
|
|
76
106
|
* rejected — a `reject` is a veto, and the reserve is released;
|
|
77
107
|
* confirmed/failed — this vote completed the threshold, so execution ran
|
|
78
|
-
* INSIDE this request (gas, then broadcast, ~10s)
|
|
108
|
+
* INSIDE this request (gas, then broadcast, ~10s);
|
|
109
|
+
* pending — with no `executionError`: the outcome is NOT
|
|
110
|
+
* established by this answer. The signing service
|
|
111
|
+
* gave no definite answer (the transaction may have
|
|
112
|
+
* left; an administrator's reconciliation settles
|
|
113
|
+
* it), another call is executing the transfer, or
|
|
114
|
+
* the execution broke off. Re-read the transfer:
|
|
115
|
+
* `confirmed`/`failed` is the outcome, `pending` for
|
|
116
|
+
* over 10 minutes by `updatedAt` is waiting for
|
|
117
|
+
* reconciliation. Never send it again.
|
|
79
118
|
*
|
|
80
119
|
* `failed` means the money is back: execution broke after approval and the
|
|
81
120
|
* reserve was released, with the reason in `executionError`. Treating the
|
|
82
|
-
* call as "approved, done" hides both the success and that failure.
|
|
121
|
+
* call as "approved, done" hides both the success and that failure. The
|
|
122
|
+
* exception is a `failed` transfer that has a `txHash`: it went out after
|
|
123
|
+
* reconciliation had already returned the reserve, and an administrator has
|
|
124
|
+
* to correct the ledger. `executionError` beside `rejected` or `cancelled`
|
|
125
|
+
* means the transfer was no longer waiting for signatures when approved,
|
|
126
|
+
* and nothing was executed.
|
|
127
|
+
*
|
|
128
|
+
* Repeating the same decision while the transfer is still waiting finishes
|
|
129
|
+
* what an earlier call left undone (it recorded the vote and broke off) — a
|
|
130
|
+
* veto, or execution once the threshold is met — instead of 409
|
|
131
|
+
* `ALREADY_VOTED`.
|
|
83
132
|
*/
|
|
84
133
|
vote(transferId: string, input: VoteInput): Promise<VoteResult> {
|
|
85
134
|
return this.#http.request<VoteResult>(
|
|
@@ -108,19 +157,34 @@ export class TransfersResource {
|
|
|
108
157
|
}
|
|
109
158
|
|
|
110
159
|
/**
|
|
111
|
-
* Settles a transfer
|
|
112
|
-
*
|
|
160
|
+
* Settles a transfer nothing else can close. Administrators only.
|
|
161
|
+
*
|
|
162
|
+
* pending — execution died, or its outcome is unknown (the
|
|
163
|
+
* send answered 502 `TRANSFER_OUTCOME_UNKNOWN`);
|
|
164
|
+
* awaiting_approval — with no live approval request to vote on;
|
|
165
|
+
* failed, rejected,
|
|
166
|
+
* cancelled — closed, but the reserve is still held: it is
|
|
167
|
+
* returned, and the status stays.
|
|
168
|
+
*
|
|
169
|
+
* A pending transfer is decided by the HASH: send `txHash` and it is driven
|
|
170
|
+
* to `confirmed`; send none and the reserve is returned (`failed`). Sending
|
|
171
|
+
* none is YOUR statement that nothing was broadcast — the server does not
|
|
172
|
+
* check it, so look on the chain first: returning the reserve of a
|
|
173
|
+
* transaction that did go out creates money. If the transfer already holds
|
|
174
|
+
* a hash from the signing service, only that hash, sent explicitly, settles
|
|
175
|
+
* it — 409 `TRANSFER_BROADCAST` without one, `TXHASH_MISMATCH` with another,
|
|
176
|
+
* the saved hash in `details.txHash`. The other two cases never executed
|
|
177
|
+
* and take no hash.
|
|
113
178
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* broadcast. A FRESH pending transfer is refused on purpose — while
|
|
118
|
-
* execution may still be in flight, intervening risks a SECOND broadcast,
|
|
119
|
-
* which costs more than waiting.
|
|
179
|
+
* A FRESH transfer is refused on purpose — while execution may still be in
|
|
180
|
+
* flight, intervening risks a SECOND broadcast, which costs more than
|
|
181
|
+
* waiting.
|
|
120
182
|
*
|
|
121
|
-
* Pass `fee` along with the hash if you know it: this is the only moment
|
|
122
|
-
*
|
|
123
|
-
*
|
|
183
|
+
* Pass `fee` along with the hash if you know it: this is the only moment it
|
|
184
|
+
* can be recorded on a transfer settled this way; otherwise it stays as it
|
|
185
|
+
* is, null unless the signing service reported one. The answer says which
|
|
186
|
+
* way it went — `confirmed`, `failed`, or the unchanged `rejected` /
|
|
187
|
+
* `cancelled` of a closed transfer.
|
|
124
188
|
*/
|
|
125
189
|
reconcile(
|
|
126
190
|
transferId: string,
|
package/src/resources/types.ts
CHANGED
|
@@ -42,10 +42,6 @@ import type {
|
|
|
42
42
|
* the two must not be merged, because "no account" and "empty account"
|
|
43
43
|
* deserve different wording. Amounts are raw smallest units; put them through
|
|
44
44
|
* the helpers in `units.ts` rather than `Number`.
|
|
45
|
-
*
|
|
46
|
-
* One field is thinner than the type admits: `sharing` is optional because
|
|
47
|
-
* `PATCH /addresses/{id}` reuses this schema WITHOUT it (the document says so
|
|
48
|
-
* itself, on the property). Listings and the detail endpoint do send it.
|
|
49
45
|
*/
|
|
50
46
|
export type Address = AddressListItem
|
|
51
47
|
|
package/src/version.ts
CHANGED