ckb-js / kuai

A protocol and framework for building universal dapps on Nervos CKB
MIT License
22 stars 11 forks source link

Technical breakdown of sudt demo backend design #446

Open yanguoyu opened 1 year ago

yanguoyu commented 1 year ago

After these designs are finished, we will create a server from the template and start coding. Here is the server's APIs. We can append comments to talk about the designs.

yanguoyu commented 1 year ago
  1. OmnilockModel
    • meta get the meta of omnilock address(capacity)
    • mint use ckb to mint sudt
  2. SudtModel
    • meta get the meta data(capacity)
    • send collect cell to send sudt
    • destory destory all sudt cell
  3. View
    • generateTx generate a tx from inputs and outputs
Daryl-L commented 1 year ago

Database

CREATE `token` (
  `id` int not null primary key auto_increment,
  `name` varchar(255) not null default '',
  `decimal` int not null default 18,
  `description` text not null default '',
  `website` varchar(255) not null default '',
  `icon` varchar(255) not null default '',
  `create_tx_hash` varchar(255) not null default '',
  `owner_id` int not null,
  `args` varchar(255) not null default '',
  `created_at` timestamp not null default current_timestamp(),
  `updated_at` timestamp not null default current_timestamp() on update current_timestamp(),
  unique key `uniq_args` (`args`),
);
CREATE `account` (
  `id` int not null primary key auto_increment(),
  `address` text not null,
  `eth_address` varchar(255) not null,
  `created_at` timestamp not null default current_timestamp(),
  `updated_at` timestamp not null default current_timestamp() on update current_timestamp()
)
CREATE `holder` (
  `id` int not null primary key auto_increment,
  `holder_id` int not null,
  `token_id` int not null,
  `amount` varchar(255) not null default '0',
  `created_at` timestamp not null default current_timestamp(),
  `updated_at` timestamp not null default current_timestamp() on update current_timestamp(),
  key `idx_token_id` (`token_id`),
  unique key `uniq_holder_id_token_id` (`holder_id`,`token_id`),
)
CREATE `token_history` (
  `id` int not null primary key auto_increment,
  `token_id` int not null,
  `tx_hash` varchar(255) not null,
  `status` int not null default 1 comment '1 - new, 2 - pending, 3 - success',
  `from` text not null,
  `to` text not null,
  `created_at` timestamp not null default current_timestamp(),
  `updated_at` timestamp not null default current_timestamp() on update current_timestamp(),
  unique key `uniq_token_id_tx_hash` (`token_id`, `tx_hash`)
)
yanguoyu commented 1 year ago

Init backend by kuai init https://github.com/ckb-js/kuai/pull/457

Daryl-L commented 1 year ago

Sample Kuai Project

This project demonstrates a basic kuai use case.

Run server

npm run build

node ./dist/src/main.js

API Doc

Mint Token

path: /sudt/mint/:typeId

method: POST

Request

{
  "from": [""],
  "to": "",
  "amount": "1000",
}

Response

{
  "code": 200,
  "data": {
    "txSkeleton": "txSkeleton": {
        "cellProvider": null,
        "cellDeps": [
            {
                "outPoint": {
                    "txHash": "0x27b62d8be8ed80b9f56ee0fe41355becdb6f6a40aeba82d3900434f43b1c8b60",
                    "index": "0x0"
                },
                "depType": "code"
            },
            {
                "outPoint": {
                    "txHash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37",
                    "index": "0x0"
                },
                "depType": "depGroup"
            },
            {
                "outPoint": {
                    "txHash": "0xe12877ebd2c3c364dc46c5c992bcfaf4fee33fa13eebdf82c591fc9825aab769",
                    "index": "0x0"
                },
                "depType": "code"
            }
        ],
        "headerDeps": [],
        "inputs": [
            {
                "cellOutput": {
                    "capacity": "0x1b41bf852c00",
                    "lock": {
                        "codeHash": "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb",
                        "hashType": "type",
                        "args": "0x00afbf535944be46a2f5879a3a349bc4fd5784a0e900"
                    },
                    "type": null
                },
                "data": "0x",
                "outPoint": {
                    "txHash": "0x5f2d84f67f378972ba7ee285e4d013450862d31defc121769fbf61fd5810627d",
                    "index": "0x1"
                },
                "blockNumber": "0xa66258"
            }
        ],
        "outputs": [
            {
                "cellOutput": {
                    "capacity": "0x35a4e9000",
                    "lock": {
                        "codeHash": "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb",
                        "hashType": "type",
                        "args": "0x00afbf535944be46a2f5879a3a349bc4fd5784a0e900"
                    },
                    "type": {
                        "codeHash": "0xc5e5dcf215925f7ef4dfaf5f4b4f105bc321c02776d6e7d52a1db3fcd9d011a4",
                        "hashType": "type",
                        "args": "0xfb7b6c4a2baf39ebfdd634e76737725362cf18042a31256488382137ae830784"
                    }
                },
                "data": "0xa0860100000000000000000000000000"
            },
            {
                "cellOutput": {
                    "lock": {
                        "codeHash": "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb",
                        "hashType": "type",
                        "args": "0x00afbf535944be46a2f5879a3a349bc4fd5784a0e900"
                    },
                    "capacity": "0x1b3e65351560"
                },
                "data": "0x"
            }
        ],
        "witnesses": [
            "0x690000001000000069000000690000005500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        ],
        "fixedEntries": [],
        "signingEntries": [],
        "inputSinces": {}
    }
  }
}

Create Token

path: /token

method: POST

Request

{
  "code": 200,
  "data": {
    "name": "USDT",
    "account": "", // the address of owner
    "decimal": 18,
    "description": "",
    "website": "",
    "icon": "",
    "email": ""
  }
}

Reponse

{
    "code": "201",
    "data": {
      "txSkeleton": {
        "cellProvider": null,
        "cellDeps": [
            {
                "outPoint": {
                    "txHash": "0x27b62d8be8ed80b9f56ee0fe41355becdb6f6a40aeba82d3900434f43b1c8b60",
                    "index": "0x0"
                },
                "depType": "code"
            },
            {
                "outPoint": {
                    "txHash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37",
                    "index": "0x0"
                },
                "depType": "depGroup"
            },
            {
                "outPoint": {
                    "txHash": "0xe12877ebd2c3c364dc46c5c992bcfaf4fee33fa13eebdf82c591fc9825aab769",
                    "index": "0x0"
                },
                "depType": "code"
            }
        ],
        "headerDeps": [],
        "inputs": [
            {
                "cellOutput": {
                    "capacity": "0x1b41bf852c00",
                    "lock": {
                        "codeHash": "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb",
                        "hashType": "type",
                        "args": "0x00afbf535944be46a2f5879a3a349bc4fd5784a0e900"
                    },
                    "type": null
                },
                "data": "0x",
                "outPoint": {
                    "txHash": "0x5f2d84f67f378972ba7ee285e4d013450862d31defc121769fbf61fd5810627d",
                    "index": "0x1"
                },
                "blockNumber": "0xa66258"
            }
        ],
        "outputs": [
            {
                "cellOutput": {
                    "capacity": "0x35a4e9000",
                    "lock": {
                        "codeHash": "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb",
                        "hashType": "type",
                        "args": "0x00afbf535944be46a2f5879a3a349bc4fd5784a0e900"
                    },
                    "type": {
                        "codeHash": "0xc5e5dcf215925f7ef4dfaf5f4b4f105bc321c02776d6e7d52a1db3fcd9d011a4",
                        "hashType": "type",
                        "args": "0xfb7b6c4a2baf39ebfdd634e76737725362cf18042a31256488382137ae830784"
                    }
                },
                "data": "0xa0860100000000000000000000000000"
            },
            {
                "cellOutput": {
                    "lock": {
                        "codeHash": "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb",
                        "hashType": "type",
                        "args": "0x00afbf535944be46a2f5879a3a349bc4fd5784a0e900"
                    },
                    "capacity": "0x1b3e65351560"
                },
                "data": "0x"
            }
        ],
        "witnesses": [
            "0x690000001000000069000000690000005500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        ],
        "fixedEntries": [],
        "signingEntries": [],
        "inputSinces": {}
    }
  }
}

Update Token

path: /token/:typeId

method: PUT

Request

{
  "code": 200,
  "data": {
    "name": "USDT",
    "decimal": 18,
    "description": "",
    "website": "",
    "icon": "",
    "explorerCode": "" // the verify code from explorer
  }
}

Response

{
  "code": 201,
  "data": {}
}

Transfer Token

path: /token/transfer

method: POST

Request

{
  "typeId": "", // token args
  "amount": "",
  "to": ""
}

Response

{
  "code": 200,
  "data": {
    "txSkeleton": {
      "cellProvider": null,
      "cellDeps": [
          {
              "outPoint": {
                  "txHash": "0x27b62d8be8ed80b9f56ee0fe41355becdb6f6a40aeba82d3900434f43b1c8b60",
                  "index": "0x0"
              },
              "depType": "code"
          },
          {
              "outPoint": {
                  "txHash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37",
                  "index": "0x0"
              },
              "depType": "depGroup"
          },
          {
              "outPoint": {
                  "txHash": "0xe12877ebd2c3c364dc46c5c992bcfaf4fee33fa13eebdf82c591fc9825aab769",
                  "index": "0x0"
              },
              "depType": "code"
          }
      ],
      "headerDeps": [],
      "inputs": [
          {
              "cellOutput": {
                  "capacity": "0x1b41bf852c00",
                  "lock": {
                      "codeHash": "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb",
                      "hashType": "type",
                      "args": "0x00afbf535944be46a2f5879a3a349bc4fd5784a0e900"
                  },
                  "type": null
              },
              "data": "0x",
              "outPoint": {
                  "txHash": "0x5f2d84f67f378972ba7ee285e4d013450862d31defc121769fbf61fd5810627d",
                  "index": "0x1"
              },
              "blockNumber": "0xa66258"
          }
      ],
      "outputs": [
          {
              "cellOutput": {
                  "capacity": "0x35a4e9000",
                  "lock": {
                      "codeHash": "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb",
                      "hashType": "type",
                      "args": "0x00afbf535944be46a2f5879a3a349bc4fd5784a0e900"
                  },
                  "type": {
                      "codeHash": "0xc5e5dcf215925f7ef4dfaf5f4b4f105bc321c02776d6e7d52a1db3fcd9d011a4",
                      "hashType": "type",
                      "args": "0xfb7b6c4a2baf39ebfdd634e76737725362cf18042a31256488382137ae830784"
                  }
              },
              "data": "0xa0860100000000000000000000000000"
          },
          {
              "cellOutput": {
                  "lock": {
                      "codeHash": "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb",
                      "hashType": "type",
                      "args": "0x00afbf535944be46a2f5879a3a349bc4fd5784a0e900"
                  },
                  "capacity": "0x1b3e65351560"
              },
              "data": "0x"
          }
      ],
      "witnesses": [
          "0x690000001000000069000000690000005500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
      ],
      "fixedEntries": [],
      "signingEntries": [],
      "inputSinces": {}
    }
  }
}

Token List

path: /token

Request

param type position description
address string query user address

method: GET

Response

{
  "code": 200,
  "data": [
    {
      "uan": "USDT",
      "displayName": "USDT",
      "name": "USDT",
      "decimal": 18,
      "description": "",
      "website": "",
      "icon": "",
      "url": "",
      "issuser": "",
      "args": "",
      "typeId": "",
    },
  ]
}

Token Detail

path: /token/:args

method: GET

Response

{
  "code": 200,
  "data": {
    "uan": "USDT",
    "displayName": "USDT",
    "name": "USDT",
    "decimal": 18,
    "description": "",
    "website": "",
    "icon": "",
    "url": "",
    "issuser": ""
  }
}

Asset List

path: /account/:address/assets

method: GET

Request

param type position description
address string query user address

Response

{
  "code": 200,
  "data": [
      {
        "uan": "USDT",
        "displayName": "USDT",
        "decimal": 18,
        "amount": ""
      }
    ]
}

Token Transfer History

path: /account/:address/assets/transfer/history

method: GET

Response

{
  "code": 200,
  "data": [
    {
      "txHash": "",
      "from": "",
      "to": "",
      "time": "",
      "status": "",
      "sudtAmount": "",
      "CKBAmount": "",
      "url": "",
    }
  ]
}
Keith-CY commented 1 year ago

Create Token

path: /token

method: POST

Request

{
  "symbol": "USDT",
  "name": "USDT",
  "amount": "100000",
  "decimal": "18",
  "description": "",
  "website": "",
  "icon": ""
}

Reponse

{
  "code": 201,
  "data": {
    "url": "" // direct to explorer to the transaction to issue the token
  }
}

Response

Update Token

path: /token

method: PUT

Request

{
  "symbol": "USDT",
  "name": "USDT",
  "amount": "100000",
  "decimal": "18",
  "description": "",
  "website": "",
  "icon": "",
  "args": "", // sudt args
  "signature": ""
}

Response

{
  "code": 201,
  "data": {}
}

Transfer Token

path: /token/transfer

method: POST

Request

{
  "token": "", // token args
  "amount": "",
  "to": ""
}

Token List

path: /token

Request

param type position description address string query user address method: GET

Response

[
  {
    "symbol": "USDT",
    "name": "USDT",
    "amount": "100000",
    "decimal": "18",
    "description": "",
    "website": "",
    "icon": ""
  }
]

Token Detail

path: /token/:args

method: GET

Response

 {
    "symbol": "USDT",
    "name": "USDT",
    "amount": "100000",
    "decimal": "18",
    "description": "",
    "website": "",
    "icon": "",
    "url": "",
    "issuser": ""
}

Asset List

path: /assets

method: GET

Request

param type position description address string query user address

Response

{
  "code": 200,
  "data": [
      {
        "symbol": "USDT",
        "name": "USDT",
        "amount": ""
      }
    ]
}

Token Transfer History

path: /token/transfer/history

method: GET

Request

param type position description address string query user address

Response

[
  {
    "txHash": "",
    "from": "",
    "to": "",
    "time": "",
    "status": "",
    "sudtAmount": "",
    "CKBAmount": "",
    "url": "",
  }
]

The response should be canonical, some of them are wrapped with data that next to code, some are not.

PainterPuppets commented 1 year ago

Update Token

path: /token

method: PUT

Request

{
  "code": 200,
  "data": {
    "symbol": "USDT",
    "name": "USDT",
    "amount": "100000",
    "decimal": "18",
    "description": "",
    "website": "",
    "icon": "",
    "args": "", // sudt args
    "signature": ""
  }
}

What is signature used for?

Daryl-L commented 1 year ago

Update Token

path: /token method: PUT

Request

{
  "code": 200,
  "data": {
    "symbol": "USDT",
    "name": "USDT",
    "amount": "100000",
    "decimal": "18",
    "description": "",
    "website": "",
    "icon": "",
    "args": "", // sudt args
    "signature": ""
  }
}

What is signature used for?

My mistake, the request should be the same as token update request, I have changed it just now.

{
  "code": 200,
  "data": {
    "symbol": "USDT",
    "name": "USDT",
    "account": "", // the args of account in omnilock
    "supply": "100000",
    "decimal": 18,
    "description": "",
    "website": "",
    "icon": "",
    "typeId": "",
    "args": "", // the args of sudt type script
    "explorerCode": "" // the verify code from explorer
  }
}
Daryl-L commented 1 year ago

I have changed the API of create token based on the conclusion from last night. Some properties are added and the transaction skeleton will be responded.

PainterPuppets commented 1 year ago

There are a few questions I want to confirm

image image

And I feel like maybe the mint api is missing?

Another question, if the frontend has signed the tx through the wallet, how should it be sent to the chain, is it a direct post to the rpc or through the backend?

Daryl-L commented 1 year ago

I think the transaction should send in the front end directly

Daryl-L commented 1 year ago

There are a few questions I want to confirm

  • Updating tokens: This api seems to be missing the token identifier, for example, if a user manages multiple tokens, which token is updated when he calls update, I guess the url should be PUT /token/:args
  • Transfer token Should this api perhaps return a Transaction?
  • Token list & detail The response type of these two api's seem to be different, but there is only a difference between the issuer and url fields, is it possible to unify them?
  • Asset list Maybe need the decimal field
  • History: The front-end page displays the specific tx information and perhaps needs to return the entire tx

image image And I feel like maybe the mint api is missing?

Another question, if the frontend has signed the tx through the wallet, how should it be sent to the chain, is it a direct post to the rpc or through the backend?

update token, transfer token, token list, token detail, asset list has been changed, you can check it.

Daryl-L commented 1 year ago

There are a few questions I want to confirm

  • Updating tokens: This api seems to be missing the token identifier, for example, if a user manages multiple tokens, which token is updated when he calls update, I guess the url should be PUT /token/:args
  • Transfer token Should this api perhaps return a Transaction?
  • Token list & detail The response type of these two api's seem to be different, but there is only a difference between the issuer and url fields, is it possible to unify them?
  • Asset list Maybe need the decimal field
  • History: The front-end page displays the specific tx information and perhaps needs to return the entire tx

image image And I feel like maybe the mint api is missing?

Another question, if the frontend has signed the tx through the wallet, how should it be sent to the chain, is it a direct post to the rpc or through the backend?

mint provided

PainterPuppets commented 1 year ago

Mint Token

path: /sudt/mint/:typeId

method: POST

Request

{
  "from": [""],
  "to": "",
  "amount": "1000",
}

I'm not sure if only the issuer can mint. Will there be another address in the from field?

PainterPuppets commented 1 year ago

Token List

path: /token

Request

param type position description address string query user address method: GET

Response

{
  "code": 200,
  "data": [
    {
      "uan": "USDT",
      "displayName": "USDT",
      "name": "USDT",
      "decimal": 18,
      "description": "",
      "website": "",
      "icon": "",
      "url": "",
      "issuser": ""
    },
  ]
}

If mint and transfer apis need to use the typeid and args fields, perhaps token list and token detail need to return the typeid and args.

Daryl-L commented 1 year ago

Mint Token

path: /sudt/mint/:typeId method: POST

Request

{
  "from": [""],
  "to": "",
  "amount": "1000",
}

I'm not sure if only the issuer can mint. Will there be another address in the from field?

As I learnt from the RFC of sUDT, only issuer could mint more sUDT to others.