Skip to content

Commit

Permalink
feat(raiden): channel balance by currency
Browse files Browse the repository at this point in the history
This modifies the logic around querying raiden for channel balances.
Rather than summing up all balances across all tokens, it allows for
specifying a currency and retrieving only the balance for that
currency. Calling `ChannelBalance` without specifying a currency will
return each currency separately.

It also scales the balance returned by raiden to satoshis (10^-8). This
is because the default units of 10^-18 for currencies such as WETH can
exceed the maximum value of a `uint64` used by the gRPC layer.

Closes #1051.
  • Loading branch information
sangaman committed Jul 6, 2019
1 parent 7f60e7b commit d0ae443
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 17 deletions.
20 changes: 15 additions & 5 deletions lib/raidenclient/RaidenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ class RaidenClient extends SwapClient {
private host: string;
private disable: boolean;

// TODO: Populate the mapping from the database (Currency.decimalPlaces).
private static readonly UNITS_PER_CURRENCY: { [key: string]: number } = {
WETH: 10 ** 10,
DAI: 10 ** 10,
};

/**
* Creates a raiden client.
*/
Expand Down Expand Up @@ -272,14 +278,18 @@ class RaidenClient extends SwapClient {
}

/**
* Returns the total balance available across all channels.
* Returns the total balance available across all channels for a specified currency.
*/
public channelBalance = async (): Promise<ChannelBalance> => {
// TODO: refine logic to determine balance per token rather than all combined
const channels = await this.getChannels();
public channelBalance = async (currency?: string): Promise<ChannelBalance> => {
if (!currency) {
return { balance: 0, pendingOpenBalance: 0 };
}

const channels = await this.getChannels(this.tokenAddresses.get(currency));
const balance = channels.filter(channel => channel.state === 'opened')
.map(channel => channel.balance)
.reduce((acc, sum) => sum + acc, 0);
.reduce((acc, sum) => sum + acc, 0)
/ (RaidenClient.UNITS_PER_CURRENCY[currency] || 1);
return { balance, pendingOpenBalance: 0 };
}

Expand Down
26 changes: 15 additions & 11 deletions lib/service/Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,23 +119,27 @@ class Service {
public channelBalance = async (args: { currency: string }) => {
const { currency } = args;
const balances = new Map<string, { balance: number, pendingOpenBalance: number }>();
const getBalance = async (currency: string) => {

if (currency) {
argChecks.VALID_CURRENCY(args);

const swapClient = this.swapClientManager.get(currency.toUpperCase());
if (swapClient) {
const channelBalance = await swapClient.channelBalance();
return channelBalance;
const channelBalance = await swapClient.channelBalance(currency);
balances.set(currency, channelBalance);
} else {
throw swapsErrors.SWAP_CLIENT_NOT_FOUND(currency);
}
};

if (currency) {
argChecks.VALID_CURRENCY(args);
balances.set(currency, await getBalance(currency));
} else {
for (const currency of this.orderBook.currencies) {
balances.set(currency, await getBalance(currency));
}
const balancePromises: Promise<any>[] = [];
this.swapClientManager.swapClients.forEach((swapClient, currency) => {
if (swapClient.isConnected()) {
balancePromises.push(swapClient.channelBalance(currency).then((channelBalance) => {
balances.set(currency, channelBalance);
}));
}
});
await Promise.all(balancePromises);
}

return balances;
Expand Down
4 changes: 3 additions & 1 deletion lib/swaps/SwapClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ abstract class SwapClient extends EventEmitter {

/**
* Returns the total balance available across all channels.
* @param currency the currency whose balance to query for, otherwise all/any
* currencies supported by this client are included in the balance.
*/
public abstract channelBalance(): Promise<ChannelBalance>;
public abstract channelBalance(currency?: string): Promise<ChannelBalance>;

protected setStatus = async (status: ClientStatus): Promise<void> => {
this.logger.info(`${this.constructor.name} status: ${ClientStatus[status]}`);
Expand Down
47 changes: 47 additions & 0 deletions test/jest/RaidenClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,45 @@ const getValidTokenPaymentResponse = () => {
};
};

const channelBalance1 = 25000000;
const channelBalance2 = 5000000;
const channelBalanceTokenAddress = '0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8';
const getChannelsResponse = [
{
token_network_identifier: '0xE5637F0103794C7e05469A9964E4563089a5E6f2',
channel_identifier: 1,
partner_address: '0x61C808D82A3Ac53231750daDc13c777b59310bD9',
token_address: channelBalanceTokenAddress,
balance: channelBalance1,
total_deposit: 35000000,
state: 'opened',
settle_timeout: 100,
reveal_timeout: 30,
},
{
token_network_identifier: '0xE5637F0103794C7e05469A9964E4563089a5E6f2',
channel_identifier: 2,
partner_address: '0x2A4722462bb06511b036F00C7EbF938B2377F446',
token_address: channelBalanceTokenAddress,
balance: channelBalance2,
total_deposit: 35000000,
state: 'opened',
settle_timeout: 100,
reveal_timeout: 30,
},
{
token_network_identifier: '0xE5637F0103794C7e05469A9964E4563089a5E6f2',
channel_identifier: 3,
partner_address: '0x3b1c3C1568C848b3C12c88e2aF5E5CAa0b62071A',
token_address: channelBalanceTokenAddress,
balance: 1000000,
total_deposit: 35000000,
state: 'closed',
settle_timeout: 100,
reveal_timeout: 30,
},
];

const getValidDeal = () => {
return {
proposedQuantity: 10000,
Expand Down Expand Up @@ -91,4 +130,12 @@ describe('RaidenClient', () => {
.rejects.toMatchSnapshot();
});

test('channelBalance calculates the total balance of open channels for a currency', async () => {
raiden = new RaidenClient(config, raidenLogger);
await raiden.init();
raiden.tokenAddresses.get = jest.fn().mockReturnValue(channelBalanceTokenAddress);
raiden['getChannels'] = jest.fn()
.mockReturnValue(Promise.resolve(getChannelsResponse));
await expect(raiden.channelBalance('ABC')).resolves.toHaveProperty('balance', channelBalance1 + channelBalance2);
});
});

0 comments on commit d0ae443

Please sign in to comment.