How to generate .did. files for an actor class?

In example/ directory I have dfx.json:

{
  "canisters": {
    "example_backend": {
      "main": "src/example_backend/main.mo",
      "type": "motoko"
    },
    "example_frontend": {
      "dependencies": [
        "example_backend"
      ],
      "frontend": {
        "entrypoint": "src/example_frontend/src/index.html"
      },
      "source": [
        "src/example_frontend/assets",
        "dist/example_frontend/"
      ],
      "type": "assets"
    }
  },
  "defaults": {
    "build": {
      "args": "",
      "packtool": "vessel sources"
    }
  },
  "output_env_file": ".env",
  "version": 1
}

It generates .did. files for example_backend and example_frontend, but does not generate .did. files for these canisters that are created by example_backend from actor classes. How to generate these .did. files?

1 Like

I would guess you have to make the Motoko compiler do it for you. If you put the canister that your backend is deploying in a dfx project and dfx build it, then you’ll end up with the .did files. If you don’t want to go that route I guess you have to build the moc invocation yourself. You can find moc in the folder that dfx cache show points to, and IIRC if you build with verbose mode (-vv) you’ll get the moc invocations printed

1 Like

Is it possible to put the canister that your backend is deploying in a dfx project, but dfx deploy not to try to deploy that canister?

If there is no such feature, then it is a good proposal to add a boolean argument like dontDeploy to prevent dfx deploy to deploy the canister.

You can declare it as remote for the networks that you don’t want to deploy it on

How to declare it as remote?

What does it mean, remote?

Here’s an example of a remote canister. The intention behind the feature is that you can say that on certain networks you don’t want to deploy that canister. In this example, you only want to install the ledger if you’re running locally since there is no pre-installed ledger. On mainnet, you of course want to use the real ledger, so you don’t deploy your own

I tried to update declaration/index/index.did* files by the following command:

`dfx cache show`/moc `vessel sources` --idl -c src/index/main.mo

declaration/index/index.did* however were not updated. What is my error?

dfx generate ???

You seem to read my question not attentively.

I am asking for .did. files for an actor class, not for a canister.

sorry, not into motoko, i thought actor is treated like a single canister.

Actor is indeed treated as a single canister, but I ask for actor classes, not actors.

You recommend to use a remote canister for .did. generation. But an example of remote canister, that you pointed, refers to an already generated .did. file rather than generates it:

{
  "ledger": {
    "type": "custom",
    "candid": "https://raw.githubusercontent.com/dfinity/ic/a17247bd86c7aa4e87742bf74d108614580f216d/rs/rosetta-api/icrc1/ledger/ledger.did",
    "wasm": "https://download.dfinity.systems/ic/a17247bd86c7aa4e87742bf74d108614580f216d/canisters/ic-icrc1-ledger.wasm.gz",
    "remote": {
      "id": {
        "ic": "ryjl3-tyaaa-aaaaa-aaaba-cai"
      }
    }
  },
  "defaults":{
    "replica": {
      "subnet_type":"system"
    }
  }
}

Are you sure dfx does not build the .did files? For me dfx builds the .did file also for an actor class.

Not sure which commands you have tried. Maybe dfx generate? That command creates the .did file for me. With your dfx.json it would be in src/declarations/backend_example/backend_example.did. The only difference with an actor class is that there are service arguments, i.e. in the line
service : (...) -> the part (...) is non-empty. I would be interested in why it doesn’t do that for you.

Anyway, if dfx generate doesn’t work then another way is to use dfx build. That command does not deploy the canister. But it does create a canister id (an empty canister), i.e. it still needs a replica (dfx start) running in the background. So what you are looking for is dfx build --check. With the --check option it does not even create a canister id, hence you don’t need a replica running in the background. It builds .did and .wasm in .dfx/local/canisters/example_backend/. You can run dfx build --check example_backend to only build that one canister and not all that are defined in dfx.json.

Make it a canister of type Motoko. There you don’t need to specify a .did because moc can generate it for you

Im also confused by this

I have a few actor classes that are created on demand from a canister. So they are not in my dfx config due to them being actor classes, so there could be 0 or a 1000. From what i can tell the dfx.json just takes canisters, not dynamically created actor classes.
My biggest struggle is I cant automatically create JS/TS for the services because no .did file is created, because there is no ‘canister’ that dfx deploys. Is there a way to add it to the dfx.json so it will generate .did/JS/TS?

In Makefile:

.PHONY: CanDBPartition.wasm
CanDBPartition.wasm: do-build-backend
	. .env && moc `mops sources` --actor-idl $$PWD/.dfx/local/lsp --actor-alias ic_eth $$CANISTER_ID_ic_eth src/storage/CanDBPartition.mo

I think thats a step in the right direction

  • Use moc to build the wasm/.did files
  • Then use didc to convert the .did file to a js/ts file

Is there really no better way of doing this?

Is there a easier way to access didc than just downloading, like through dfx?

Here is the makefile i ended up making, if others need help with this
Its not generic, but can be a good starting point

# Define variables
SRC_DIR := src/backend
DID_TEMP_DIR := did_temp
OUT_DIR := src/frontend/src/ic-agent/declarations
MOC := $(shell dfx cache show)/moc
MOPS_SOURCES := $(shell mops sources)
DIDC := didc

# List of .mo files
ACTOR_CLASS_MO_FILES := stadium/StadiumActor.mo \
            team/TeamActor.mo \

build_league:
	dfx build league

process_did_files:
	@mkdir -p $(DID_TEMP_DIR)
	@for dir in .dfx/local/canisters/*; do \
		if [ -d "$$dir" ]; then \
			base=$$(basename $$dir); \
			did_file="$$dir/$$base.did"; \
			if [ -f "$$did_file" ]; then \
				echo "Processing $$did_file..."; \
				cp "$$did_file" "$(DID_TEMP_DIR)/"; \
			fi; \
		fi; \
	done

compile_actor_class_mo_files:
	@for mo in $(ACTOR_CLASS_MO_FILES); do \
		full_mo_path="$(SRC_DIR)/$$mo"; \
		ts="$$(echo "$$full_mo_path" | sed 's|^$(SRC_DIR)|$(OUT_DIR)|' | sed 's|\.mo$$|.ts|')"; \
		BASENAME=$$(basename $$mo .mo); \
		$(MOC) $(MOPS_SOURCES) $$full_mo_path --idl; \
		lowercase_did="$$(echo $$BASENAME | sed 's/Actor$$//' | tr '[:upper:]' '[:lower:]').did"; \
		mv $$BASENAME.did $(DID_TEMP_DIR)/$$lowercase_did; \
		rm -f $$BASENAME.wasm; \
	done


compile_did_files:
	@for did in $(DID_TEMP_DIR)/*.did; do \
		if [ -f "$$did" ]; then \
			js="$$(echo "$$did" | sed 's|^$(DID_TEMP_DIR)|$(OUT_DIR)|' | sed 's|\.did$$|.js|')"; \
			./didc bind -t js "$$did" > "$$js"; \
			ts="$$(echo "$$js" | sed 's|\.js$$|.d.ts|')"; \
			./didc bind -t ts "$$did" > "$$ts"; \
		fi; \
	done
	rm -rf $(DID_TEMP_DIR)

$(DIDC):
	@echo "Downloading didc..."
	@curl -L -o $(DIDC) https://github.com/dfinity/candid/releases/latest/download/didc-linux64
	@chmod +x $(DIDC)
	@echo "didc downloaded and made executable."

# Cleanup
clean:
	rm -rf $(OUT_DIR)
	@rm -f $(DIDC)
	
# Default target
generate: $(DIDC) build_league process_did_files compile_actor_class_mo_files compile_did_files

# Phony targets
.PHONY: generate clean build_league process_did_files compile_actor_class_mo_files compile_did_files