Skip to content

Commit

Permalink
feat: add getSpendable rpc endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
hui-an-yang committed Sep 9, 2024
1 parent 38f286a commit 1cd66b4
Show file tree
Hide file tree
Showing 18 changed files with 166 additions and 12 deletions.
7 changes: 6 additions & 1 deletion integration-tests/__tests__/rpc/nodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ CONFIGS().forEach(
}) => {
const Tezos = lib;
const unrestrictedRPCNode = rpc.endsWith("ecadinfra.com") ? test.skip : test;
const parisnet = protocol === Protocols.PsParisCZ ? test : test.skip;
const betanet = protocol === Protocols.PtBetaaEZ ? test : test.skip;
let ticketContract: DefaultContractType;

beforeAll(async () => {
Expand Down Expand Up @@ -66,6 +66,11 @@ CONFIGS().forEach(
expect(balance).toBeDefined();
});

betanet(`Verify that rpcClient.getSpendable for knownBaker returns the spendable balance excluding frozen bonds`, async () => {
const balance = await rpcClient.getSpendable(knownBaker);
expect(balance).toBeDefined();
});

it(`Verify that rpcClient.getFullBalance for knownBaker returns the full balance`, async () => {
const balance = await rpcClient.getFullBalance(knownBaker);
expect(balance).toBeDefined();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export class ReadWrapperContractsLibrary implements TzReadProvider {
getBalance(address: string, block: BlockIdentifier): Promise<BigNumber> {
return this.readProvider.getBalance(address, block);
}
getSpendable(address: string, block: BlockIdentifier): Promise<BigNumber> {
return this.readProvider.getSpendable(address, block);
}
getDelegate(address: string, block: BlockIdentifier): Promise<string | null> {
return this.readProvider.getDelegate(address, block);
}
Expand Down
6 changes: 6 additions & 0 deletions packages/taquito-contracts-library/src/rpc-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ export class RpcWrapperContractsLibrary implements RpcClientInterface {
): Promise<BalanceResponse> {
return this.rpc.getBalance(address, { block });
}
async getSpendable(
address: string,
{ block }: RPCOptions = defaultRPCOptions
): Promise<BalanceResponse> {
return this.rpc.getSpendable(address, { block });
}
async getFullBalance(
address: string,
{ block }: RPCOptions = defaultRPCOptions
Expand Down
2 changes: 2 additions & 0 deletions packages/taquito-rpc/src/rpc-client-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export interface RpcClientInterface {
getBlockHash(options?: RPCOptions): Promise<string>;
getLiveBlocks(options?: RPCOptions): Promise<string[]>;
getBalance(address: string, options?: RPCOptions): Promise<BalanceResponse>;
getSpendable(address: string, options?: RPCOptions): Promise<BalanceResponse>;
getFullBalance(address: string, options?: RPCOptions): Promise<BalanceResponse>;
getStakedBalance(address: string, options?: RPCOptions): Promise<BalanceResponse>;
getUnstakedFinalizableBalance(address: string, options?: RPCOptions): Promise<BalanceResponse>;
Expand Down Expand Up @@ -148,6 +149,7 @@ export enum RPCMethodName {
GET_BLOCK_HEADER = 'getBlockHeader',
GET_BLOCK_METADATA = 'getBlockMetadata',
GET_BALANCE = 'getBalance',
GET_SPENDABLE = 'getSpendable',
GET_FULL_BALANCE = 'getFullBalance',
GET_STAKED_BALANCE = 'getStakedBalance',
GET_UNSTAKED_FINALIZABLE_BALANCE = 'getUnstakedFinalizableBalance',
Expand Down
27 changes: 25 additions & 2 deletions packages/taquito-rpc/src/rpc-client-modules/rpc-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ export class RpcClientCache implements RpcClientInterface {
}

/**
* @param address address from which we want to retrieve the balance
* @param address address from which we want to retrieve the spendable balance
* @param options contains generic configuration for rpc calls to specified block (default to head)
* @description Access the spendable balance of a contract, excluding frozen bonds
* @description The spendable balance of a contract (in mutez), also known as liquid balance. Corresponds to tez owned by the contract that are neither staked, nor in unstaked requests, nor in frozen bonds. Identical to the 'spendable' RPC.
* @see https://tezos.gitlab.io/active/rpc.html#get-block-id-context-contracts-contract-id-balance
*/
async getBalance(
Expand All @@ -223,6 +223,29 @@ export class RpcClientCache implements RpcClientInterface {
}
}

/**
* @param address address from which we want to retrieve the balance
* @param options contains generic configuration for rpc calls to specified block (default to head)
* @description The spendable balance of a contract (in mutez), also known as liquid balance. Corresponds to tez owned by the contract that are neither staked, nor in unstaked requests, nor in frozen bonds. Identical to the 'balance' RPC.
*/
async getSpendable(
address: string,
{ block }: RPCOptions = defaultRPCOptions
): Promise<BalanceResponse> {
this.validateAddress(address);
const key = this.formatCacheKey(this.rpcClient.getRpcUrl(), RPCMethodName.GET_SPENDABLE, [
block,
address,
]);
if (this.has(key)) {
return this.get(key);
} else {
const response = this.rpcClient.getSpendable(address, { block });
this.put(key, response);
return response;
}
}

/**
* @param address address from which we want to retrieve the full balance
* @param options contains generic configuration for rpc calls to specified block (default to head)
Expand Down
23 changes: 21 additions & 2 deletions packages/taquito-rpc/src/taquito-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,9 @@ export class RpcClient implements RpcClientInterface {
}

/**
* @param address address from which we want to retrieve the balance
* @param address address from which we want to retrieve the spendable balance
* @param options contains generic configuration for rpc calls to specified block (default to head)
* @description Access the spendable balance of a contract, excluding frozen bonds
* @description The spendable balance of a contract (in mutez), also known as liquid balance. Corresponds to tez owned by the contract that are neither staked, nor in unstaked requests, nor in frozen bonds. Identical to the 'spendable' RPC.
* @see https://tezos.gitlab.io/active/rpc.html#get-block-id-context-contracts-contract-id-balance
*/
async getBalance(
Expand All @@ -185,6 +185,25 @@ export class RpcClient implements RpcClientInterface {
return new BigNumber(balance);
}

/**
* @param address address from which we want to retrieve the spendable balance
* @param options contains generic configuration for rpc calls to specified block (default to head)
* @description The spendable balance of a contract (in mutez), also known as liquid balance. Corresponds to tez owned by the contract that are neither staked, nor in unstaked requests, nor in frozen bonds. Identical to the 'balance' RPC.
*/
async getSpendable(
address: string,
{ block }: RPCOptions = defaultRPCOptions
): Promise<BalanceResponse> {
this.validateAddress(address);
const balance = await this.httpBackend.createRequest<BalanceResponse>({
url: this.createURL(
`/chains/${this.chain}/blocks/${block}/context/contracts/${address}/spendable`
),
method: 'GET',
});
return new BigNumber(balance);
}

/**
* @param address address from which we want to retrieve the full balance
* @param options contains generic configuration for rpc calls to specified block (default to head)
Expand Down
13 changes: 12 additions & 1 deletion packages/taquito-rpc/test/rpc-cache.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ describe('RpcClientCache test', () => {
getBlockHash: jest.fn(),
getLiveBlocks: jest.fn(),
getBalance: jest.fn(),
getSpendable: jest.fn(),
getFullBalance: jest.fn(),
getStakedBalance: jest.fn(),
getUnstakedFinalizableBalance: jest.fn(),
Expand Down Expand Up @@ -104,6 +105,7 @@ describe('RpcClientCache test', () => {
mockRpcClient.getBlockHash.mockReturnValue(blockHash);
mockRpcClient.getLiveBlocks.mockReturnValue(liveBlocks);
mockRpcClient.getBalance.mockReturnValue(balance);
mockRpcClient.getSpendable.mockReturnValue(balance);
mockRpcClient.getFullBalance.mockReturnValue(balance);
mockRpcClient.getStakedBalance.mockReturnValue(balance);
mockRpcClient.getUnstakedFinalizableBalance.mockReturnValue(balance);
Expand Down Expand Up @@ -155,6 +157,7 @@ describe('RpcClientCache test', () => {
await rpcCache.getBlock();
await rpcCache.getLiveBlocks();
await rpcCache.getBalance(address);
await rpcCache.getSpendable(address);
await rpcCache.getFullBalance(address);
await rpcCache.getStakedBalance(address);
await rpcCache.getUnstakedFinalizableBalance(address);
Expand Down Expand Up @@ -207,7 +210,10 @@ describe('RpcClientCache test', () => {
expect(rpcCache.getAllCachedData()[`rpcTest/getBalance/head/${address}/`].response).toEqual(
balance
);
expect(rpcCache.getAllCachedData()[`rpcTest/getFullBalance/head/${address}/`].response).toEqual(
expect(rpcCache.getAllCachedData()[`rpcTest/getBalance/head/${address}/`].response).toEqual(
balance
);
expect(rpcCache.getAllCachedData()[`rpcTest/getSpendable/head/${address}/`].response).toEqual(
balance
);
expect(
Expand Down Expand Up @@ -323,6 +329,7 @@ describe('RpcClientCache test', () => {
await rpcCache.getBlock(block);
await rpcCache.getLiveBlocks(block);
await rpcCache.getBalance(address, block);
await rpcCache.getSpendable(address, block);
await rpcCache.getFullBalance(address, block);
await rpcCache.getStakedBalance(address, block);
await rpcCache.getUnstakedFinalizableBalance(address, block);
Expand Down Expand Up @@ -388,6 +395,9 @@ describe('RpcClientCache test', () => {
expect(
rpcCache.getAllCachedData()[`rpcTest/getBalance/${block.block}/${address}/`].response
).toEqual(balance);
expect(
rpcCache.getAllCachedData()[`rpcTest/getSpendable/${block.block}/${address}/`].response
).toEqual(balance);
expect(
rpcCache.getAllCachedData()[`rpcTest/getFullBalance/${block.block}/${address}/`].response
).toEqual(balance);
Expand Down Expand Up @@ -517,6 +527,7 @@ describe('RpcClientCache test', () => {
await rpcCache.getBlock();
await rpcCache.getLiveBlocks();
await rpcCache.getBalance(address);
await rpcCache.getSpendable(address);
await rpcCache.getFullBalance(address);
await rpcCache.getStakedBalance(address);
await rpcCache.getUnstakedFinalizableBalance(address);
Expand Down
14 changes: 14 additions & 0 deletions packages/taquito-rpc/test/taquito-rpc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,20 @@ describe('RpcClient test', () => {
});
});

describe('getSpendable', () => {
it('should query the right url and return a string', async () => {
httpBackend.createRequest.mockReturnValue(Promise.resolve('10000'));
const balance = await client.getSpendable(contractAddress);

expect(httpBackend.createRequest.mock.calls[0][0]).toEqual({
method: 'GET',
url: `root/chains/test/blocks/head/context/contracts/${contractAddress}/spendable`,
});
expect(balance).toBeInstanceOf(BigNumber);
expect(balance.toString()).toEqual('10000');
});
});

describe('getFullBalance', () => {
it('should query the right url and return a string', async () => {
httpBackend.createRequest.mockReturnValue(Promise.resolve('10000'));
Expand Down
12 changes: 9 additions & 3 deletions packages/taquito/src/read-provider/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ export type BlockIdentifier = 'head' | `head~${number}` | `B${string}` | number;

export interface TzReadProvider {
/**
* @description Access the spendable balance of a contract, excluding frozen bonds.
* @param address address from which we want to retrieve the balance
* @description The spendable balance of a contract (in mutez), also known as liquid balance. Corresponds to tez owned by the contract that are neither staked, nor in unstaked requests, nor in frozen bonds. Identical to the 'spendable' RPC.
* @param address address from which we want to retrieve the spendable balance
* @param block from which we want to retrieve the balance
* @returns the balance in mutez
*/
getBalance(address: string, block: BlockIdentifier): Promise<BigNumber>;

/**
* @description The spendable balance of a contract (in mutez), also known as liquid balance. Corresponds to tez owned by the contract that are neither staked, nor in unstaked requests, nor in frozen bonds. Identical to the 'balance' RPC.
* @param address address from which we want to retrieve the spendable balance
* @param block from which we want to retrieve the balance
* @returns the balance in mutez
*/
getSpendable(address: string, block: BlockIdentifier): Promise<BigNumber>;
/**
* @description Access the delegate of a contract, if any.
* @param address contract address from which we want to retrieve the delegate (baker)
Expand Down
14 changes: 12 additions & 2 deletions packages/taquito/src/read-provider/rpc-read-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,25 @@ export class RpcReadAdapter implements TzReadProvider {
constructor(private rpc: RpcClientInterface) {}

/**
* @description Access the spendable balance of a contract, excluding frozen bonds.
* @param address address from which we want to retrieve the balance
* @description The spendable balance of a contract (in mutez), also known as liquid balance. Corresponds to tez owned by the contract that are neither staked, nor in unstaked requests, nor in frozen bonds. Identical to the 'spendable' RPC.
* @param address address from which we want to retrieve the spendable balance
* @param block from which we want to retrieve the balance
* @returns the balance in mutez
*/
async getBalance(address: string, block: BlockIdentifier): Promise<BigNumber> {
return this.rpc.getBalance(address, { block: String(block) });
}

/**
* @description The spendable balance of a contract (in mutez), also known as liquid balance. Corresponds to tez owned by the contract that are neither staked, nor in unstaked requests, nor in frozen bonds. Identical to the 'balance' RPC.
* @param address address from which we want to retrieve the spendable balance
* @param block from which we want to retrieve the balance
* @returns the balance in mutez
*/
async getSpendable(address: string, block: BlockIdentifier): Promise<BigNumber> {
return this.rpc.getSpendable(address, { block: String(block) });
}

/**
* @description Access the delegate of a contract, if any.
* @param address contract address from which we want to retrieve the delegate (baker)
Expand Down
6 changes: 5 additions & 1 deletion packages/taquito/src/tz/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ export interface TzProvider {
* @param address Tezos address you want to get the spendable balance for (eg tz1...)
*/
getBalance(address: string): Promise<BigNumber>;

/**
*
* @param address Tezos address you want to get the spendable balance for (eg tz1...)
*/
getSpendable(address: string): Promise<BigNumber>;
/**
*
* @param address Tezos address you want to get the delegate for (eg tz1...)
Expand Down
8 changes: 8 additions & 0 deletions packages/taquito/src/tz/rpc-tz-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ export class RpcTzProvider extends Provider implements TzProvider {
return this.context.readProvider.getBalance(address, 'head');
}

async getSpendable(address: string): Promise<BigNumber> {
const addressValidation = validateAddress(address);
if (addressValidation !== ValidationResult.VALID) {
throw new InvalidAddressError(address, invalidDetail(addressValidation));
}
return this.context.readProvider.getSpendable(address, 'head');
}

async getDelegate(address: string): Promise<string | null> {
const addressValidation = validateAddress(address);
if (addressValidation !== ValidationResult.VALID) {
Expand Down
2 changes: 2 additions & 0 deletions packages/taquito/test/batch/rpc-batch-provider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('OperationBatch test', () => {
getCounter: jest.Mock<any, any>;
getProtocolConstants: jest.Mock<any, any>;
getBalance: jest.Mock<any, any>;
getSpendable: jest.Mock<any, any>;
isAccountRevealed: jest.Mock<any, any>;
getChainId: jest.Mock<any, any>;
};
Expand Down Expand Up @@ -92,6 +93,7 @@ describe('OperationBatch test', () => {
getCounter: jest.fn(),
getProtocolConstants: jest.fn(),
getBalance: jest.fn(),
getSpendable: jest.fn(),
isAccountRevealed: jest.fn(),
getChainId: jest.fn(),
};
Expand Down
2 changes: 2 additions & 0 deletions packages/taquito/test/contract/rpc-contract-provider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ describe('RpcContractProvider test', () => {
getCounter: jest.Mock<any, any>;
getProtocolConstants: jest.Mock<any, any>;
getBalance: jest.Mock<any, any>;
getSpendable: jest.Mock<any, any>;
isAccountRevealed: jest.Mock<any, any>;
getChainId: jest.Mock<any, any>;
};
Expand Down Expand Up @@ -124,6 +125,7 @@ describe('RpcContractProvider test', () => {
getCounter: jest.fn(),
getProtocolConstants: jest.fn(),
getBalance: jest.fn(),
getSpendable: jest.fn(),
isAccountRevealed: jest.fn(),
getChainId: jest.fn(),
};
Expand Down
4 changes: 4 additions & 0 deletions packages/taquito/test/estimate/rpc-estimate-provider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ describe('RPCEstimateProvider test signer', () => {
let mockRpcClient: {
getScript: jest.Mock<any, any>;
getBalance: jest.Mock<any, any>;
getSpendable: jest.Mock<any, any>;
getStorage: jest.Mock<any, any>;
getBlockHeader: jest.Mock<any, any>;
getManagerKey: jest.Mock<any, any>;
Expand Down Expand Up @@ -74,6 +75,7 @@ describe('RPCEstimateProvider test signer', () => {
runOperation: jest.fn(),
simulateOperation: jest.fn(),
getBalance: jest.fn(),
getSpendable: jest.fn(),
getBlock: jest.fn(),
getScript: jest.fn(),
getManagerKey: jest.fn(),
Expand Down Expand Up @@ -1124,6 +1126,7 @@ describe('RPCEstimateProvider test wallet', () => {
let mockRpcClient: {
getScript: jest.Mock<any, any>;
getBalance: jest.Mock<any, any>;
getSpendable: jest.Mock<any, any>;
getStorage: jest.Mock<any, any>;
getBlockHeader: jest.Mock<any, any>;
getManagerKey: jest.Mock<any, any>;
Expand Down Expand Up @@ -1152,6 +1155,7 @@ describe('RPCEstimateProvider test wallet', () => {
runOperation: jest.fn(),
simulateOperation: jest.fn(),
getBalance: jest.fn(),
getSpendable: jest.fn(),
getBlock: jest.fn(),
getScript: jest.fn(),
getManagerKey: jest.fn(),
Expand Down
2 changes: 2 additions & 0 deletions packages/taquito/test/prepare/prepare-provider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ describe('PrepareProvider test', () => {
getCounter: jest.Mock<any, any>;
getProtocolConstants: jest.Mock<any, any>;
getBalance: jest.Mock<any, any>;
getSpendable: jest.Mock<any, any>;
isAccountRevealed: jest.Mock<any, any>;
getChainId: jest.Mock<any, any>;
};
Expand Down Expand Up @@ -51,6 +52,7 @@ describe('PrepareProvider test', () => {
getCounter: jest.fn(),
getProtocolConstants: jest.fn(),
getBalance: jest.fn(),
getSpendable: jest.fn(),
isAccountRevealed: jest.fn(),
getChainId: jest.fn(),
};
Expand Down
Loading

0 comments on commit 1cd66b4

Please sign in to comment.