Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sdk): add getProtocolVersion function #76

Merged
merged 12 commits into from
Apr 22, 2024
1 change: 1 addition & 0 deletions sdk/package.json
Original file line number Diff line number Diff line change
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);
}
}
35 changes: 35 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,34 @@ export function getInterfaceId(iface: Interface): string {
}
return interfaceId.toHexString();
}
/**
josemarinas marked this conversation as resolved.
Show resolved Hide resolved
* 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) {
// ethers5 throws an call exception error which could mean a lot of things
josemarinas marked this conversation as resolved.
Show resolved Hide resolved
// so this is not accurate
version = [1, 0, 0];
josemarinas marked this conversation as resolved.
Show resolved Hide resolved
}
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`;

josemarinas marked this conversation as resolved.
Show resolved Hide resolved
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,
heueristik marked this conversation as resolved.
Show resolved Hide resolved
},
{
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
38 changes: 37 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,34 @@ describe('introspection', () => {
expect(interfaceId).toEqual('0x9bb235aa');
});
});
describe('getProtocolVersion', () => {
it('should return the correct protocol version', async () => {
josemarinas marked this conversation as resolved.
Show resolved Hide resolved
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);
});
});
});
Loading