Skip to content

Commit

Permalink
feat(sdk): add getProtocolVersion function (#76)
Browse files Browse the repository at this point in the history
* add: getProtocolVersion function

* fix: prettier

* fix: linting

* Update sdk/src/introspection.ts

Co-authored-by: Carles <[email protected]>

* Update sdk/src/introspection.ts

Co-authored-by: Michael Heuer <[email protected]>

* Update sdk/test/unit/introspection.test.ts

Co-authored-by: Michael Heuer <[email protected]>

* remove: constant ADDRESS_ONE

* fix: prettier

* update: changelog and package version

* fix: export of addres one

* fix: update getProtocolVersion return comment

* chore: add ADDRESS_ONE to constants

---------

Co-authored-by: Carles <[email protected]>
Co-authored-by: Michael Heuer <[email protected]>
  • Loading branch information
3 people authored Apr 22, 2024
1 parent 2093fe6 commit b3bd6da
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 4 deletions.
1 change: 1 addition & 0 deletions sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Add `resolveEnsName` functions
- Add `getProtocolVersion` function

### Removed

Expand Down
3 changes: 2 additions & 1 deletion sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@aragon/osx-commons-sdk",
"author": "Aragon Association",
"version": "0.0.1-alpha.8",
"version": "0.0.1-alpha.9",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/osx-commons-sdk.esm.js",
Expand Down Expand Up @@ -58,6 +58,7 @@
"@ethersproject/contracts": "^5.7.0",
"@ethersproject/hash": "^5.7.0",
"@ethersproject/providers": "^5.7.0",
"@ethersproject/address": "^5.7.0",
"ipfs-http-client": "^51.0.0"
}
}
6 changes: 6 additions & 0 deletions sdk/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,9 @@ export class InvalidBitPositionError extends SdkError {
);
}
}

export class InvalidAddressError extends SdkError {
constructor(address: string, cause?: any) {
super(`Invalid address: ${address}`, cause);
}
}
38 changes: 38 additions & 0 deletions sdk/src/introspection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import {InvalidAddressError} from './errors';
import {Interface} from '@ethersproject/abi';
import {isAddress} from '@ethersproject/address';
import {BigNumber} from '@ethersproject/bignumber';
import {Contract} from '@ethersproject/contracts';
import {JsonRpcProvider} from '@ethersproject/providers';

/**
* Gets the interfaceId of a given interface
Expand All @@ -16,3 +20,37 @@ export function getInterfaceId(iface: Interface): string {
}
return interfaceId.toHexString();
}

/**
* Gets the protocol version of a contract, if the contract does not have a
* protocolVersion function, it will return [1, 0, 0]
*
* @export
* @param {string} rpc
* @param {string} contractAddress
* @return {*} {Promise<[number, number, number]>}
*/
export async function getProtocolVersion(
rpc: string,
contractAddress: string
): Promise<[number, number, number]> {
if (!isAddress(contractAddress)) {
throw new InvalidAddressError(contractAddress);
}
const provider = new JsonRpcProvider(rpc);
const iface = new Interface([
'function protocolVersion() public pure returns (uint8[3] memory)',
]);
const contract = new Contract(contractAddress, iface, provider);
let version: [number, number, number];
try {
version = await contract.protocolVersion();
} catch (e) {
// version 1.0.0 of the contract does not have a protocolVersion function
// so if we receive an error we cannot differentiate between a call exception
// and a contract that does not have the function. So we assume that is
// a version 1.0.0 contract that does not have the function and return [1, 0, 0]
version = [1, 0, 0];
}
return version;
}
2 changes: 2 additions & 0 deletions sdk/test/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export const TEST_ABI: MetadataAbiInput[] = [
},
];

export const ADDRESS_ONE = `0x${'0'.repeat(39)}1`;

export const TEST_ENS_NAME = 'subdomain.test.eth';
export const TEST_INVALID_ENS_NAME = 'test.invalid';

Expand Down
37 changes: 37 additions & 0 deletions sdk/test/mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export function mockContractProtocolVersion(
version: [number, number, number] = [1, 0, 0],
throwException: boolean = false
) {
jest
.spyOn(jest.requireActual('@ethersproject/contracts'), 'Contract')
.mockImplementation(() => {
return {
protocolVersion: () => {
if (throwException) {
throw new Error('Error');
}
return Promise.resolve(version);
},
};
});
}

export function mockJSONRPCProvider(
chainId: number = 1,
blockNumber: number = 1
) {
return jest
.spyOn(jest.requireActual('@ethersproject/providers'), 'JsonRpcProvider')
.mockImplementation(() => {
return {
getNetwork: () => {
return {
chainId,
};
},
getBlockNumber: () => {
return Promise.resolve(blockNumber);
},
};
});
}
5 changes: 3 additions & 2 deletions sdk/test/unit/ens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
resolveEnsName,
} from '../../src';
import {
ADDRESS_ONE,
TEST_ENS_NAME,
TEST_HTTP_URI,
TEST_INVALID_ENS_NAME,
Expand All @@ -20,7 +21,7 @@ describe('ens', () => {
{
input: TEST_ENS_NAME,
network: 'mainnet',
output: `0x${'0'.repeat(39)}1`,
output: ADDRESS_ONE,
},
{
input: TEST_HTTP_URI,
Expand Down Expand Up @@ -48,7 +49,7 @@ describe('ens', () => {
{
input: TEST_ENS_NAME,
network: 'mainnet',
output: `0x${'0'.repeat(39)}1`,
output: ADDRESS_ONE,
},
{
input: TEST_HTTP_URI,
Expand Down
39 changes: 38 additions & 1 deletion sdk/test/unit/introspection.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import {getInterfaceId} from '../../src';
import {
InvalidAddressError,
getInterfaceId,
getProtocolVersion,
} from '../../src';
import {ADDRESS_ONE, TEST_HTTP_URI} from '../constants';
import {mockContractProtocolVersion, mockJSONRPCProvider} from '../mocks';
import {Interface} from '@ethersproject/abi';

describe('introspection', () => {
Expand All @@ -13,4 +19,35 @@ describe('introspection', () => {
expect(interfaceId).toEqual('0x9bb235aa');
});
});

describe('getProtocolVersion', () => {
it('should return the correct protocol version', async () => {
const expectedVersion: [number, number, number] = [1, 3, 0];
// mock call to the contract
mockJSONRPCProvider();
// mock the call to the contract
mockContractProtocolVersion(expectedVersion);
const version = await getProtocolVersion(TEST_HTTP_URI, ADDRESS_ONE);
expect(version).toEqual(expectedVersion);
});
it('should fail when an invalid address is passed', async () => {
const expectedVersion: [number, number, number] = [1, 3, 0];
// mock call to the contract
mockJSONRPCProvider();
// mock the call to the contract
mockContractProtocolVersion(expectedVersion);
await expect(() =>
getProtocolVersion(TEST_HTTP_URI, '0x')
).rejects.toThrow(new InvalidAddressError('0x'));
});
it('should return [1,0,0] when the call throws an error', async () => {
const expectedVersion: [number, number, number] = [1, 0, 0];
// mock call to the contract
mockJSONRPCProvider();
// mock the call to the contract
mockContractProtocolVersion(expectedVersion, true);
const version = await getProtocolVersion(TEST_HTTP_URI, ADDRESS_ONE);
expect(version).toEqual(expectedVersion);
});
});
});

0 comments on commit b3bd6da

Please sign in to comment.