Skip to content

Commit

Permalink
feat: provider getContractVersion, contract getVersion, cairo getAbiC…
Browse files Browse the repository at this point in the history
…ontractVersion
  • Loading branch information
tabaktoni committed Sep 22, 2023
1 parent 8c5b6d3 commit 71e634e
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 2 deletions.
5 changes: 5 additions & 0 deletions __tests__/cairo1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ describeIfDevnet('Cairo 1 Devnet', () => {
expect(cairo1Contract).toBeInstanceOf(Contract);
});

test('getCairoVersion', async () => {
const version1 = await cairo1Contract.getVersion();
expect(version1).toEqual({ cairo: '1', compiler: '1' });
});

test('ContractFactory on Cairo1', async () => {
const c1CFactory = new ContractFactory({
compiledContract: compiledHelloSierra,
Expand Down
10 changes: 9 additions & 1 deletion __tests__/cairo1v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const { uint256, tuple, isCairo1Abi } = cairo;
const { toHex } = num;
const { starknetKeccak } = selector;

describe('Cairo 1 Devnet', () => {
describe('Cairo 1', () => {
const provider = getTestProvider();
const account = getTestAccount(provider);
describe('API & Contract interactions', () => {
Expand Down Expand Up @@ -72,6 +72,14 @@ describe('Cairo 1 Devnet', () => {
expect(cairo210Contract).toBeInstanceOf(Contract);
});

test('getCairoVersion', async () => {
const version1 = await cairo1Contract.getVersion();
expect(version1).toEqual({ cairo: '1', compiler: '2' });

const version210 = await cairo210Contract.getVersion();
expect(version210).toEqual({ cairo: '1', compiler: '2' });
});

xtest('validate TS for redeclare - skip testing', async () => {
const cc0 = await account.getClassAt(dd.deploy.address);
const cc0_1 = await account.getClassByHash(toHex(dd.declare.class_hash));
Expand Down
5 changes: 5 additions & 0 deletions __tests__/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ describe('contract module', () => {
);
});

test('getCairoVersion', async () => {
const version = await erc20Contract.getVersion();
expect(version).toEqual({ cairo: '0', compiler: '0' });
});

test('isCairo1', async () => {
const isContractCairo1: boolean = erc20Contract.isCairo1();
expect(isContractCairo1).toBe(false);
Expand Down
5 changes: 5 additions & 0 deletions __tests__/defaultProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ describe('defaultProvider', () => {
expect(exampleTransactionHash).toBeTruthy();
});

test('getContractVersion', async () => {
const version = await testProvider.getContractVersion(erc20ContractAddress);
expect(version).toEqual({ cairo: '0', compiler: '0' });
});

describe('getBlock', () => {
test('getBlock(blockIdentifier=latest)', async () => {
expect(exampleBlock).not.toBeNull();
Expand Down
4 changes: 4 additions & 0 deletions src/contract/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@ export class Contract implements ContractInterface {
return cairo.isCairo1Abi(this.abi);
}

public async getVersion() {
return this.providerOrAccount.getContractVersion(this.address);
}

public typed<TAbi extends AbiKanabi>(tAbi: TAbi): TypedContract<TAbi> {
return this as TypedContract<typeof tAbi>;
}
Expand Down
7 changes: 7 additions & 0 deletions src/contract/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
BlockIdentifier,
CallOptions,
ContractFunction,
ContractVersion,
EstimateFeeResponse,
GetTransactionReceiptResponse,
Invocation,
Expand Down Expand Up @@ -132,5 +133,11 @@ export abstract class ContractInterface {
*/
public abstract isCairo1(): boolean;

/**
* Gets contract's version (cairo version & compiler version)
* @returns ContractVersion
*/
public abstract getVersion(): Promise<ContractVersion>;

public abstract typed<TAbi extends AbiKanabi>(tAbi: TAbi): TypedContract<TAbi>;
}
5 changes: 5 additions & 0 deletions src/provider/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
SimulateTransactionResponse,
StateUpdateResponse,
Storage,
getContractVersionOptions,
getEstimateFeeBulkOptions,
getSimulateTransactionOptions,
waitForTransactionOptions,
Expand Down Expand Up @@ -221,4 +222,8 @@ export class Provider implements ProviderInterface {
public async getAddressFromStarkName(name: string, StarknetIdContract?: string): Promise<string> {
return getAddressFromStarkName(this, name, StarknetIdContract);
}

public async getContractVersion(contractAddress: string, options?: getContractVersionOptions) {
return this.provider.getContractVersion(contractAddress, options);
}
}
14 changes: 14 additions & 0 deletions src/provider/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
Call,
CallContractResponse,
ContractClassResponse,
ContractVersion,
DeclareContractResponse,
DeclareContractTransaction,
DeployAccountContractPayload,
Expand All @@ -24,6 +25,7 @@ import type {
SimulateTransactionResponse,
StateUpdateResponse,
Storage,
getContractVersionOptions,
getEstimateFeeBulkOptions,
getSimulateTransactionOptions,
waitForTransactionOptions,
Expand Down Expand Up @@ -330,4 +332,16 @@ export abstract class ProviderInterface {
* @returns StateUpdateResponse
*/
public abstract getStateUpdate(blockIdentifier?: BlockIdentifier): Promise<StateUpdateResponse>;

/**
* Gets the contract version from the provided address
* @param contractAddress string
* @param options - getContractVersionOptions
* - (optional) compiler - (default true) extract compiler version using type tactic from abi
* - (optional) blockIdentifier - block identifier
*/
public abstract getContractVersion(
contractAddress: string,
options?: getContractVersionOptions
): Promise<ContractVersion>;
}
17 changes: 17 additions & 0 deletions src/provider/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
RpcProviderOptions,
SimulateTransactionResponse,
TransactionType,
getContractVersionOptions,
getEstimateFeeBulkOptions,
getSimulateTransactionOptions,
waitForTransactionOptions,
Expand All @@ -38,6 +39,7 @@ import {
TransactionFinalityStatus,
} from '../types/api/rpc';
import { CallData } from '../utils/calldata';
import { getAbiContractVersion } from '../utils/calldata/cairo';
import { isSierra } from '../utils/contract';
import { pascalToSnake } from '../utils/encode';
import fetch from '../utils/fetchPonyfill';
Expand Down Expand Up @@ -251,6 +253,21 @@ export class RpcProvider implements ProviderInterface {
throw new Error('RPC does not implement getCode function');
}

public async getContractVersion(
contractAddress: string,
{ blockIdentifier = this.blockIdentifier, compiler = true }: getContractVersionOptions
) {
const contractClass = await this.getClassAt(contractAddress, blockIdentifier);
if (isSierra(contractClass)) {
if (compiler) {
const abiTest = getAbiContractVersion(contractClass.abi);
return { cairo: '1', compiler: abiTest.compiler };
}
return { cairo: '1', compiler: 'unknown' };
}
return { cairo: '0', compiler: '0' };
}

public async getEstimateFee(
invocation: Invocation,
invocationDetails: InvocationsDetailsWithNonce,
Expand Down
20 changes: 20 additions & 0 deletions src/provider/sequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ import {
TransactionExecutionStatus,
TransactionFinalityStatus,
TransactionType,
getContractVersionOptions,
getEstimateFeeBulkOptions,
getSimulateTransactionOptions,
waitForTransactionOptions,
} from '../types';
import { CallData } from '../utils/calldata';
import { getAbiContractVersion } from '../utils/calldata/cairo';
import { isSierra } from '../utils/contract';
import fetch from '../utils/fetchPonyfill';
import {
Expand Down Expand Up @@ -342,6 +344,24 @@ export class SequencerProvider implements ProviderInterface {
return this.fetchEndpoint('get_compiled_class_by_class_hash', { classHash, blockIdentifier });
}

public async getContractVersion(
contractAddress: string,
{ blockIdentifier, compiler }: getContractVersionOptions = {
blockIdentifier: this.blockIdentifier,
compiler: true,
}
) {
const contractClass = await this.getClassAt(contractAddress, blockIdentifier);
if (isSierra(contractClass)) {
if (compiler) {
const abiTest = getAbiContractVersion(contractClass.abi);
return { cairo: '1', compiler: abiTest.compiler };
}
return { cairo: '1', compiler: 'unknown' };
}
return { cairo: '0', compiler: '0' };
}

public async invokeFunction(
functionInvocation: Invocation,
details: InvocationsDetailsWithNonce
Expand Down
15 changes: 15 additions & 0 deletions src/types/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ export type getSimulateTransactionOptions = {
skipFeeCharge?: boolean;
};

export type getContractVersionOptions = {
blockIdentifier?: BlockIdentifier;
compiler?: boolean;
};

export type getEstimateFeeBulkOptions = {
blockIdentifier?: BlockIdentifier;
skipValidate?: boolean;
Expand All @@ -241,4 +246,14 @@ export interface CallStruct {
calldata: string[];
}

/**
* Represent Contract version
* cairo: version of the cairo language
* compiler: version of the cairo compiler used to compile the contract
*/
export type ContractVersion = {
cairo: string | 'unknown';
compiler: string | 'unknown';
};

export * from './contract';
38 changes: 37 additions & 1 deletion src/utils/calldata/cairo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { Abi, AbiEnums, AbiStructs, BigNumberish, Litteral, Uint, Uint256 } from '../../types';
import {
Abi,
AbiEnums,
AbiStructs,
BigNumberish,
ContractVersion,
Litteral,
Uint,
Uint256,
} from '../../types';
import { isBigInt, isHex, isStringWholeNumber } from '../num';
import { encodeShortString, isShortString, isText } from '../shortString';
import { UINT_128_MAX, isUint256 } from '../uint256';
Expand Down Expand Up @@ -59,6 +68,33 @@ export function isCairo1Abi(abi: Abi): boolean {
throw new Error(`Error in ABI. No input/output in function ${firstFunction.name}`);
}

/**
* Return ContractVersion (Abi version) based on Abi
* or undefined for unknown version
* @param abi
* @returns string
*/
export function getAbiContractVersion(abi: Abi): ContractVersion {
// determine by interface for "Cairo 1.2"
if (abi.find((it) => it.type === 'interface')) {
return { cairo: '1', compiler: '2' };
}

// determine by function io types "Cairo 1.1" or "Cairo 0.0"
// find first function with inputs or outputs
const testFunction = abi.find(
(it) => it.type === 'function' && (it.inputs.length || it.outputs.length)
);
if (!testFunction) {
return { cairo: 'unknown', compiler: 'unknown' };
}
const io = testFunction.inputs.length ? testFunction.inputs : testFunction.outputs;
if (isCairo1Type(io[0].type)) {
return { cairo: '1', compiler: '1' };
}
return { cairo: '0', compiler: '0' };
}

/**
* named tuple cairo type is described as js object {}
* struct cairo type are described as js object {}
Expand Down

0 comments on commit 71e634e

Please sign in to comment.