Skip to content

Latest commit

 

History

History
438 lines (348 loc) · 14.4 KB

README.md

File metadata and controls

438 lines (348 loc) · 14.4 KB

AptoORM

AptoORM is a utility for Object–relational mapping from Typescript and Javascript class objects to Aptos Object model.

Once you define a class in your source code with AptoORM decorators, you can record the class objects to Aptos Blockchain. It is also supported to update, delete them on chain and to transfer the ownership of the objects to others as like other digital assets.

Furthermore, AptoORM can be a framework to create, mint NFT on Aptos Blockchain with simple and clear settings.

Features

  • Add OrmObject to OrmClass decorator.
  • Add OrmToken to OrmTokenClass decorator.
  • Add ORM Token generation to gen-move.ts.
  • Remove OrmToken's name, description and uri from user data.
  • Add OrmToken's name, description and uri fields update procedure with token_mutable_by_creator and token_mutable_by_owner options.
  • Rename OrmCollection to OrmTokenClass.
  • Add update and delete operations for OrmToken.
  • Add an option, immutable, for the name, uri and description fields of OrmToken.
  • Updated get function generated by user-defined class.
  • Add Aptos Collection object name option to OrmTokenConfig.
  • Add constant option to OrmTokenConfig for fixed name, uri and description fields of OrmToken. The OrmToken has the same name, uri and descriptions if it is configured.
  • Add index_fields option to OrmClass and OrmField in order to Aptos create named object.
  • Add package configuration
  • Add default royalty_payee to class object.
  • Add OrmFreePrepayClient, OrmFreePostpayClient for free transaction fee interface.
  • Add PoA typescript API
  • Add create_to (create and send an object) API
  • Add royalty_present_per_token to represent the royalty to each token.
  • Add UpdateDateField(), CreateDateField().
  • Add new OrmField option (token_property) to add/delete the field from/to token's property_map.
  • Add txn result parser to get the object or token created.
  • Add Indexer function to list the created objects.
  • Check need to add the move ability [key, store, drop, copy] should be controlled by the AptoORM.
  • Add CLIs for example.

Installation

Install the npm package:

npm install apto_orm --save

You need to install reflect-metadata:

npm install reflect-metadata --save

and import it somewhere in the global place of your app (for example in app.ts):

import "reflect-metadata"

You may need to install node typings:

npm install @types/node ts-node --save-dev

ts-node is required to execute the apto_orm cli.

TypeScript configuration

Also, make sure you are using TypeScript version 4.5 or higher, and you have enabled the following settings in tsconfig.json:

"emitDecoratorMetadata": true,
"experimentalDecorators": true,

Getting Started

You can start a project with apto_orm from scratch. Try the following.

Initialize your package

# package.json
pnpm init

# add build command to package.json
jq --arg key "build" --arg val "tsc --build" '.scripts[$key]=$val' package.json\
  | jq "." > _package.json && mv _package.json package.json

# add `apto_orm` command to package.json
jq --arg key "orm" --arg val "apto_orm" '.scripts[$key]=$val' package.json \
  | jq "." > _package.json && mv _package.json package.json

# setup tsconfig
{
  echo "{"
  echo '  "compilerOptions": {'
  echo '    "target": "ES2020",'
  echo '    "module": "CommonJS",'
  echo '    "moduleResolution": "node",'
  echo '    "skipLibCheck": true,'
  echo '    "resolveJsonModule": true,'
  echo '    "esModuleInterop": true,'
  echo '    "emitDecoratorMetadata": true,'
  echo '    "experimentalDecorators": true,'
  echo '    "allowSyntheticDefaultImports": true,'
  echo '    "declaration": false,'
  echo '    "outDir": "dist"'
  echo '  },'
  echo '  "include": ["src"],'
  echo '  "exclude": ["node_modules", "**/*.spec.ts"]'
  echo "}"
} > tsconfig.json

# setup .gitignore.
{
  echo 'dist'
  echo 'out'
  echo 'build'
  echo 'node_modules'
  echo '**/build'
  echo '.key'
} > .gitignore

Initialize package account

# Create a keypair to .key directory
mkdir -p .key
aptos key generate --assume-yes --output-file .key/villain

# Initialize Aptos account and receive funds of Aptos Coin 
# if devnet or testnet because your package publishing costs Aptos Coin.
aptos init --network devnet --private-key-file .key/villain --profile villain

Create and modify your class.

Create your sample typescript class by apto_orm init command. This command will create a class file (MyToken.ts) to src/my_tokens.

npx apto_orm init src/my_tokens --key .key/villain --token -c MyToken

Modify the following token class to suit your taste.

import { OrmTokenClass, OrmField, OrmIndexField } from 'apto_orm';

@OrmTokenClass({
  package_address: '0x43ded2bab6265e70d3fe47879e2af7d7826984c1f44e5fe6cb4912678c3cab40',
  package_creator: '0x8953be30cc7c69682a2aa972fefee8d8ba474eb69830349d03cda73f5568e8ff',
  package_name: 'my_tokens',
  collection_name: 'AptoORM MyToken',
  collection_uri: 'https://raw.githubusercontent.com/neoul/apto_orm/main/resource.png',
  collection_description: 'Sample AptoORM Token',
  max_supply: 1000n,
  token_use_property_map: false,
  royalty_present: false,
  royalty_denominator: 100,
  royalty_numerator: 1,
})
export class MyToken {
  @OrmIndexField({ immutable: true })
  id!: number;

  @OrmField({ immutable: true })
  name!: string;

  @OrmField()
  uri!: string;

  @OrmField()
  description!: string;
}

Generate your Move module and publish it to onchain

The following command generates a Move module based on your class to YOUR_PACKAGE_PATH/move.

npx apto_orm generate src/my_tokens
# classes 'MyToken' are created to the package 'my_tokens'
#  - creator: 0x8953be30cc7c69682a2aa972fefee8d8ba474eb69830349d03cda73f5568e8ff
#  - address: 0x43ded2bab6265e70d3fe47879e2af7d7826984c1f44e5fe6cb4912678c3cab40
#  - path: /home/willing/projects/apto_example/src/my_tokens

The generated module must be compiled and published to your package account.
This package account is an object that has the signer capability of your package and is a place where your package bytecodes are stored.

npx apto_orm compile src/my_tokens
npx apto_orm publish src/my_tokens --key .key/villain

Please check for information about Aptos Move language.

create, update and delete your apto_orm objects by apto_orm cli

npx apto_orm create src/my_tokens --key .key/villain -c MyToken \
  -d '{ "uri": "https://raw.githubusercontent.com/neoul/apto_orm/main/resource.png", "description": "ok", "id": 1, "name": "ABC" }'
npx apto_orm create src/my_tokens --key .key/villain \
  --to 0xfd2984f201abdbf30ccd0ec5c2f2357789222c0bbd3c68999acfebe188fdc09d -c MyToken \
  -d '{ "uri": "https://raw.githubusercontent.com/neoul/apto_orm/main/resource.png", "description": "ok", "id": 2, "name": "EFG" }'
npx apto_orm delete src/my_tokens --key .key/villain -c MyToken \
  -a 0x10dd8012f4c83a2e058347d7178d81432fcadf30cc11e623e2c8ba93a0a786df

Decorators to make your AptoORM objects

OrmClass and OrmTokenClass

OrmClass decorates a Typescript class to declare Aptos Object that the Object model allows Move to represent a complex type as a set of resources stored within a single address.

@OrmClass({
  package_creator: '0xabc',
  package_name: 'Move package name',
  deletable_by_owner: true,
  ...
})
export type OrmObjectConfig = {
  /** The creator address of the object and package */
  package_creator: AptosAccount | MaybeHexString;
  /** The package name where the objects belongs to */
  package_name: string;
  package_address?: MaybeHexString; // address of the package
  /** Aptos creates named objects with predictable hash addresses, which are derived
   * from user input and the creator's address. This enables named objects to be indexed
   * and traced based on the user input provided by the creator.
   * AptoORM facilitates the creation of indexable objects by utilizing `index_fields`
   * to organize the fields used in the named object creation.
   * Conversely, if index_fields is not set, OrmObject is created with a random address. */
  index_fields?: string[];
  /** Objects created by the class can be transferred by `object::transfer()`. */
  direct_transfer?: boolean;
  /** The creator (The owner of OrmCreator object) can remove the ORM objects. */
  deletable_by_creator?: boolean;
  /** The owner can remove the ORM objects */
  deletable_by_owner?: boolean;
  /** The creator can transfer the ORM objects by AptoORM facilities. */
  indirect_transfer_by_creator?: boolean;
  /** The owner can transfer the ORM objects by AptoORM facilities. */
  indirect_transfer_by_owner?: boolean;
  /** The creator can extend the ORM objects. */
  extensible_by_creator?: boolean;
  /** The owner can extend the ORM objects. */
  extensible_by_owner?: boolean;
  named_addresses?: NamedAddresses;
  /** The token configuration must be set if the OrmObject is Aptos Token object. */
  token_config?: OrmTokenConfig;
};

OrmTokenClass is used to define custom Aptos Token Object. The OrmTokenClass should be defined with the OrmObjectConfig and additional following attributes.

/** ORM Token configuration */
export type OrmTokenConfig = {
  /** The representative name of the token collection (= Aptos Collection Name) */
  collection_name: string;
  /** The representative URI of the token collection (= Aptos Collection URI) */
  collection_uri: string;
  /** The representative description of the token collection (= Aptos Collection Description) */
  collection_description: string;
  /** The maximum token supply of the AptoORM class (= Aptos Collection Max Supply) */
  max_supply: number | bigint;
  /** Whether the token uses property map */
  token_use_property_map: boolean;
  /** Whether the token has royalty or the collection has royalty itself. */
  royalty_present: boolean;
  /** The payee of the royalty */
  royalty_payee: MaybeHexString | string;
  /** The denominator of the royalty */
  royalty_denominator: number;
  /** The numerator of the royalty */
  royalty_numerator: number;
};

OrmField and OrmIndexField

OrmField and OrmIndexField must be used in the fields of the class decorated by OrmClass and OrmTokenClass. Each class fields decorated by the OrmField becomes the field of the resource in an Aptos Object.

POA (Power Of Attorney)

POA Account is an account that can generate and transfer the transaction. The account has the POA (Power of attorney) creates AptoORM Objects instead of the NFT Creator.

Initialize POA

aptos key generate --assume-yes --output-file .key/poa1
aptos init --network devnet --private-key-file .key/poa1 --profile poa1
npx apto_orm poa register -d .key/villain -l .key/poa1
npx apto_orm poa show -l .key/poa1

Mint a token by POA

npx apto_orm create src/my_tokens --key .key/poa1 -c MyToken \
 -d '{ "uri": "https://raw.githubusercontent.com/neoul/apto_orm/main/resource.png", "description": "ok", "id": 100, "name": "ABC" }'

Build AptoORM on your local machine

Test, Build and publish AptoORM to your local onchain (local machine)

# [How to build, test and run]
# testing
docker build --target apto_orm-testing -t apto_orm-testing .

# Building
docker build -t apto_orm .

# Running
docker run -d -p 8080-8082:8080-8082 -p 9101:9101 -p 50051:50051 -p 5678:5678 --name apto_orm apto_orm

# Downloading keys and .env
curl http://localhost:8082/download.sh | sh

Or you can setup AptoORM at once.

./run.sh setup

ORM Onchain Architecture

ORM modules

orm_creator

orm_creator module provides a signer object that becomes the creator of AptoORM class objects in order to support the programmatic onchain resource handling. The signer object that has OrmCreator resource returns back its signer if the transaction sender (signer) is the owner of the signer object. It also returns the signer if other accounts authorized by the owner through PowerOfAttorney resource declaration. The owner account can register or revoke the authorized accounts directly or via the proof challenge.

orm_class

orm_class module is used to store the onchain properties of the user-defined class.

orm_module

orm_module is used to designate the orm_creator and orm_class of your class module. The orm_module is recorded to your account that is a placeholder to deploy your class module.

orm_object

orm_object ... TBD

ORM Onchain Resource

---
ORM Onchain Resource
---
classDiagram
  OrmModule "1" --> "1" OrmCreator: refer
  OrmModule "1" --> "1" `OrmClass, OrmTokenClass`: refer
  UserData .. `OrmObject, OrmToken`: co-locate in an object
  `OrmObject, OrmToken` --> "1" `OrmClass, OrmTokenClass`: refer
  `OrmClass, OrmTokenClass` "1" --> "1" OrmCreator: refer
  
  class UserData {
    +data: any
  }

  class OrmCreator {
    +publisher: address
    +extend_ref: ExtendRef
    +load_signer()
    +load_signer_by_signer_capability()
  }

  class `OrmClass, OrmTokenClass` {
    +creator: OrmCreator
    +config: OrmObjectConfig
  }

  class OrmModule {
    +signer: OrmCreator
    +class: OrmClass
  }

  class `OrmObject, OrmToken` {
    +class: OrmClass
  }
Loading

Onchain accounts in ORM Onchain Architecture

---
title Onchain accounts in ORM Onchain Architecture
---
flowchart
  subgraph OwnerAccount
  OwnerAccount::Account
  end
  subgraph AuthorizedAccount
  AuthorizedAccount::Account
  AuthorizedAccount::PowerOfAttorney
  end
  subgraph OrmCreator
  OrmCreator::ObjectCore
  OrmCreator::OrmCreator
  end
  subgraph OrmClass
  OrmClass::ObjectCore
  OrmClass::OrmClass
  OrmClass::OrmTokenClass
  end
  subgraph OrmObject
  OrmObject::ObjectCore
  OrmObject::OrmObject
  OrmObject::OrmToken
  OrmObject::UserData
  end
  OwnerAccount -- give authority (POA) --> AuthorizedAccount
  OwnerAccount -- create and load OrmCreator --> OrmCreator
  AuthorizedAccount -- load OrmCreator to handle OrmObject  --> OrmCreator
  OrmCreator -- create/delete/update --> OrmObject
  OrmCreator -- own --> OrmClass
  OrmObject -- refer --> OrmClass
Loading

AptoORM Free Charge server

[설명 추가]