Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(helpers): model helpers for ckb object #694

Merged
merged 5 commits into from
May 24, 2024
Merged

feat(helpers): model helpers for ckb object #694

merged 5 commits into from
May 24, 2024

Conversation

homura
Copy link
Collaborator

@homura homura commented May 23, 2024

Description

This PR introduced a new helper ModelHelper to provide some common functions for CKB-related objects

A Glance

ModelHelpers

export type ModelHelper<Model, ModelLike = Model> = {
  create(modelLike: ModelLike): Model;
  equals(modelLike: ModelLike, modelR: ModelLike): boolean;
  hash(modelLike: ModelLike): Uint8Array;
  clone(model: ModelLike): Model;
};
import { CellHelper } from "@ckb-lumos/helpers";

const cell = CellHelper.create({
  lock: "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5m759c",
});

const _61CKB = 61 * 10 ** 8;
asserts(BI.from(cell.cellOutput.capacity).eq(_61CKB)); // 61 CKB

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
    Tested?
  • Unit test

@homura homura requested review from Keith-CY and twhy May 23, 2024 01:33
Copy link

vercel bot commented May 23, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
lumos-website ✅ Ready (Inspect) Visit Preview May 24, 2024 6:42am

@homura homura added this to the 0.23.0 milestone May 23, 2024
Copy link

codecov bot commented May 23, 2024

Codecov Report

Attention: Patch coverage is 94.41341% with 10 lines in your changes are missing coverage. Please review.

Project coverage is 86.89%. Comparing base (a1592dd) to head (424c960).
Report is 22 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #694      +/-   ##
===========================================
+ Coverage    84.30%   86.89%   +2.58%     
===========================================
  Files          121      131      +10     
  Lines        24574    25343     +769     
  Branches      2364     2744     +380     
===========================================
+ Hits         20717    22021    +1304     
+ Misses        3816     3281     -535     
  Partials        41       41              
Files Coverage Δ
packages/helpers/src/models/blockchain.ts 100.00% <100.00%> (ø)
packages/helpers/src/models/index.ts 100.00% <100.00%> (ø)
packages/helpers/src/index.ts 91.02% <0.00%> (-0.06%) ⬇️
packages/helpers/src/models/base.ts 95.89% <95.89%> (ø)
packages/helpers/src/models/cell.ts 95.52% <95.52%> (ø)
packages/helpers/src/models/script.ts 88.88% <88.88%> (ø)

... and 43 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d483b92...424c960. Read the comment docs.

@Keith-CY
Copy link
Member

I didn't get the use case of ModelHelper. Just to instantiate a model easily with hash, clone and equals methods?

@homura
Copy link
Collaborator Author

homura commented May 23, 2024

I didn't get the use case of ModelHelper. Just to instantiate a model easily with hash, clone and equals methods?

There are two primary purposes of this PR:

  • create CKB-related objects in a convenient way
  • provide common methods to make the projects that are using Lumos more robust

Convenience

In the past, if developers wanted to create a Cell, they had to use template code like this:

const cell: Cell = {
  cellOutput: {
    lock: {...},
    capacity: '0x0'
  },
  data: '0x'
};

cell.cellOutput.capacity = minimalCapacityCompatible(cell).toHexString();

This process was heavy during transaction assembly. This PR introduces the concept of ModelLike to make the creation easier, simplifying it to:

CellHelper.create({
  lock: 'ckt1...', // ScriptLike
  type: {
    codeHash: randomBytes(32), // BytesLike is acceptable
    hashType: 'type',
    args: Buffer.from([...]) // BytesLike is acceptable
  },
  data: Uint128.pack(111) // BytesLike is acceptable
} /* CellLike */);

Robust

In the past, developers had multiple ways to interact with CKB-related objects, such as comparing two Scripts. However, the code may not have been robust enough because they compared objects based on a string. For example, Neuron:

if (
  codeHash === anotherCodeHash && 
  hashType === anotherHashType && 
  args === anotherArgs
) {
  // ...
}

This approach may cause unexpected behaviors, such as when the hex format string is not the same case, e.g. 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee !== 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE. This is especially relevant when using Omnilock Ethereum authentication

@Keith-CY
Copy link
Member

Keith-CY commented May 24, 2024

I didn't get the use case of ModelHelper. Just to instantiate a model easily with hash, clone and equals methods?

There are two primary purposes of this PR:

  • create CKB-related objects in a convenient way
  • provide common methods to make the projects that are using Lumos more robust

Convenience

In the past, if developers wanted to create a Cell, they had to use template code like this:

const cell: Cell = {
  cellOutput: {
    lock: {...},
    capacity: '0x0'
  },
  data: '0x'
};

cell.cellOutput.capacity = minimalCapacityCompatible(cell).toHexString();

This process was heavy during transaction assembly. This PR introduces the concept of ModelLike to make the creation easier, simplifying it to:

CellHelper.create({
  lock: 'ckt1...', // ScriptLike
  type: {
    codeHash: randomBytes(32), // BytesLike is acceptable
    hashType: 'type',
    args: Buffer.from([...]) // BytesLike is acceptable
  },
  data: Uint128.pack(111) // BytesLike is acceptable
} /* CellLike */);

Robust

In the past, developers had multiple ways to interact with CKB-related objects, such as comparing two Scripts. However, the code may not have been robust enough because they compared objects based on a string. For example, Neuron:

if (
  codeHash === anotherCodeHash && 
  hashType === anotherHashType && 
  args === anotherArgs
) {
  // ...
}

This approach may cause unexpected behaviors, such as when the hex format string is not the same case, e.g. 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee !== 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE. This is especially relevant when using Omnilock Ethereum authentication

It's pretty OOP, so why not

const cell = new Cell({
  codeHash: "hash"
})

// or

const cell = Cell.new({
  codeHash: 'hash'
})

and make the hash/equals a reader & getter as

class Cell {
  static hash(cell) {
    if (cell instanceof Cell) return cell.hash
    return new Cell(cell).hash
  }

  static equals(a, b) {
    const [c, d] = [a, b].map(i => i instanceof Cell ? i : new Cell(i))
    return c.equals(d)
  }

  getter hash() {
    return this._hash()
  }

  equals (cell) {
    return this._equals(cell)
  }
}

const cellHash = new Cell().hash
Cell.equals(a, b)
new Cell(a).equals(b)

@homura
Copy link
Collaborator Author

homura commented May 24, 2024

It's pretty OOP, so why not

The ModelHelpers includes a set of common methods similar to OOP. However, the transaction assembling process of Lumos uses plain objects directly instead of classes. We can find a lot of code like this:

const { injectCapacity } = common;

txSkeleton = await injectCapacity(txSkeleton, ...);

instead of

await txSkeleton.injectCapacity(...)

To keep a more consistent code style, ModelHelpers is designed as a utility instead of a superclass

OOP in JavaScript may cause some unexpected behavior like this:

class Named {
  name = 'Alice';

  sayHi() {
    console.log('Hi, I am ' + this.name);
  }
}

const named = new Named();
const { sayHi } = named;

sayHi(); // Cannot read properties of undefined...

There were many object deconstructions used in Lumos or used with Lumos; therefore, a helper may be a more suitable for Lumos

@Keith-CY
Copy link
Member

Keith-CY commented May 24, 2024

It's pretty OOP, so why not

The ModelHelpers includes a set of common methods similar to OOP. However, the transaction assembling process of Lumos uses plain objects directly instead of classes. We can find a lot of code like this:

const { injectCapacity } = common;

txSkeleton = await injectCapacity(txSkeleton, ...);

instead of

await txSkeleton.injectCapacity(...)

To keep a more consistent code style, ModelHelpers is designed as a utility instead of a superclass

OOP in JavaScript may cause some unexpected behavior like this:

class Named {
  name = 'Alice';

  sayHi() {
    console.log('Hi, I am ' + this.name);
  }
}

const named = new Named();
const { sayHi } = named;

sayHi(); // Cannot read properties of undefined...

There were many object deconstructions used in Lumos or used with Lumos; therefore, a helper may be a more suitable for Lumos

It's counterintuitive to me because it looks like OOP but works in FP way. Maybe I'm misled by the name CellHelper which is usually a Class or Enum.

How about defining it as a Class with static methods.

Or simply name it cellHelpers

@homura
Copy link
Collaborator Author

homura commented May 24, 2024

Maybe I'm misled by the name CellHelper which is usually a Class or Enum.

Indeed, I hadn't realized that the first first capitalized camel naming implied that the reference should be a class instead of a plain object

How about defining it as a Class with static methods.

Or simply name it cellHelpers

I'd prefer cellHelper

```javascript
import { CellHelper } from "@ckb-lumos/helpers";

const cell = CellHelper.create({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CellHelper should be cellHelper

@homura homura merged commit 8461e56 into develop May 24, 2024
9 checks passed
@homura homura deleted the model-helper branch May 24, 2024 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

2 participants