Repository: NethermindEth/warp (language: python)

Issue:
uint/intX -> uX : cairo1



Discussion / hints:

Fix patch:
diff --git a/src/ast/cairoNodes/cairoFunctionDefinition.ts b/src/ast/cairoNodes/cairoFunctionDefinition.ts
index 1d34395e4..0b7c63a11 100644
--- a/src/ast/cairoNodes/cairoFunctionDefinition.ts
+++ b/src/ast/cairoNodes/cairoFunctionDefinition.ts
@@ -29,6 +29,7 @@ export enum FunctionStubKind {
   FunctionDefStub,
   StorageDefStub,
   StructDefStub,
+  TraitStructDefStub,
 }
 
 export class CairoFunctionDefinition extends FunctionDefinition {
diff --git a/src/cairoUtilFuncGen/base.ts b/src/cairoUtilFuncGen/base.ts
index 33a38e728..b53a2c5bf 100644
--- a/src/cairoUtilFuncGen/base.ts
+++ b/src/cairoUtilFuncGen/base.ts
@@ -53,8 +53,9 @@ export abstract class CairoUtilFuncGenBase {
     name: string,
     inputs?: ParameterInfo[],
     outputs?: ParameterInfo[],
+    options?: { isTrait?: boolean },
   ): CairoImportFunctionDefinition {
-    return createImport(location, name, this.sourceUnit, this.ast, inputs, outputs);
+    return createImport(location, name, this.sourceUnit, this.ast, inputs, outputs, options);
   }
 }
 
diff --git a/src/cairoUtilFuncGen/memory/memoryRead.ts b/src/cairoUtilFuncGen/memory/memoryRead.ts
index b3245e014..f5a02a00f 100644
--- a/src/cairoUtilFuncGen/memory/memoryRead.ts
+++ b/src/cairoUtilFuncGen/memory/memoryRead.ts
@@ -6,6 +6,7 @@ import {
   FunctionStateMutability,
   generalizeType,
   TypeNode,
+  FunctionDefinition,
 } from 'solc-typed-ast';
 import { CairoFunctionDefinition, typeNameFromTypeNode } from '../../export';
 import {
@@ -101,9 +102,16 @@ export class MemoryReadGen extends StringIndexedFuncGen {
   }
 
   private getOrCreate(typeToRead: CairoType): GeneratedFunctionInfo {
+    const functionsCalled: FunctionDefinition[] = [this.requireImport(...DICT_READ)];
+
     const funcName = `WM${this.generatedFunctionsDef.size}_READ_${typeToRead.typeName}`;
     const resultCairoType = typeToRead.toString();
-    const [reads, pack] = serialiseReads(typeToRead, readFelt, readFelt);
+    const [reads, pack, requiredImports] = serialiseReads(typeToRead, readFelt, readFelt);
+
+    requiredImports.map((i) => {
+      functionsCalled.push(this.requireImport(...i.import, [], [], { isTrait: i.isTrait }));
+    });
+
     const funcInfo: GeneratedFunctionInfo = {
       name: funcName,
       code: [
@@ -113,7 +121,7 @@ export class MemoryReadGen extends StringIndexedFuncGen {
         `    return (${pack},);`,
         '}',
       ].join('\n'),
-      functionsCalled: [this.requireImport(...DICT_READ)],
+      functionsCalled: functionsCalled,
     };
     return funcInfo;
   }
diff --git a/src/cairoUtilFuncGen/serialisation.ts b/src/cairoUtilFuncGen/serialisation.ts
index 4e889f452..3dcea45c2 100644
--- a/src/cairoUtilFuncGen/serialisation.ts
+++ b/src/cairoUtilFuncGen/serialisation.ts
@@ -1,4 +1,5 @@
 import {
+  CairoContractAddress,
   CairoBool,
   CairoFelt,
   CairoStaticArray,
@@ -9,28 +10,48 @@ import {
   WarpLocation,
 } from '../utils/cairoTypeSystem';
 import { TranspileFailedError } from '../utils/errors';
-import { FELT252_INTO_BOOL } from '../utils/importPaths';
+import { CONTRACT_ADDRESS, FELT252_INTO_BOOL, OPTION_TRAIT } from '../utils/importPaths';
 
 export function serialiseReads(
   type: CairoType,
   readFelt: (offset: number) => string,
   readId: (offset: number) => string,
-): [reads: string[], pack: string, requiredImports: [string[], string][]] {
+): [
+  reads: string[],
+  pack: string,
+  requiredImports: { import: [string[], string]; isTrait?: boolean }[],
+] {
   const packExpression = producePackExpression(type);
   const reads: string[] = [];
-  const requiredImports: [string[], string][] = [];
+  const requiredImports: { import: [string[], string]; isTrait?: boolean }[] = [];
   const packString: string = packExpression
-    .map((elem: string | Read) => {
-      if (elem === Read.Felt) {
+    .map((elem: packExpReturnType) => {
+      if (elem.dataOrDataType === Read.UN) {
         reads.push(readFelt(reads.length));
-      } else if (elem === Read.Id) {
+        reads.push(
+          `let read${reads.length} = core::integer::u${elem.metadata.nBits}_from_felt252(read${
+            reads.length - 1
+          });`,
+        );
+      } else if (elem.dataOrDataType === Read.Address) {
+        requiredImports.push({ import: OPTION_TRAIT, isTrait: true });
+        requiredImports.push({ import: CONTRACT_ADDRESS });
+        reads.push(readFelt(reads.length));
+        reads.push(
+          `let read${reads.length} =  starknet::contract_address_try_from_felt252(read${
+            reads.length - 1
+          }).unwrap();`,
+        );
+      } else if (elem.dataOrDataType === Read.Felt) {
+        reads.push(readFelt(reads.length));
+      } else if (elem.dataOrDataType === Read.Id) {
         reads.push(readId(reads.length));
-      } else if (elem === Read.Bool) {
-        requiredImports.push(FELT252_INTO_BOOL);
+      } else if (elem.dataOrDataType === Read.Bool) {
+        requiredImports.push({ import: FELT252_INTO_BOOL });
         reads.push(readFelt(reads.length));
         reads.push(`let read${reads.length} = felt252_into_bool(read${reads.length - 1});`);
       } else {
-        return elem;
+        return elem.dataOrDataType;
       }
       return `read${reads.length - 1}`;
     })
@@ -41,58 +62,73 @@ export function serialiseReads(
 enum Read {
   Felt,
   Id,
+  Address,
+  UN,
   Bool,
 }
 
-function producePackExpression(type: CairoType): (string | Read)[] {
-  if (type instanceof WarpLocation) return [Read.Id];
-  if (type instanceof CairoFelt) return [Read.Felt];
-  if (type instanceof CairoBool) return [Read.Bool];
+interface packExpReturnType {
+  dataOrDataType: string | Read;
+  metadata: {
+    nBits?: number;
+  };
+}
+
+function createPackExpReturnType(dataOrDataType: string | Read): packExpReturnType {
+  return { dataOrDataType, metadata: {} };
+}
+
+function producePackExpression(type: CairoType): packExpReturnType[] {
+  if (type instanceof WarpLocation) return [createPackExpReturnType(Read.Id)];
+  if (type instanceof CairoFelt) return [createPackExpReturnType(Read.Felt)];
+  if (type instanceof CairoContractAddress) return [createPackExpReturnType(Read.Address)];
+  if (type instanceof CairoBool) return [createPackExpReturnType(Read.Bool)];
   if (type instanceof CairoStaticArray) {
     return [
-      '(',
+      createPackExpReturnType('('),
       ...Array(type.size)
         .fill([...producePackExpression(type.type), ','])
-        .flat(),
-      ')',
+        .flat()
+        .map(createPackExpReturnType),
+      createPackExpReturnType(')'),
     ];
   }
 
   if (type instanceof CairoUint) {
     if (type.fullStringRepresentation === CairoUint256.fullStringRepresentation) {
       return [
-        type.toString(),
-        '{',
+        createPackExpReturnType(type.toString()),
+        createPackExpReturnType('{'),
         ...[
           ['low', new CairoUint(128)],
           ['high', new CairoUint(128)],
         ]
           .flatMap(([memberName, memberType]) => [
-            memberName as string,
-            ':',
+            createPackExpReturnType(memberName as string),
+            createPackExpReturnType(':'),
             ...producePackExpression(memberType as CairoType),
-            ',',
+            createPackExpReturnType(','),
           ])
           .slice(0, -1),
-        '}',
+        createPackExpReturnType('}'),
       ];
     }
-    return [`core::integer::${type.toString()}_from_felt252(${Read.Felt})`];
+    return [{ dataOrDataType: Read.UN, metadata: { nBits: type.nBits } }];
   }
 
   if (type instanceof CairoStruct) {
     return [
-      type.name,
-      '{',
+      createPackExpReturnType(type.name),
+      createPackExpReturnType('{'),
       ...[...type.members.entries()]
         .flatMap(([memberName, memberType]) => [
-          memberName,
-          ':',
+          createPackExpReturnType(memberName),
+          createPackExpReturnType(':'),
           ...producePackExpression(memberType),
-          ',',
+          createPackExpReturnType(','),
         ])
         .slice(0, -1),
-      '}',
+      createPackExpReturnType('}'),
     ];
   }
 
diff --git a/src/cairoUtilFuncGen/storage/storageRead.ts b/src/cairoUtilFuncGen/storage/storageRead.ts
index 37db58746..5d3858723 100644
--- a/src/cairoUtilFuncGen/storage/storageRead.ts
+++ b/src/cairoUtilFuncGen/storage/storageRead.ts
@@ -67,8 +67,7 @@ export class StorageReadGen extends StringIndexedFuncGen {
     const [reads, pack, requiredImports] = serialiseReads(typeToRead, readFelt, readId);
 
     requiredImports.map((i) => {
-      const funcDef = this.requireImport(...i);
-      if (!functionsCalled.includes(funcDef)) functionsCalled.push(funcDef);
+      functionsCalled.push(this.requireImport(...i.import, [], [], { isTrait: i.isTrait }));
     });
 
     const funcInfo: GeneratedFunctionInfo = {
diff --git a/src/cairoWriter/utils.ts b/src/cairoWriter/utils.ts
index 59e01f342..727757ca5 100644
--- a/src/cairoWriter/utils.ts
+++ b/src/cairoWriter/utils.ts
@@ -19,6 +19,8 @@ export function getInterfaceNameForContract(
   contractName: string,
   nodeInSourceUnit: ASTNode,
   interfaceNameMappings: Map<SourceUnit, Map<string, string>>,
+  isDelegateCall = false,
+  dispatcherSuffix = true,
 ): string {
   const sourceUnit =
     nodeInSourceUnit instanceof SourceUnit
@@ -36,5 +38,7 @@ export function getInterfaceNameForContract(
     `An error occurred during name substitution for the interface ${contractName}`,
   );
 
-  return interfaceName;
+  return (
+    interfaceName + (dispatcherSuffix ? (isDelegateCall ? 'LibraryDispatcher' : 'Dispatcher') : '')
+  );
 }
diff --git a/src/cairoWriter/writers/assignmentWriter.ts b/src/cairoWriter/writers/assignmentWriter.ts
index ea71b4342..45eb985ef 100644
--- a/src/cairoWriter/writers/assignmentWriter.ts
+++ b/src/cairoWriter/writers/assignmentWriter.ts
@@ -7,6 +7,6 @@ export class AssignmentWriter extends CairoASTNodeWriter {
     assert(node.operator === '=', `Unexpected operator ${node.operator}`);
     const [lhs, rhs] = [node.vLeftHandSide, node.vRightHandSide];
     const nodes = [lhs, rhs].map((v) => writer.write(v));
-    return [`let ${nodes[0]} ${node.operator} ${nodes[1]};`];
+    return [`${nodes[0]} ${node.operator} ${nodes[1]}`];
   }
 }
diff --git a/src/cairoWriter/writers/cairoContractWriter.ts b/src/cairoWriter/writers/cairoContractWriter.ts
index 00d72d475..58df6093f 100644
--- a/src/cairoWriter/writers/cairoContractWriter.ts
+++ b/src/cairoWriter/writers/cairoContractWriter.ts
@@ -158,8 +158,11 @@ export class CairoContractWriter extends CairoASTNodeWriter {
 
     const contractHeader = '#[contract] \n' + `mod ${node.name} {`;
 
+    const globalImports = ['use starknet::ContractAddress;'].join('\n');
+
     return [
       [
+        globalImports,
         contractHeader,
         documentation,
         writtenImportFuncs,
@@ -194,9 +197,10 @@ export class CairoContractWriter extends CairoASTNodeWriter {
         // remove all content between any two pairing curly braces
         .replace(/\{[^]*\}/g, '')
         .split('\n');
-      const funcLineIndex = resultLines.findIndex((line) => line.startsWith('func'));
+      const funcLineIndex = resultLines.findIndex((line) => line.startsWith('fn'));
       resultLines.splice(0, funcLineIndex);
-      return resultLines.join('\n') + '{\n}';
+      resultLines[0] = '#[external] ' + resultLines[0];
+      return resultLines.join('\n') + ';';
     });
     // Handle the workaround of genContractInterface function of externalContractInterfaceInserter.ts
     // Remove `@interface` to get the actual contract interface name
@@ -206,14 +210,17 @@ export class CairoContractWriter extends CairoASTNodeWriter {
           node.name.replace(TEMP_INTERFACE_SUFFIX, ''),
           node,
           interfaceNameMappings,
+          false,
+          false,
         )
       : node.name;
 
     return [
-      [
-        documentation,
-        [`@contract_interface`, `namespace ${interfaceName}{`, ...functions, `}`].join('\n'),
-      ].join('\n'),
+      endent`#[abi]
+        ${documentation}
+        trait ${interfaceName}{
+          ${functions.join('\n')}
+          }`,
     ];
   }
 }
diff --git a/src/cairoWriter/writers/functionCallWriter.ts b/src/cairoWriter/writers/functionCallWriter.ts
index bede99b59..8082c6d91 100644
--- a/src/cairoWriter/writers/functionCallWriter.ts
+++ b/src/cairoWriter/writers/functionCallWriter.ts
@@ -21,6 +21,7 @@ import {
 import { CairoASTNodeWriter } from '../base';
 import { getDocumentation, getInterfaceNameForContract } from '../utils';
 import { interfaceNameMappings } from './sourceUnitWriter';
+import endent from 'endent';
 
 export class FunctionCallWriter extends CairoASTNodeWriter {
   writeInner(node: FunctionCall, writer: ASTWriter): SrcDesc {
@@ -50,15 +51,13 @@ export class FunctionCallWriter extends CairoASTNodeWriter {
               isDelegateCall = true;
               firstArg = classHashTextLets[1];
             }
-            return [
-              `${getInterfaceNameForContract(
-                nodeType.definition.name,
-                node,
-                interfaceNameMappings,
-              )}.${(isDelegateCall ? 'library_call_' : '') + memberName}(${firstArg}${
-                args ? ', ' : ''
-              }${args})`,
-            ];
+            const interfaceName = getInterfaceNameForContract(
+              nodeType.definition.name,
+              node,
+              interfaceNameMappings,
+              isDelegateCall,
+            );
+            return [endent`${interfaceName}{contract_address: ${firstArg}}.${memberName}(${args})`];
           }
         } else if (
           node.vReferencedDeclaration instanceof CairoGeneratedFunctionDefinition &&
diff --git a/src/cairoWriter/writers/variableDeclarationStatementWriter.ts b/src/cairoWriter/writers/variableDeclarationStatementWriter.ts
index 596f6f69d..d3a08327f 100644
--- a/src/cairoWriter/writers/variableDeclarationStatementWriter.ts
+++ b/src/cairoWriter/writers/variableDeclarationStatementWriter.ts
@@ -15,6 +15,7 @@ import { isDynamicArray, safeGetNodeType } from '../../utils/nodeTypeProcessing'
 import { isExternalCall } from '../../utils/utils';
 import { CairoASTNodeWriter } from '../base';
 import { getDocumentation } from '../utils';
+import endent from 'endent';
 
 export class VariableDeclarationStatementWriter extends CairoASTNodeWriter {
   gapVarCounter = 0;
@@ -69,14 +70,15 @@ export class VariableDeclarationStatementWriter extends CairoASTNodeWriter {
     });
     if (declarations.length > 1) {
       return [
-        [
-          documentation,
-          `let (${declarations.join(', ')}) = ${writer.write(node.vInitialValue)};`,
-        ].join('\n'),
+        endent`${documentation}
+          let (${declarations.map((decl) => `mut ${decl}`).join(', ')}) = ${writer.write(
+          node.vInitialValue,
+        )};`,
       ];
     }
     return [
-      [documentation, `let ${declarations[0]} = ${writer.write(node.vInitialValue)};`].join('\n'),
+      endent`${documentation}
+      let mut ${declarations[0]} = ${writer.write(node.vInitialValue)};`,
     ];
   }
 }
diff --git a/src/passes/builtinHandler/thisKeyword.ts b/src/passes/builtinHandler/thisKeyword.ts
index 7b3e1f39d..5cb1e9dc3 100644
--- a/src/passes/builtinHandler/thisKeyword.ts
+++ b/src/passes/builtinHandler/thisKeyword.ts
@@ -52,7 +52,7 @@ export class ThisKeyword extends ASTMapper {
           (contract) => contract.name === getTemporaryInterfaceName(currentContract.name),
         );
         if (contractIndex === -1) {
-          const insertedInterface = genContractInterface(currentContract, sourceUnit, ast);
+          const insertedInterface = genContractInterface(node, currentContract, sourceUnit, ast);
           replaceInterfaceWithCairoContract(insertedInterface, ast);
         }
       }
diff --git a/src/passes/externalContractHandler/externalContractInterfaceInserter.ts b/src/passes/externalContractHandler/externalContractInterfaceInserter.ts
index 3c6e3067f..243652e06 100644
--- a/src/passes/externalContractHandler/externalContractInterfaceInserter.ts
+++ b/src/passes/externalContractHandler/externalContractInterfaceInserter.ts
@@ -1,5 +1,6 @@
 import assert = require('assert');
 import {
+  ASTNode,
   ContractDefinition,
   ContractKind,
   FunctionKind,
@@ -18,7 +19,9 @@ import { TEMP_INTERFACE_SUFFIX } from '../../export';
 import { cloneASTNode } from '../../utils/cloning';
 import { TranspileFailedError } from '../../utils/errors';
 import { safeGetNodeType } from '../../utils/nodeTypeProcessing';
-import { isExternallyVisible } from '../../utils/utils';
+import { getContainingSourceUnit, isExternallyVisible } from '../../utils/utils';
+import { createImport } from '../../utils/importFuncGenerator';
+import { SUPER } from '../../utils/importPaths';
 
 export class ExternalContractInterfaceInserter extends ASTMapper {
   /*
@@ -38,6 +41,7 @@ export class ExternalContractInterfaceInserter extends ASTMapper {
 
     if (declaration instanceof ContractDefinition) {
       importExternalContract(
+        node,
         declaration,
         node.getClosestParentByType(SourceUnit),
         this.contractInterfaces,
@@ -55,6 +59,7 @@ export class ExternalContractInterfaceInserter extends ASTMapper {
       if (node.context === undefined)
         throw new TranspileFailedError('Node context should not be undefined');
       importExternalContract(
+        node,
         node.context.locate(nodeType.definition.id) as ContractDefinition,
         node.getClosestParentByType(SourceUnit),
         this.contractInterfaces,
@@ -74,6 +79,7 @@ export class ExternalContractInterfaceInserter extends ASTMapper {
       varType.vReferencedDeclaration instanceof ContractDefinition
     ) {
       importExternalContract(
+        node,
         varType.vReferencedDeclaration,
         node.getClosestParentByType(SourceUnit),
         this.contractInterfaces,
@@ -84,6 +90,7 @@ export class ExternalContractInterfaceInserter extends ASTMapper {
 }
 
 function importExternalContract(
+  node: ASTNode,
   contract: ContractDefinition,
   sourceUnit: SourceUnit | undefined,
   contractInterfaces: Map<number, ContractDefinition>,
@@ -93,7 +100,7 @@ function importExternalContract(
 
   if (contract.kind === ContractKind.Library) return;
   if (contractInterfaces.has(contract.id)) return;
-  contractInterfaces.set(contract.id, genContractInterface(contract, sourceUnit, ast));
+  contractInterfaces.set(contract.id, genContractInterface(node, contract, sourceUnit, ast));
 }
 
 export function getTemporaryInterfaceName(contractName: string): string {
@@ -101,6 +108,7 @@ export function getTemporaryInterfaceName(contractName: string): string {
 }
 
 export function genContractInterface(
+  node: ASTNode,
   contract: ContractDefinition,
   sourceUnit: SourceUnit,
   ast: AST,
@@ -138,5 +146,38 @@ export function genContractInterface(
     });
   sourceUnit.appendChild(contractInterface);
   ast.registerChild(contractInterface, sourceUnit);
+  createImport(
+    SUPER,
+    `${contract.name}_warped_interfaceDispatcherTrait`,
+    getContainingSourceUnit(node),
+    ast,
+    [],
+    [],
+    {
+      isTrait: true,
+    },
+  );
+  createImport(
+    SUPER,
+    `${contract.name}_warped_interfaceDispatcher`,
+    getContainingSourceUnit(node),
+    ast,
+    [],
+    [],
+    {
+      isTrait: true,
+    },
+  );
+  createImport(
+    SUPER,
+    `${contract.name}_warped_interfaceLibraryDispatcher`,
+    getContainingSourceUnit(node),
+    ast,
+    [],
+    [],
+    {
+      isTrait: true,
+    },
+  );
   return contractInterface;
 }
diff --git a/src/passes/functionPruner/functionRemover.ts b/src/passes/functionPruner/functionRemover.ts
index 739d9a82c..f6512e617 100644
--- a/src/passes/functionPruner/functionRemover.ts
+++ b/src/passes/functionPruner/functionRemover.ts
@@ -4,6 +4,7 @@ import { AST } from '../../ast/ast';
 import { ASTMapper } from '../../ast/mapper';
 import { printNode } from '../../utils/astPrinter';
 import { isExternallyVisible } from '../../utils/utils';
+import { CairoImportFunctionDefinition, FunctionStubKind } from '../../ast/cairoNodes';
 
 export class FunctionRemover extends ASTMapper {
   private functionGraph: Map<number, FunctionDefinition[]>;
@@ -15,14 +16,23 @@ export class FunctionRemover extends ASTMapper {
     this.reachableFunctions = new Set();
   }
 
+  importFuncFilter = (funcs: readonly FunctionDefinition[]) =>
+    funcs
+      .filter((func) => !this.reachableFunctions.has(func.id))
+      .filter(
+        (func) =>
+          !(
+            func instanceof CairoImportFunctionDefinition &&
+            func.functionStubKind === FunctionStubKind.TraitStructDefStub
+          ),
+      );
+
   visitSourceUnit(node: SourceUnit, ast: AST): void {
     node.vFunctions.filter((func) => isExternallyVisible(func)).forEach((func) => this.dfs(func));
 
     node.vContracts.forEach((c) => this.visitContractDefinition(c, ast));
 
-    node.vFunctions
-      .filter((func) => !this.reachableFunctions.has(func.id))
-      .forEach((func) => node.removeChild(func));
+    this.importFuncFilter(node.vFunctions).forEach((func) => node.removeChild(func));
   }
 
   visitContractDefinition(node: ContractDefinition, _ast: AST) {
@@ -30,9 +40,7 @@ export class FunctionRemover extends ASTMapper {
     node.vFunctions.filter((func) => isExternallyVisible(func)).forEach((func) => this.dfs(func));
 
     // Remove unreachable functions
-    node.vFunctions
-      .filter((func) => !this.reachableFunctions.has(func.id))
-      .forEach((func) => node.removeChild(func));
+    this.importFuncFilter(node.vFunctions).forEach((func) => node.removeChild(func));
   }
 
   dfs(f: FunctionDefinition): void {
diff --git a/src/utils/cairoTypeSystem.ts b/src/utils/cairoTypeSystem.ts
index c79bb694f..ba2bdfb8a 100644
--- a/src/utils/cairoTypeSystem.ts
+++ b/src/utils/cairoTypeSystem.ts
@@ -137,7 +137,7 @@ export abstract class CairoType {
           );
         }
       } else if (tp.definition instanceof ContractDefinition) {
-        return new CairoFelt();
+        return new CairoContractAddress();
       } else if (tp.definition instanceof UserDefinedValueTypeDefinition) {
         return CairoType.fromSol(
           safeGetNodeType(tp.definition.underlyingType, ast.inference),
diff --git a/src/utils/functionGeneration.ts b/src/utils/functionGeneration.ts
index 9147d84ad..8ec1f1637 100644
--- a/src/utils/functionGeneration.ts
+++ b/src/utils/functionGeneration.ts
@@ -140,7 +140,7 @@ export function createCairoImportFunctionDefinition(
     implicits,
     createParameterList(createParameters(params, id, ast), ast),
     createParameterList(createParameters(retParams, id, ast), ast),
-    FunctionStubKind.FunctionDefStub,
+    options.stubKind ?? FunctionStubKind.FunctionDefStub,
     options.acceptsRawDArray,
     options.acceptsUnpackedStructArray,
   );
@@ -154,6 +154,9 @@ export function createCairoImportStructDefinition(
   path: string[],
   ast: AST,
   nodeInSourceUnit: ASTNode,
+  options: CairoFunctionStubOptions = {
+    stubKind: FunctionStubKind.StructDefStub,
+  },
 ): CairoImportFunctionDefinition {
   const sourceUnit = ast.getContainingRoot(nodeInSourceUnit);
 
@@ -172,7 +175,7 @@ export function createCairoImportStructDefinition(
     implicits,
     params,
     retParams,
-    FunctionStubKind.StructDefStub,
+    options.stubKind ?? FunctionStubKind.StructDefStub,
   );
   ast.setContextRecursive(funcDef);
   sourceUnit.insertAtBeginning(funcDef);
diff --git a/src/utils/importFuncGenerator.ts b/src/utils/importFuncGenerator.ts
index e7a273eeb..f72e5b6ac 100644
--- a/src/utils/importFuncGenerator.ts
+++ b/src/utils/importFuncGenerator.ts
@@ -1,5 +1,5 @@
 import { ASTNode, SourceUnit } from 'solc-typed-ast';
-import { CairoImportFunctionDefinition } from '../ast/cairoNodes';
+import { CairoImportFunctionDefinition, FunctionStubKind } from '../ast/cairoNodes';
 import { AST } from '../ast/ast';
 import { TranspileFailedError } from '../utils/errors';
 import { warplibImportInfo } from '../warplib/gatherWarplibImports';
@@ -19,7 +19,7 @@ export function createImport(
   ast: AST,
   inputs?: ParameterInfo[],
   outputs?: ParameterInfo[],
-  options?: { acceptsRawDarray?: boolean; acceptsUnpackedStructArray?: boolean },
+  options?: { acceptsRawDarray?: boolean; acceptsUnpackedStructArray?: boolean; isTrait?: boolean },
 ) {
   const sourceUnit = getContainingSourceUnit(nodeInSourceUnit);
 
@@ -51,13 +51,22 @@ export function createImport(
       sourceUnit,
       options,
     );
-  const createStructImport = () => createCairoImportStructDefinition(name, path, ast, sourceUnit);
+  const createStructImport = () =>
+    createCairoImportStructDefinition(name, path, ast, sourceUnit, {
+      stubKind: options?.isTrait
+        ? FunctionStubKind.TraitStructDefStub
+        : FunctionStubKind.StructDefStub,
+    });
 
   const warplibFunc = warplibImportInfo.get(encodePath(path))?.get(name);
   if (warplibFunc !== undefined) {
     return createFuncImport(...warplibFunc);
   }
 
+  if (options?.isTrait) {
+    return createStructImport();
+  }
+
   const pathForStructImport = [
     Paths.U8_FROM_FELT,
     Paths.U16_FROM_FELT,
@@ -100,6 +109,7 @@ export function createImport(
     Paths.EMIT_EVENT,
     Paths.GET_CALLER_ADDRESS,
     Paths.GET_CONTRACT_ADDRESS,
+    Paths.CONTRACT_ADDRESS_FROM_FELT,
     Paths.INTO,
     Paths.CONTRACT_ADDRESS,
     Paths.U8_TO_FELT,
diff --git a/src/utils/importPaths.ts b/src/utils/importPaths.ts
index d6899be4c..b6d01f10d 100644
--- a/src/utils/importPaths.ts
+++ b/src/utils/importPaths.ts
@@ -228,7 +228,17 @@ export const U248_TO_FELT: [string[], string] = [['integer'], 'u248_to_felt252']
 
 export const GET_CALLER_ADDRESS: [string[], string] = [['starknet'], 'get_caller_address'];
 export const CONTRACT_ADDRESS: [string[], string] = [['starknet'], 'ContractAddress'];
+
+export const CONTRACT_ADDRESS_FROM_FELT: [string[], string] = [
+  ['starknet'],
+  'contract_address_try_from_felt252',
+];
+
 export const INTO: [string[], string] = [['traits'], 'Into'];
 
+export const OPTION_TRAIT: [string[], string] = [['option'], 'OptionTrait'];
+
 export const WARP_MEMORY: [string[], string] = [['warplib', 'warp_memory'], 'WarpMemory'];
 export const MEMORY_TRAIT: [string[], string] = [['warplib', 'warp_memory'], 'MemoryTrait'];
+
+export const SUPER: string[] = ['super'];


Test patch:
diff --git a/tests/compilation/passingContracts.ts b/tests/compilation/passingContracts.ts
index 0767240d8..c8fd8e28f 100644
--- a/tests/compilation/passingContracts.ts
+++ b/tests/compilation/passingContracts.ts
@@ -41,7 +41,7 @@ export const passingContracts = [
   // 'tests/compilation/contracts/expressionSplitter/assignSimple.sol',
   // 'tests/compilation/contracts/expressionSplitter/funcCallSimple.sol',
   // 'tests/compilation/contracts/expressionSplitter/tupleAssign.sol',
-  // 'tests/compilation/contracts/externalFunction.sol',
+  'tests/compilation/contracts/externalFunction.sol',
   // 'tests/compilation/contracts/fallbackWithArgs.sol',
   // 'tests/compilation/contracts/fallbackWithoutArgs.sol',
   'tests/compilation/contracts/fileWithMinusSignIncluded-.sol',