Skip to content

Commit 38e4d3f

Browse files
authored
feat: Add transferable field to /accounts/{accountId}/balance-info (#1722)
* Add transferable balance to balance-info * Add docs * Fix error messaging and tests * Remove ! hack * fix grammar
1 parent d0fa929 commit 38e4d3f

7 files changed

Lines changed: 38 additions & 3 deletions

File tree

docs/dist/app.bundle.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/src/openapi-v1.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4309,6 +4309,14 @@ components:
43094309
Note, that some runtimes may not have support for frozen and if so the following
43104310
will be returned `frozen does not exist for this runtime`
43114311
format: unsignedInteger
4312+
transferable:
4313+
type: string
4314+
description: The amount that can be transferred from this account. This is calculated using the formula
4315+
`free - max(maybeEd, frozen - reserve)` where `maybeEd` is the existential deposit if there are frozen
4316+
funds or reserves, otherwise it is zero. This represents the actual spendable balance.
4317+
Note, that some historical runtimes may not have support for the current formula used and if so the following
4318+
will be returned `transferable not supported for this runtime`.
4319+
format: unsignedInteger
43124320
locks:
43134321
type: array
43144322
description: Array of locks on a balance. There can be many of these on

src/services/accounts/AccountsBalanceInfoService.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ describe('AccountsBalanceInfoService', () => {
179179
},
180180
],
181181
miscFrozen: '100000000000',
182+
transferable: 'transferable formula not supported for this runtime',
182183
nonce: '6',
183184
reserved: '0',
184185
tokenSymbol: 'DOT',

src/services/accounts/AccountsBalanceInfoService.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export class AccountsBalanceInfoService extends AbstractService {
9090
miscFrozen: 'miscFrozen does not exist for this runtime',
9191
feeFrozen: 'feeFrozen does not exist for this runtime',
9292
frozen: 'frozen does not exist for this runtime',
93+
transferable: 'transferable formula not supported for this runtime',
9394
locks: this.inDenominationLocks(denominate, locks, decimal),
9495
};
9596
} else {
@@ -106,11 +107,15 @@ export class AccountsBalanceInfoService extends AbstractService {
106107

107108
const { data, nonce } = accountInfo;
108109

109-
let free, reserved, feeFrozen, miscFrozen, frozen;
110+
let free, reserved, feeFrozen, miscFrozen, frozen, transferable;
110111
if (accountInfo.data?.frozen) {
112+
const deriveData = await this.api.derive.balances.all(address);
111113
free = data.free;
112114
reserved = data.reserved;
113115
frozen = data.frozen;
116+
transferable = deriveData.transferable
117+
? deriveData.transferable
118+
: 'transferable formula not supported for this runtime';
114119
miscFrozen = 'miscFrozen does not exist for this runtime';
115120
feeFrozen = 'feeFrozen does not exist for this runtime';
116121
} else {
@@ -119,6 +124,7 @@ export class AccountsBalanceInfoService extends AbstractService {
119124
reserved = tmpData.reserved;
120125
feeFrozen = tmpData.feeFrozen;
121126
miscFrozen = tmpData.miscFrozen;
127+
transferable = 'transferable formula not supported for this runtime';
122128
frozen = 'frozen does not exist for this runtime';
123129
}
124130

@@ -138,6 +144,8 @@ export class AccountsBalanceInfoService extends AbstractService {
138144
typeof miscFrozen === 'string' ? miscFrozen : this.inDenominationBal(denominate, miscFrozen, decimal),
139145
feeFrozen: typeof feeFrozen === 'string' ? feeFrozen : this.inDenominationBal(denominate, feeFrozen, decimal),
140146
frozen: typeof frozen === 'string' ? frozen : this.inDenominationBal(denominate, frozen, decimal),
147+
transferable:
148+
typeof transferable === 'string' ? transferable : this.inDenominationBal(denominate, transferable, decimal),
141149
locks: this.inDenominationLocks(denominate, locks, decimal),
142150
};
143151
} else {
@@ -188,19 +196,24 @@ export class AccountsBalanceInfoService extends AbstractService {
188196

189197
if (accountData && locks && accountInfo) {
190198
const { nonce } = accountInfo;
191-
let free, reserved, feeFrozen, miscFrozen, frozen;
199+
let free, reserved, feeFrozen, miscFrozen, frozen, transferable;
192200
if ((accountData as AccountData).miscFrozen) {
193201
const tmpData = accountData as AccountData;
194202
free = tmpData.free;
195203
reserved = tmpData.reserved;
196204
feeFrozen = tmpData.feeFrozen;
197205
miscFrozen = tmpData.miscFrozen;
198206
frozen = 'frozen does not exist for this runtime';
207+
transferable = 'transferable formula not supported for this runtime';
199208
} else {
209+
const deriveData = await this.api.derive.balances.all(address);
200210
const tmpData = accountData as PalletBalancesAccountData;
201211
free = tmpData.free;
202212
reserved = tmpData.reserved;
203213
frozen = tmpData.frozen;
214+
transferable = deriveData.transferable
215+
? deriveData.transferable
216+
: 'transferable formula not supported for this runtime';
204217
feeFrozen = 'feeFrozen does not exist for this runtime';
205218
miscFrozen = 'miscFrozen does not exist for this runtime';
206219
}
@@ -215,6 +228,8 @@ export class AccountsBalanceInfoService extends AbstractService {
215228
typeof miscFrozen === 'string' ? miscFrozen : this.inDenominationBal(denominate, miscFrozen, decimal),
216229
feeFrozen: typeof feeFrozen === 'string' ? feeFrozen : this.inDenominationBal(denominate, feeFrozen, decimal),
217230
frozen: typeof frozen === 'string' ? frozen : this.inDenominationBal(denominate, frozen, decimal),
231+
transferable:
232+
typeof transferable === 'string' ? transferable : this.inDenominationBal(denominate, transferable, decimal),
218233
locks: this.inDenominationLocks(denominate, locks, decimal),
219234
};
220235
} else {

src/services/test-helpers/responses/accounts/balanceInfo789629.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"miscFrozen": "100000000000",
1111
"feeFrozen": "100000000000",
1212
"frozen": "frozen does not exist for this runtime",
13+
"transferable": "transferable formula not supported for this runtime",
1314
"locks": [
1415
{
1516
"id": "0x7374616b696e6720",

src/services/test-helpers/responses/accounts/balanceInfoFeeFrozen.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"miscFrozen": "111111",
1111
"feeFrozen": "111111",
1212
"frozen": "frozen does not exist for this runtime",
13+
"transferable": "transferable formula not supported for this runtime",
1314
"locks": [
1415
{
1516
"id": "0x7374616b696e6720",

src/types/responses/AccountBalanceInfo.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ export interface IAccountBalanceInfo {
2828
miscFrozen: Balance | string;
2929
feeFrozen: Balance | string;
3030
frozen: Balance | string;
31+
/**
32+
* Calculated transferable balance. This uses the formula: free - max(maybeEd, frozen - reserve)
33+
* Where `maybeEd` means if there is no frozen and reserves it will be zero, else it will be the existential deposit.
34+
* This is only correct when the return type of `api.query.system.account` is `FrameSystemAccountInfo`.
35+
* Which is the most up to date calculation for transferable balances.
36+
*
37+
* ref: https://github.com/paritytech/polkadot-sdk/issues/1833
38+
*/
39+
transferable: Balance | string;
3140
locks: Vec<BalanceLock> | IBalanceLock[];
3241
rcBlockNumber?: string;
3342
ahTimestamp?: string;

0 commit comments

Comments
 (0)