Use dubhe to develop a contract

Let’s develop one of the simplest examples, called Distributed Messaging, or dms for short. The contract will have a schema called message, which will have a field called content, which will store the message content. The contract will have a method called send, which will take a string as a parameter and store it in the content field of the mailbox schema. The contract will also have an event called MessageSent, which will be triggered when a message is sent.

Dubhe generates some code based on the dubhe.config.ts configuration, so we just need to focus on this file.

import { DubheConfig } from '@0xobelisk/sui-common';
export const dubheConfig = {
	name: 'template',
	description: 'template',
	schemas: {},
} as DubheConfig;

Let’s go over what’s in dubheConfig

  • name: The name of the contract project.
  • description: The description of the contract project.
  • schemas: The schema of the contract project. Refer to the Schemas section for details of schema.

To complete dms contract I need a Schema to store the numbers, now let’s complete the Schema.

dubhe.config.ts
import { DubheConfig } from '@0xobelisk/sui-common';
export const dubheConfig = {
    name: 'dms',
    description: 'Distributed Messaging',
    schemas: {
        message: {
            content: 'StorageValue<String>'
        }
    },
} as DubheConfig;

The name of this Schema is message, and he has only one number field with storage type StorageValue, which is used to store String.

Now we need to generate the contract frame content using Dubhe’s most powerful feature.

➜  dubhe-template-project pnpm dubhe schemagen
 
πŸš€ Starting Schema Generation Process...
πŸ“‹ Project Configuration:
  β”œβ”€ Name: dms
  β”œβ”€ Description: Distributed Messaging
  β”œβ”€ Network: testnet
 
πŸ“„ Starting Move.toml Generation...
  └─ Output path: /Volumes/project/dubhe-template-project/contracts/dms/Move.toml
βœ… Move.toml Generation Complete
 
 
πŸ“ Starting Deploy Hook Generation...
  └─ Output path: /Volumes/project/dubhe-template-project/contracts/dms/sources/scripts/deploy_hook.move
βœ… Deploy Hook Generation Complete
 
 
πŸ”¨ Starting Schema Structure Generation...
  β”œβ”€ Generating schema: message
     β”œβ”€ Output path: /Volumes/project/dubhe-template-project/contracts/dms/sources/codegen/schemas/message.move
     └─ Structure fields: 1
βœ… Schema Structure Generation Complete
 
 
πŸ“ Starting Init Generation...
  └─ Output path: /Volumes/project/dubhe-template-project/contracts/dms/sources/tests/init.move
βœ… Deploy Hook Generation Complete
 
 
βš™οΈ Starting System Generation...
  β”œβ”€ Generating systems
     └─ Output path: /Volumes/project/dubhe-template-project/contracts/dms/sources/systems
βœ… System Generation Complete
 
βœ… Schema Generation Process Complete!

Take a look at the file structure of dms, we don’t need to concern ourselves with anything else but our business code systems folder.

➜  dms tree
β”œβ”€β”€ Move.toml
└── sources
   β”œβ”€β”€ codegen
   β”‚   └── schemas
   β”‚         β”œβ”€β”€ default
   β”‚         β”‚   └── dapp
   β”‚         β”‚     β”œβ”€β”€ metadata.move
   β”‚         β”‚     β”œβ”€β”€ schema.move
   β”‚         β”‚     └── system.move
   β”‚         └── message.move
   β”œβ”€β”€ scripts
   β”‚         β”œβ”€β”€ deploy_hook.move
   β”‚         └── migrate.move
   β”œβ”€β”€ systems
   └── tests
       └── init.move
 
9 directories, 8 files

A Message Schema has been generated for you, now you need to write your own business code! We create a new move file called message in the Systems folder.

touch contracts/counter/sources/systems/message.move

This can be easily achieved with the help of Dubhe’s code generation. The contents of the file:

message.move
module dms::message_system {
    use std::ascii::String;
    use dms::message_schema::Message;

    public entry fun send(message: &mut Message, content: String) {
        message.content().set(content);
    }
}

Let’s add an error handler so that the length of the content doesn’t exceed 12, how to do error handling?

dubhe.config.ts
import { DubheConfig } from '@0xobelisk/sui-common';
export const dubheConfig = {
    name: 'dms',
    description: 'Distributed Messaging',
    schemas: {
        message: {
            content: 'StorageValue<String>'
        }
    },
    errors: {
        InvalidContentLength: "Content length must be less than 12"
    }
} as DubheConfig;

The error handling code has been added, now we also want to add events to the contract to tell us who sent what message.

dubhe.config.ts
import { DubheConfig } from '@0xobelisk/sui-common';
export const dubheConfig = {
	name: 'dms',
	description: 'Distributed Messaging',
	schemas: {
		message: {
			content: 'StorageValue<String>'
		}
	},
	errors: {
		InvalidContentLength: "Content length must be less than 12"
	},
	events: {
		MessageSent: {
            sender: "address",
            content: "String"
		}
	}
} as DubheConfig;

Execute the schemagen command here.

➜  dubhe-template-project pnpm dubhe schemagen
 
πŸš€ Starting Schema Generation Process...
πŸ“‹ Project Configuration:
  β”œβ”€ Name: dms
  β”œβ”€ Description: Distributed Messaging
  β”œβ”€ Network: testnet
 
πŸ“ Starting Deploy Hook Generation...
  └─ Output path: /Volumes/project/dubhe-template-project/contracts/dms/sources/scripts/deploy_hook.move
βœ… Deploy Hook Generation Complete
 
 
πŸ“¦ Starting Schema Event Generation...
     └─ Generating MessageSent event: [object Object]
βœ… Schema Event Generation Complete
 
 
πŸ”¨ Starting Schema Structure Generation...
  β”œβ”€ Generating schema: message
     β”œβ”€ Output path: /Volumes/project/dubhe-template-project/contracts/dms/sources/codegen/schemas/message.move
     └─ Structure fields: 1
βœ… Schema Structure Generation Complete
 
 
πŸ“¦ Starting Schema Error Generation...
     └─ Generating InvalidContentLength message: Content length must be less than 12
βœ… Schema Error Generation Complete
 
 
πŸ“ Starting Init Generation...
  └─ Output path: /Volumes/project/dubhe-template-project/contracts/dms/sources/tests/init.move
βœ… Deploy Hook Generation Complete
 
 
βš™οΈ Starting System Generation...
  β”œβ”€ Generating systems
     └─ Output path: /Volumes/project/dubhe-template-project/contracts/dms/sources/systems
βœ… System Generation Complete
 
βœ… Schema Generation Process Complete!

Errors and events are already generated.

First we do an error check, the input content must be greater than 0.

message.move
module dms::message_system {
    use std::ascii::String;
    use dms::message_schema::Message;
    use dms::invalid_content_length_error;

    public entry fun send(message: &mut Message, content: String) {
        invalid_content_length_error::require(content.length() < 12);
        message.content().set(content);
    }
}

Finally we add events to the contract.

message.move
module dms::message_system {
    use std::ascii::String;
    use dms::message_schema::Message;
    use dms::invalid_content_length_error;
    use dms::message_sent_event;

    public entry fun send(message: &mut Message, content: String, ctx: &mut TxContext) {
        invalid_content_length_error::require(content.length() < 12);
        message.content().set(content);
        message_sent_event::emit(ctx.sender(), content);
    }
}

By this point we need to write the contents of the deploy_hook, which will be automatically executed for you when the contract is deployed, now we initialise the content in the store to Hello World!.

dubhe-template-project/contracts/counter/sources/scripts/deploy_hook.move
deploy_hook.move
#[allow(lint(share_owned), unused_let_mut)]
module dms::deploy_hook {
  use std::ascii::string;
  use sui::clock::Clock;
  use dms::dapp_system;
  use dms::message_schema::Message;

  public entry fun run(clock: &Clock, ctx: &mut TxContext) {
    // Create a dapp.
    let mut dapp = dapp_system::create(string(b"dms"),string(b"Distributed Messaging"), clock , ctx);
    // Create schemas
    let mut message = dms::message_schema::create(ctx);
    // Logic that needs to be automated once the contract is deployed
    {
      message.content().set(string(b"Hello, World!"));
    };
    // Authorize schemas and public share objects
    dapp.add_schema<Message>(message, ctx);
    sui::transfer::public_share_object(dapp);
  }
}

Let’s run the Build command to check for syntax errors.

➜  dubhe-template-project pnpm dubhe build --network localnet
πŸš€ Running move build
 
INCLUDING DEPENDENCY Dubhe
INCLUDING DEPENDENCY Sui
INCLUDING DEPENDENCY MoveStdlib
BUILDING dms
Total number of linter warnings suppressed: 1 (unique lints: 1)