Skip to content

Commit

Permalink
solana address validation + docs. (#608)
Browse files Browse the repository at this point in the history
* docs: SolAddress.
* solana address validation.
  • Loading branch information
b4rtaz authored Aug 24, 2022
1 parent 263779d commit 5644d32
Show file tree
Hide file tree
Showing 12 changed files with 297 additions and 34 deletions.
6 changes: 6 additions & 0 deletions .changeset/beige-onions-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@moralisweb3/sol-utils': patch
'@moralisweb3/sol-api': patch
---

Added a validation of a Solana address.
2 changes: 1 addition & 1 deletion packages/integration/mockRequests/solApi/getNFT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { rest } from 'msw';
import { SOL_API_ROOT, MOCK_API_KEY } from '../config';

export const mockGetNFTs: Record<string, string> = {
'5xoBq7f7CDgZwqHrDBdRWM84ExRetg4gZq93dyJtoSwp': '6zZsdnfhhfnf',
'5xoBq7f7CDgZwqHrDBdRWM84ExRetg4gZq93dyJtoSwp': '5xoBq7f7CDgZwqHrDBdRWM84ExRetg4gZq93dyJtoSwp',
};

export const mockGetNFT = rest.get(`${SOL_API_ROOT}/account/:network/:address/nft`, (req, res, ctx) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/integration/test/solApi/getBalance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('Moralis SolApi', () => {
const failedResult = await SolApi.account
.getBalance({
network: 'mainnet',
address: '5xoBq7',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
})
.then()
.catch((err: any) => {
Expand All @@ -38,7 +38,7 @@ describe('Moralis SolApi', () => {
expect(
SolApi.account.getBalance({
network: 'mainnet',
address: '5xoB',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`"[C0006] Request failed with status 400"`);
});
Expand Down
4 changes: 2 additions & 2 deletions packages/integration/test/solApi/getMetadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('Moralis SolApi', () => {
const failedResult = await SolApi.nft
.getNFTMetadata({
network: 'mainnet',
address: 'A8rFZ2Y3Kcr2A',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
})
.then()
.catch((err) => {
Expand All @@ -38,7 +38,7 @@ describe('Moralis SolApi', () => {
expect(
SolApi.nft.getNFTMetadata({
network: 'mainnet',
address: 'A8rFZ',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`"[C0006] Request failed with status 400"`);
});
Expand Down
6 changes: 3 additions & 3 deletions packages/integration/test/solApi/getNFT.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ describe('Moralis SolApi', () => {

expect(result).toBeDefined();
expect(result).toEqual(expect.objectContaining({}));
expect(result.raw).toStrictEqual({ associatedTokenAddress: '6zZsdnfhhfnf' });
expect(result.raw).toStrictEqual({ associatedTokenAddress: '5xoBq7f7CDgZwqHrDBdRWM84ExRetg4gZq93dyJtoSwp' });
});

it('should not get the NFTs of an account', async () => {
const failedResult = await SolApi.account
.getNFTs({
network: 'mainnet',
address: '5xogZq93dyJtoSwp',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
})
.then()
.catch((err) => {
Expand All @@ -38,7 +38,7 @@ describe('Moralis SolApi', () => {
expect(
SolApi.account.getNFTs({
network: 'mainnet',
address: '5xotoSwp',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`"[C0006] Request failed with status 400"`);
});
Expand Down
4 changes: 2 additions & 2 deletions packages/integration/test/solApi/getPortfolio.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('Moralis SolApi', () => {
const failedResult = await SolApi.account
.getPortfolio({
network: 'mainnet',
address: '5xoBq7f7CDgZwqHrDBd',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
})
.then()
.catch((err) => {
Expand All @@ -38,7 +38,7 @@ describe('Moralis SolApi', () => {
expect(
SolApi.account.getPortfolio({
network: 'mainnet',
address: '5xoBq7f7CDgZwqHrDBdRW',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`"[C0006] Request failed with status 400"`);
});
Expand Down
4 changes: 2 additions & 2 deletions packages/integration/test/solApi/getSPL.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('Moralis SolApi', () => {
const failedResult = await SolApi.account
.getSPL({
network: 'mainnet',
address: '5xoBq7f7CDgZwqHrDBd',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
})
.then()
.catch((err) => {
Expand All @@ -38,7 +38,7 @@ describe('Moralis SolApi', () => {
expect(
SolApi.account.getSPL({
network: 'mainnet',
address: '5xoBq7f7CDgZwqHrDBdRW',
address: '5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x',
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`"[C0006] Request failed with status 400"`);
});
Expand Down
3 changes: 2 additions & 1 deletion packages/solUtils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"typescript": "^4.5.5"
},
"dependencies": {
"@moralisweb3/core": "^2.0.3"
"@moralisweb3/core": "^2.0.3",
"@solana/web3.js": "^1.53.0"
}
}
12 changes: 9 additions & 3 deletions packages/solUtils/src/dataTypes/SolAddress/SolAddress.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ describe('SolAddress', () => {
expect(address.format()).toEqual(ADDRESS);
});

it('create() throws an error when a passed address is invalid', () => {
expect(() => SolAddress.create('5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5x5xwp')).toThrowError(
'Invalid Solana address provided',
);
});

it('create() does not create a new instance when SolAddress passed', () => {
const address1 = SolAddress.create(ADDRESS);
const address2 = SolAddress.create(address1);
Expand All @@ -22,11 +28,11 @@ describe('SolAddress', () => {
it('equals() returns correct value', () => {
const a = SolAddress.create(ADDRESS);
const b = SolAddress.create(ADDRESS);
const c = SolAddress.create('9xoBq7f7CDgZwqHrDBdRWM84ExRetg4gZq93dyJtoSwp');
const c = '9xoBq7f7CDgZwqHrDBdRWM84ExRetg4gZq93dyJtoSwp';

expect(a.equals(b)).toBe(true);
expect(b.equals(a)).toBe(true);
expect(c.equals(a)).toBe(false);
expect(c.equals(b)).toBe(false);
expect(a.equals(c)).toBe(false);
expect(b.equals(c)).toBe(false);
});
});
52 changes: 48 additions & 4 deletions packages/solUtils/src/dataTypes/SolAddress/SolAddress.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,76 @@
import { MoralisData, MoralisDataFormatted } from '@moralisweb3/core';
import { CoreErrorCode, MoralisCoreError, MoralisData, MoralisDataFormatted } from '@moralisweb3/core';
import { PublicKey } from '@solana/web3.js';

/**
* Valid input for a new SolAddress instance.
* This can be an existing SolAddress or a valid address string.
*
* @example "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
* @example SolAddress.create("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM")
*/
export type SolAddressish = SolAddress | string;

/**
* A representation of an address on the Solana network.
*
* Use this class any time you work with an address.
*
* @category DataType
*/
export class SolAddress implements MoralisData {
/**
* Create a new instance of SolAddress from any valid address input.
*
* @example `const address = SolAddress.create("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM")`
* @throws an error when a passed address is invalid.
*/
public static create(address: SolAddressish): SolAddress {
return address instanceof SolAddress ? address : new SolAddress(SolAddress.parse(address));
}

private static parse(address: string): string {
// TODO: add address validation
if (!PublicKey.isOnCurve(address)) {
throw new MoralisCoreError({
code: CoreErrorCode.INVALID_ARGUMENT,
message: 'Invalid Solana address provided',
});
}
return address;
}

public constructor(public readonly address: string) {}

/**
* Formats the address to a specific format.
* Currently returns a string representing the address.
* @example "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
*/
public format(): MoralisDataFormatted {
// TODO: add `format` argument
return this.address;
}

public equals(address: SolAddress): boolean {
return this.address === address.address;
/**
* Checks the equality of the current address with another Solana address.
* @example `address.equals("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM")`
* @example `address.equals(SolAddress.create("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"))`
*/
public equals(address: SolAddressish): boolean {
return this.address === SolAddress.create(address).address;
}

/**
* @returns a string representing the address.
* @example "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
*/
public toString(): string {
return this.address;
}

/**
* @returns a string representing the address.
* @example "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
*/
public toJSON(): string {
return this.address;
}
Expand Down
1 change: 1 addition & 0 deletions typedoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"excludeInternal": true,
"excludePrivate": true,
"excludeNotDocumented": true,
"exclude": ["packages/eslintConfig"],
"validation": {
"notExported": false,
"invalidLink": true,
Expand Down
Loading

0 comments on commit 5644d32

Please sign in to comment.