-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: rpc implementation [part 2/5] (#2)
* chore: added initial yarn + typescript structure * chore: added hathor-rpc-handler * chore: update flake inputs * chore: added github PR templates * chore: added tsconfig and eslint.config * chore: opt-out of yarn's PnP * chore: removed release candidate and release PR templates * chore: removed gitattributes * feat: added rpc handler and rpc method initial implementation * chore: added jest * feat: implemented getAddress method * feat: added getUtxos method and improved other methods * feat: added getUtxos method and tests * feat: added signWithAddress * feat: getConnectedNetwork rpc * feat: sendNanoContractTx and tests * refactor: better rpc handling * feat: getAddress accepting all parameters and throwing if invalid network * feat: passing tests for getAddress * refactor: organize prompt types * refactor: getBalance according to RPC document * tests: most tests passing * tests: tests passing * refactor: better prompt response for pin code * refator: removed unused log * chore: updated package.json and gitignore * chore: eslint * chore: added CI * refactor: prompt -> trigger * tests: updated tests to use new methods * chore: updated wallet-lib to v1.8.0 * refactor: use NATIVE_TOKEN_UID * refactor: SignMessageFailure -> SignMessageError * chore: updated actions/checkout to v4 and cachix/install-nix to latest version * refactor: using getNetwork() instead of getNetworkObject() * fix: added missing exports * refactor: using NATIVE_TOKEN_UID * chore: update magic-nix-cache-action to v7 * refactor: removed sendTx * feat: sending method to prompt handler in sendNanoContractTx * refactor: better response for signWithAddress * fix: unused export * refactor: pass requestMetadata and promptHandler to GetConnectedNetwork RPC * refactor: receiving, but ignoring requestMetadata and promptHandler in getConnectedNetwork * feat: accepting authorities in getUtxos * docs: added requestMetadata to docstrings * refactor: removed unused logic to set blueprintId to the ncId * refactor: SendNanoContractTxFailure -> SendNanoContractTxError * refactor: tests and invalid await in getNetwork * feat: normalized rpc responses * refactor: removed query utxos filters * fix: added missing types from RpcRequest * refactor: returning RpcResponse on handleRpcRequest type
- Loading branch information
1 parent
f0d2dd5
commit 31b3f85
Showing
28 changed files
with
6,115 additions
and
630 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
name: CI | ||
on: [push] | ||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
services: | ||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
|
||
- name: Install Nix | ||
uses: cachix/install-nix-action/ba0dd844c9180cbf77aa72a116d6fbc515d0e87b | ||
with: | ||
nix_path: nixpkgs=channel:nixos-unstable | ||
extra_nix_config: | | ||
experimental-features = nix-command flakes | ||
- name: Cache Nix | ||
uses: DeterminateSystems/magic-nix-cache-action@v7 | ||
|
||
- name: Install dependencies | ||
run: | | ||
nix develop . -c yarn install | ||
- name: lint | ||
run: | | ||
nix develop . -c yarn workspace hathor-rpc-handler run lint | ||
- name: tests | ||
run: | | ||
nix develop . -c yarn workspace hathor-rpc-handler run test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { constants } from '@hathor/wallet-lib'; | ||
import { | ||
GetAddressRpcRequest, | ||
GetBalanceRpcRequest, | ||
GetConnectedNetworkRpcRequest, | ||
GetUtxosRpcRequest, | ||
RpcMethods, | ||
SignWithAddressRpcRequest, | ||
} from '../../src/types'; | ||
|
||
export const mockGetBalanceRequest: GetBalanceRpcRequest = { | ||
id: '1', | ||
jsonrpc: '2.0', | ||
method: RpcMethods.GetBalance, | ||
params: { | ||
network: 'mainnet', | ||
tokens: [constants.NATIVE_TOKEN_UID], | ||
}, | ||
}; | ||
|
||
export const mockGetAddressRequest: GetAddressRpcRequest = { | ||
id: '1', | ||
jsonrpc: '2.0', | ||
method: RpcMethods.GetAddress, | ||
params: { | ||
network: 'mainnet', | ||
type: 'index', | ||
index: 1, | ||
} | ||
}; | ||
|
||
export const mockGetUtxosRequest: GetUtxosRpcRequest = { | ||
id: '1', | ||
jsonrpc: '2.0', | ||
method: RpcMethods.GetUtxos, | ||
params: { | ||
network: 'mainnet', | ||
token: 'mock_token', | ||
maxUtxos: 10, | ||
filterAddress: 'mock_address', | ||
amountSmallerThan: 1000, | ||
amountBiggerThan: 10, | ||
maximumAmount: 10000, | ||
onlyAvailableUtxos: true, | ||
}, | ||
}; | ||
|
||
export const mockSignWithAddressRequest: SignWithAddressRpcRequest = { | ||
id: '1', | ||
jsonrpc: '2.0', | ||
method: RpcMethods.SignWithAddress, | ||
params: { | ||
network: 'mainnet', | ||
addressIndex: 0, | ||
message: 'Test message', | ||
}, | ||
}; | ||
|
||
export const mockGetConnectedNetworkRequest: GetConnectedNetworkRpcRequest = { | ||
id: '1', | ||
jsonrpc: '2.0', | ||
method: RpcMethods.GetConnectedNetwork, | ||
}; | ||
|
||
export const mockPromptHandler = jest.fn(); |
126 changes: 126 additions & 0 deletions
126
packages/hathor-rpc-handler/__tests__/rpcMethods/getAddress.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/** | ||
* Copyright (c) Hathor Labs and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import { NotImplementedError, PromptRejectedError } from '../../src/errors'; | ||
import { getAddress } from '../../src/rpcMethods/getAddress'; | ||
import { HathorWallet } from '@hathor/wallet-lib'; | ||
import { TriggerTypes, GetAddressRpcRequest, RpcMethods } from '../../src/types'; | ||
|
||
export const mockPromptHandler = jest.fn(); | ||
|
||
describe('getAddress', () => { | ||
let promptHandler: jest.Mock; | ||
let mockWallet: jest.Mocked<HathorWallet>; | ||
|
||
beforeEach(() => { | ||
promptHandler = jest.fn(); | ||
mockWallet = { | ||
getAddressAtIndex: jest.fn().mockReturnValue('mocked_address'), | ||
getCurrentAddress: jest.fn().mockReturnValue({ | ||
address: 'address1', | ||
index: 0, | ||
addressPath: `m/44'/280'/0'/0/10`, | ||
}), | ||
getNetwork: jest.fn().mockReturnValue('mainnet') | ||
} as unknown as HathorWallet; | ||
}); | ||
|
||
it('should return the current address for type "first_empty"', async () => { | ||
const rpcRequest: GetAddressRpcRequest = { | ||
id: '3', | ||
jsonrpc: '2.0', | ||
params: { type: 'first_empty', network: 'mainnet' }, | ||
method: RpcMethods.GetAddress, | ||
}; | ||
mockWallet.getCurrentAddress.mockResolvedValue('current-address'); | ||
promptHandler.mockReturnValueOnce(true); | ||
|
||
const address = await getAddress(rpcRequest, mockWallet, {}, promptHandler); | ||
|
||
expect(address.response).toBe('current-address'); | ||
expect(mockWallet.getCurrentAddress).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should throw NotImplementedError for type "full_path"', async () => { | ||
const rpcRequest: GetAddressRpcRequest = { | ||
id: '3', | ||
jsonrpc: '2.0', | ||
params: { type: 'full_path', network: 'mainnet' }, | ||
method: RpcMethods.GetAddress, | ||
}; | ||
|
||
await expect(getAddress(rpcRequest, mockWallet, {}, promptHandler)).rejects.toThrow(NotImplementedError); | ||
}); | ||
|
||
it('should return the address at index for type "index"', async () => { | ||
const rpcRequest: GetAddressRpcRequest = { | ||
id: '3', | ||
jsonrpc: '2.0', | ||
params: { type: 'index', index: 5, network: 'mainnet' }, | ||
method: RpcMethods.GetAddress, | ||
}; | ||
mockWallet.getAddressAtIndex.mockResolvedValue('address-at-index'); | ||
promptHandler.mockReturnValueOnce(true); | ||
|
||
const address = await getAddress(rpcRequest, mockWallet, {}, promptHandler); | ||
|
||
expect(address.response).toBe('address-at-index'); | ||
expect(mockWallet.getAddressAtIndex).toHaveBeenCalledWith(5); | ||
}); | ||
|
||
it('should return the client address for type "client"', async () => { | ||
const rpcRequest: GetAddressRpcRequest = { | ||
id: '3', | ||
jsonrpc: '2.0', | ||
params: { type: 'client', network: 'mainnet' }, | ||
method: RpcMethods.GetAddress, | ||
}; | ||
const clientPromptResponse = { data: { address: 'client-address' } }; | ||
promptHandler.mockResolvedValue(clientPromptResponse); | ||
|
||
const address = await getAddress(rpcRequest, mockWallet, {}, promptHandler); | ||
|
||
expect(address.response).toBe('client-address'); | ||
expect(promptHandler).toHaveBeenCalledWith({ | ||
type: TriggerTypes.AddressRequestClientPrompt, | ||
method: RpcMethods.GetAddress, | ||
}, {}); | ||
}); | ||
|
||
it('should throw PromptRejectedError if address confirmation is rejected', async () => { | ||
const rpcRequest: GetAddressRpcRequest = { | ||
id: '3', | ||
jsonrpc: '2.0', | ||
params: { type: 'first_empty', network: 'mainnet' }, | ||
method: RpcMethods.GetAddress, | ||
}; | ||
mockWallet.getCurrentAddress.mockResolvedValue('current-address'); | ||
promptHandler.mockResolvedValueOnce(false); | ||
|
||
await expect(getAddress(rpcRequest, mockWallet, {}, promptHandler)).rejects.toThrow(PromptRejectedError); | ||
}); | ||
|
||
it('should confirm the address if type is not "client"', async () => { | ||
const rpcRequest: GetAddressRpcRequest = { | ||
id: '3', | ||
jsonrpc: '2.0', | ||
params: { type: 'first_empty', network: 'mainnet' }, | ||
method: RpcMethods.GetAddress, | ||
}; | ||
mockWallet.getCurrentAddress.mockResolvedValue('current-address'); | ||
promptHandler.mockResolvedValue(true); | ||
|
||
const address = await getAddress(rpcRequest, mockWallet, {}, promptHandler); | ||
|
||
expect(address.response).toBe('current-address'); | ||
expect(promptHandler).toHaveBeenCalledWith({ | ||
type: TriggerTypes.AddressRequestPrompt, | ||
method: RpcMethods.GetAddress, | ||
data: { address: 'current-address' }, | ||
}, {}); | ||
}); | ||
}); |
105 changes: 105 additions & 0 deletions
105
packages/hathor-rpc-handler/__tests__/rpcMethods/getBalance.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/** | ||
* Copyright (c) Hathor Labs and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import { GetBalanceObject } from '@hathor/wallet-lib/lib/wallet/types'; | ||
import { NotImplementedError, PromptRejectedError } from '../../src/errors'; | ||
import { getBalance } from '../../src/rpcMethods/getBalance'; | ||
import { HathorWallet } from '@hathor/wallet-lib'; | ||
import { TriggerTypes, GetBalanceRpcRequest, RpcMethods } from '../../src/types'; | ||
|
||
const mockedTokenBalance: GetBalanceObject[] = [{ | ||
token: { | ||
id: 'moon-id', | ||
name: 'MOON TOKEN', | ||
symbol: 'MOON', | ||
}, | ||
balance: { | ||
unlocked: 0, | ||
locked: 0, | ||
}, | ||
tokenAuthorities: { | ||
unlocked: { | ||
mint: false, | ||
melt: false, | ||
}, | ||
locked: { | ||
mint: false, | ||
melt: false, | ||
} | ||
}, | ||
transactions: 0, | ||
lockExpires: null, | ||
}]; | ||
|
||
const BaseRpcCall = { | ||
jsonrpc: '2.0', | ||
id: '3', | ||
}; | ||
|
||
describe('getBalance', () => { | ||
let wallet: jest.Mocked<HathorWallet>; | ||
let promptHandler: jest.Mock; | ||
|
||
beforeEach(() => { | ||
wallet = { | ||
getBalance: jest.fn().mockReturnValue(Promise.resolve(mockedTokenBalance)), | ||
getNetwork: jest.fn().mockReturnValue('mainnet') | ||
} as unknown as HathorWallet; | ||
promptHandler = jest.fn(); | ||
}); | ||
|
||
it('should throw NotImplementedError if addressIndexes are specified', async () => { | ||
const rpcRequest: GetBalanceRpcRequest = { | ||
...BaseRpcCall, | ||
params: { | ||
network: 'mainnet', | ||
tokens: ['token1'], | ||
addressIndexes: [0], | ||
}, | ||
method: RpcMethods.GetBalance, | ||
}; | ||
|
||
await expect(getBalance(rpcRequest, wallet, {}, promptHandler)).rejects.toThrow(NotImplementedError); | ||
}); | ||
|
||
it('should return balances of specified tokens', async () => { | ||
const rpcRequest: GetBalanceRpcRequest = { | ||
...BaseRpcCall, | ||
params: { network: 'mainnet', tokens: ['token1', 'token2'], addressIndexes: undefined }, | ||
method: RpcMethods.GetBalance, | ||
}; | ||
|
||
promptHandler.mockResolvedValue(true); | ||
|
||
const balances = await getBalance(rpcRequest, wallet, {}, promptHandler); | ||
|
||
expect(balances.response).toEqual([mockedTokenBalance, mockedTokenBalance]); | ||
expect(wallet.getBalance).toHaveBeenCalledWith('token1'); | ||
expect(wallet.getBalance).toHaveBeenCalledWith('token2'); | ||
expect(promptHandler).toHaveBeenCalledWith({ | ||
type: TriggerTypes.GetBalanceConfirmationPrompt, | ||
method: RpcMethods.GetBalance, | ||
data: [mockedTokenBalance, mockedTokenBalance], | ||
}, {}); | ||
}); | ||
|
||
it('should throw PromptRejectedError if balance confirmation is rejected', async () => { | ||
const rpcRequest: GetBalanceRpcRequest = { | ||
...BaseRpcCall, | ||
params: { | ||
network: 'mainnet', | ||
tokens: ['token1'], | ||
addressIndexes: undefined, | ||
}, | ||
method: RpcMethods.GetBalance, | ||
}; | ||
wallet.getBalance.mockResolvedValue({ token: 'token1', balance: 100 }); | ||
promptHandler.mockResolvedValue(false); | ||
|
||
await expect(getBalance(rpcRequest, wallet, {}, promptHandler)).rejects.toThrow(PromptRejectedError); | ||
}); | ||
}); |
35 changes: 35 additions & 0 deletions
35
packages/hathor-rpc-handler/__tests__/rpcMethods/getConnectedNetwork.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/** | ||
* Copyright (c) Hathor Labs and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import { HathorWallet } from "@hathor/wallet-lib"; | ||
import { getConnectedNetwork } from "../../src/rpcMethods/getConnectedNetwork"; | ||
import { mockGetConnectedNetworkRequest } from "../mocks"; | ||
|
||
export const mockWallet = { | ||
getNetwork: jest.fn().mockReturnValue('mainnet') | ||
} as unknown as HathorWallet; | ||
|
||
describe('getConnectedNetwork', () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should return network information', async () => { | ||
const result = await getConnectedNetwork( | ||
mockGetConnectedNetworkRequest, | ||
mockWallet, | ||
{}, | ||
jest.fn(), | ||
); | ||
|
||
expect(mockWallet.getNetwork).toHaveBeenCalled(); | ||
expect(result.response).toStrictEqual({ | ||
network: 'mainnet', | ||
genesisHash: '', // TODO: Update when logic to retrieve genesisHash is implemented | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.