Skip to content

Commit c32b2a2

Browse files
feat(sdk-coin-sui): update balance querying to handle fundsInAddressBalance field
TICKET: CSHLD-407
1 parent d7d12b7 commit c32b2a2

File tree

6 files changed

+199
-37
lines changed

6 files changed

+199
-37
lines changed

modules/sdk-coin-sui/src/lib/iface.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,12 @@ export interface SuiObjectInfo extends SuiObjectRef {
178178
/** balance */
179179
balance: BigNumber;
180180
}
181+
182+
export interface SuiBalanceInfo {
183+
/** Total balance combining coin object balance and address balance */
184+
totalBalance: string;
185+
/** Balance held in coin objects (UTXO-style Coin<T> objects) */
186+
coinObjectBalance: string;
187+
/** Balance held in the address balance system (not in coin objects) */
188+
fundsInAddressBalance: string;
189+
}

modules/sdk-coin-sui/src/lib/mystenlab/types/coin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export const CoinBalance = object({
2525
coinType: string(),
2626
coinObjectCount: number(),
2727
totalBalance: number(),
28+
fundsInAddressBalance: optional(number()),
2829
lockedBalance: object({
2930
epochId: optional(number()),
3031
number: optional(number()),

modules/sdk-coin-sui/src/lib/utils.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
StakingProgrammableTransaction,
2020
WalrusStakingProgrammableTransaction,
2121
WalrusWithdrawStakeProgrammableTransaction,
22+
SuiBalanceInfo,
2223
SuiObjectInfo,
2324
SuiProgrammableTransaction,
2425
SuiTransaction,
@@ -493,12 +494,19 @@ export class Utils implements BaseUtils {
493494
return netCost.comparedTo(computationCost) > 0 ? netCost : computationCost;
494495
}
495496

496-
async getBalance(url: string, owner: string, coinType?: string): Promise<string> {
497+
async getBalance(url: string, owner: string, coinType?: string): Promise<SuiBalanceInfo> {
497498
if (coinType === undefined) {
498499
coinType = SUI_TYPE_ARG;
499500
}
500501
const result = await makeRPC(url, 'suix_getBalance', [owner, coinType]);
501-
return result.totalBalance;
502+
const totalBalance = (result.totalBalance ?? '0').toString();
503+
const fundsInAddressBalance = (result.fundsInAddressBalance ?? '0').toString();
504+
const coinObjectBalance = new BigNumber(totalBalance).minus(new BigNumber(fundsInAddressBalance)).toString();
505+
return {
506+
totalBalance,
507+
coinObjectBalance,
508+
fundsInAddressBalance,
509+
};
502510
}
503511

504512
async getInputCoins(url: string, owner: string, coinType?: string): Promise<SuiObjectInfo[]> {

modules/sdk-coin-sui/src/sui.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import {
4242
} from './lib';
4343
import utils from './lib/utils';
4444
import * as _ from 'lodash';
45-
import { SuiObjectInfo, SuiTransactionType } from './lib/iface';
45+
import { SuiBalanceInfo, SuiObjectInfo, SuiTransactionType } from './lib/iface';
4646
import {
4747
DEFAULT_GAS_OVERHEAD,
4848
DEFAULT_GAS_PRICE,
@@ -306,7 +306,7 @@ export class Sui extends BaseCoin {
306306
return Environments[this.bitgo.getEnv()].suiNodeUrl;
307307
}
308308

309-
protected async getBalance(owner: string, coinType?: string): Promise<string> {
309+
protected async getBalance(owner: string, coinType?: string): Promise<SuiBalanceInfo> {
310310
const url = this.getPublicNodeUrl();
311311
return await utils.getBalance(url, owner, coinType);
312312
}
@@ -354,7 +354,8 @@ export class Sui extends BaseCoin {
354354
const senderAddress = this.getAddressFromPublicKey(derivedPublicKey);
355355
let availableBalance = new BigNumber(0);
356356
try {
357-
availableBalance = new BigNumber(await this.getBalance(senderAddress));
357+
const balanceInfo = await this.getBalance(senderAddress);
358+
availableBalance = new BigNumber(balanceInfo.totalBalance);
358359
} catch (e) {
359360
continue;
360361
}
@@ -370,7 +371,8 @@ export class Sui extends BaseCoin {
370371
}
371372
const coinType = `${token.packageId}::${token.module}::${token.symbol}`;
372373
try {
373-
const availableTokenBalance = new BigNumber(await this.getBalance(senderAddress, coinType));
374+
const tokenBalanceInfo = await this.getBalance(senderAddress, coinType);
375+
const availableTokenBalance = new BigNumber(tokenBalanceInfo.totalBalance);
374376
if (availableTokenBalance.toNumber() <= 0) {
375377
continue;
376378
}

modules/sdk-coin-sui/test/unit/sui.ts

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,9 @@ describe('SUI:', function () {
520520

521521
beforeEach(() => {
522522
getBalanceStub = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
523-
getBalanceStub.withArgs(senderAddress0).resolves('1900000000');
523+
getBalanceStub
524+
.withArgs(senderAddress0)
525+
.resolves({ totalBalance: '1900000000', coinObjectBalance: '1900000000', fundsInAddressBalance: '0' });
524526

525527
getInputCoinsStub = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
526528
getInputCoinsStub.withArgs(senderAddress0).resolves([
@@ -638,7 +640,9 @@ describe('SUI:', function () {
638640

639641
it('should recover a txn for unsigned sweep recovery with multiple input coins', async function () {
640642
const senderAddress = '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389';
641-
getBalanceStub.withArgs(senderAddress).resolves('1798002120');
643+
getBalanceStub
644+
.withArgs(senderAddress)
645+
.resolves({ totalBalance: '1798002120', coinObjectBalance: '1798002120', fundsInAddressBalance: '0' });
642646
getInputCoinsStub.withArgs(senderAddress).resolves([
643647
{
644648
coinType: '0x2::sui::SUI',
@@ -747,7 +751,11 @@ describe('SUI:', function () {
747751
});
748752

749753
it('should recover a token txn for non-bitgo recovery', async function () {
750-
getBalanceStub.withArgs(senderAddress0, coinType).resolves('1000');
754+
getBalanceStub
755+
.withArgs(senderAddress0)
756+
.resolves({ totalBalance: '1900000000', coinObjectBalance: '1900000000', fundsInAddressBalance: '0' })
757+
.withArgs(senderAddress0, coinType)
758+
.resolves({ totalBalance: '1000', coinObjectBalance: '1000', fundsInAddressBalance: '0' });
751759
getInputCoinsStub.withArgs(senderAddress0, coinType).resolves([
752760
{
753761
coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
@@ -815,8 +823,12 @@ describe('SUI:', function () {
815823
});
816824

817825
it('should recover a token txn for unsigned sweep recovery', async function () {
818-
getBalanceStub.withArgs(senderAddressColdWallet).resolves('298980240');
819-
getBalanceStub.withArgs(senderAddressColdWallet, coinType).resolves('1000');
826+
getBalanceStub
827+
.withArgs(senderAddressColdWallet)
828+
.resolves({ totalBalance: '298980240', coinObjectBalance: '298980240', fundsInAddressBalance: '0' });
829+
getBalanceStub
830+
.withArgs(senderAddressColdWallet, coinType)
831+
.resolves({ totalBalance: '1000', coinObjectBalance: '1000', fundsInAddressBalance: '0' });
820832

821833
getInputCoinsStub.withArgs(senderAddressColdWallet, coinType).resolves([
822834
{
@@ -916,8 +928,12 @@ describe('SUI:', function () {
916928
});
917929

918930
it('should recover a token txn for unsigned sweep recovery with multiple input coins', async function () {
919-
getBalanceStub.withArgs(senderAddressColdWallet).resolves('298980240');
920-
getBalanceStub.withArgs(senderAddressColdWallet, coinType).resolves('11000');
931+
getBalanceStub
932+
.withArgs(senderAddressColdWallet)
933+
.resolves({ totalBalance: '298980240', coinObjectBalance: '298980240', fundsInAddressBalance: '0' });
934+
getBalanceStub
935+
.withArgs(senderAddressColdWallet, coinType)
936+
.resolves({ totalBalance: '11000', coinObjectBalance: '11000', fundsInAddressBalance: '0' });
921937
getInputCoinsStub.withArgs(senderAddressColdWallet, coinType).resolves([
922938
{
923939
coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
@@ -1031,7 +1047,11 @@ describe('SUI:', function () {
10311047

10321048
beforeEach(function () {
10331049
let callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
1034-
callBack.withArgs(senderAddress0).resolves('0').withArgs(senderAddress1).resolves('1800000000');
1050+
callBack
1051+
.withArgs(senderAddress0)
1052+
.resolves({ totalBalance: '0', coinObjectBalance: '0', fundsInAddressBalance: '0' })
1053+
.withArgs(senderAddress1)
1054+
.resolves({ totalBalance: '1800000000', coinObjectBalance: '1800000000', fundsInAddressBalance: '0' });
10351055

10361056
callBack = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
10371057
callBack.withArgs(senderAddress1).resolves([
@@ -1130,13 +1150,13 @@ describe('SUI:', function () {
11301150
getBalanceStub = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
11311151
getBalanceStub
11321152
.withArgs(senderAddress0)
1133-
.resolves('706875692')
1153+
.resolves({ totalBalance: '706875692', coinObjectBalance: '706875692', fundsInAddressBalance: '0' })
11341154
.withArgs(senderAddress0, coinType)
1135-
.resolves('0')
1155+
.resolves({ totalBalance: '0', coinObjectBalance: '0', fundsInAddressBalance: '0' })
11361156
.withArgs(senderAddress1)
1137-
.resolves('120101976')
1157+
.resolves({ totalBalance: '120101976', coinObjectBalance: '120101976', fundsInAddressBalance: '0' })
11381158
.withArgs(senderAddress1, coinType)
1139-
.resolves('1000');
1159+
.resolves({ totalBalance: '1000', coinObjectBalance: '1000', fundsInAddressBalance: '0' });
11401160

11411161
getInputCoinsStub = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
11421162
getInputCoinsStub.withArgs(senderAddress1, coinType).resolves([
@@ -1250,13 +1270,13 @@ describe('SUI:', function () {
12501270
let callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
12511271
callBack
12521272
.withArgs(receiveAddress1)
1253-
.resolves('200101976')
1273+
.resolves({ totalBalance: '200101976', coinObjectBalance: '200101976', fundsInAddressBalance: '0' })
12541274
.withArgs(receiveAddress2)
1255-
.resolves('200000000')
1275+
.resolves({ totalBalance: '200000000', coinObjectBalance: '200000000', fundsInAddressBalance: '0' })
12561276
.withArgs(seedReceiveAddress1)
1257-
.resolves('500000000')
1277+
.resolves({ totalBalance: '500000000', coinObjectBalance: '500000000', fundsInAddressBalance: '0' })
12581278
.withArgs(seedReceiveAddress2)
1259-
.resolves('200000000');
1279+
.resolves({ totalBalance: '200000000', coinObjectBalance: '200000000', fundsInAddressBalance: '0' });
12601280

12611281
callBack = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
12621282
callBack
@@ -1600,13 +1620,13 @@ describe('SUI:', function () {
16001620
it('should build signed token consolidation transactions for hot wallet', async function () {
16011621
getBalanceStub
16021622
.withArgs(hotWalletReceiveAddress1)
1603-
.resolves('116720144')
1623+
.resolves({ totalBalance: '116720144', coinObjectBalance: '116720144', fundsInAddressBalance: '0' })
16041624
.withArgs(hotWalletReceiveAddress1, coinType)
1605-
.resolves('1500')
1625+
.resolves({ totalBalance: '1500', coinObjectBalance: '1500', fundsInAddressBalance: '0' })
16061626
.withArgs(hotWalletReceiveAddress2)
1607-
.resolves('120101976')
1627+
.resolves({ totalBalance: '120101976', coinObjectBalance: '120101976', fundsInAddressBalance: '0' })
16081628
.withArgs(hotWalletReceiveAddress2, coinType)
1609-
.resolves('2000');
1629+
.resolves({ totalBalance: '2000', coinObjectBalance: '2000', fundsInAddressBalance: '0' });
16101630
getInputCoinsStub
16111631
.withArgs(hotWalletReceiveAddress1, coinType)
16121632
.resolves([
@@ -1708,13 +1728,13 @@ describe('SUI:', function () {
17081728
it('should build unsigned token consolidation transactions for cold wallet', async function () {
17091729
getBalanceStub
17101730
.withArgs(coldWalletReceiveAddress1)
1711-
.resolves('116720144')
1731+
.resolves({ totalBalance: '116720144', coinObjectBalance: '116720144', fundsInAddressBalance: '0' })
17121732
.withArgs(coldWalletReceiveAddress1, coinType)
1713-
.resolves('4000')
1733+
.resolves({ totalBalance: '4000', coinObjectBalance: '4000', fundsInAddressBalance: '0' })
17141734
.withArgs(coldWalletReceiveAddress2)
1715-
.resolves('120101976')
1735+
.resolves({ totalBalance: '120101976', coinObjectBalance: '120101976', fundsInAddressBalance: '0' })
17161736
.withArgs(coldWalletReceiveAddress2, coinType)
1717-
.resolves('6000');
1737+
.resolves({ totalBalance: '6000', coinObjectBalance: '6000', fundsInAddressBalance: '0' });
17181738
getInputCoinsStub
17191739
.withArgs(coldWalletReceiveAddress1, coinType)
17201740
.resolves([
@@ -1884,13 +1904,13 @@ describe('SUI:', function () {
18841904
it('should build unsigned token consolidation transactions for cold wallet with seed', async function () {
18851905
getBalanceStub
18861906
.withArgs(seedReceiveAddress1)
1887-
.resolves('120199788')
1907+
.resolves({ totalBalance: '120199788', coinObjectBalance: '120199788', fundsInAddressBalance: '0' })
18881908
.withArgs(seedReceiveAddress1, coinType)
1889-
.resolves('1500')
1909+
.resolves({ totalBalance: '1500', coinObjectBalance: '1500', fundsInAddressBalance: '0' })
18901910
.withArgs(seedReceiveAddress2)
1891-
.resolves('120199788')
1911+
.resolves({ totalBalance: '120199788', coinObjectBalance: '120199788', fundsInAddressBalance: '0' })
18921912
.withArgs(seedReceiveAddress2, coinType)
1893-
.resolves('2000');
1913+
.resolves({ totalBalance: '2000', coinObjectBalance: '2000', fundsInAddressBalance: '0' });
18941914

18951915
getInputCoinsStub
18961916
.withArgs(seedReceiveAddress1, coinType)
@@ -2575,7 +2595,9 @@ describe('SUI:', function () {
25752595

25762596
it('should fail to recover due to non-zero fund but insufficient funds address', async function () {
25772597
const callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
2578-
callBack.withArgs(senderAddress0).resolves('9800212');
2598+
callBack
2599+
.withArgs(senderAddress0)
2600+
.resolves({ totalBalance: '9800212', coinObjectBalance: '9800212', fundsInAddressBalance: '0' });
25792601

25802602
await basecoin
25812603
.recover({
@@ -2596,7 +2618,7 @@ describe('SUI:', function () {
25962618

25972619
it('should fail to recover due to not finding an address with funds', async function () {
25982620
const callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
2599-
callBack.resolves('0');
2621+
callBack.resolves({ totalBalance: '0', coinObjectBalance: '0', fundsInAddressBalance: '0' });
26002622

26012623
await basecoin
26022624
.recover({
@@ -2622,7 +2644,9 @@ describe('SUI:', function () {
26222644
const walletPassphrase = 'p$Sw<RjvAgf{nYAYI2xM';
26232645

26242646
const callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
2625-
callBack.withArgs(receiveAddress1).resolves('1');
2647+
callBack
2648+
.withArgs(receiveAddress1)
2649+
.resolves({ totalBalance: '1', coinObjectBalance: '1', fundsInAddressBalance: '0' });
26262650

26272651
await basecoin
26282652
.recoverConsolidations({

0 commit comments

Comments
 (0)