Skip to content

Conversation

@rivjc1025
Copy link

@changeset-bot
Copy link

changeset-bot bot commented Nov 18, 2025

⚠️ No Changeset found

Latest commit: b5699bd

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@netlify
Copy link

netlify bot commented Nov 18, 2025

Deploy Preview for apiccc ready!

Name Link
🔨 Latest commit b5699bd
🔍 Latest deploy log https://app.netlify.com/projects/apiccc/deploys/693287e798a14a00084a2d55
😎 Deploy Preview https://deploy-preview-330--apiccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 82 (no change from production)
Accessibility: 100 (no change from production)
Best Practices: 100 (no change from production)
SEO: 94 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 18, 2025

Deploy Preview for appccc ready!

Name Link
🔨 Latest commit b5699bd
🔍 Latest deploy log https://app.netlify.com/projects/appccc/deploys/693287e73f3cf700084d0c03
😎 Deploy Preview https://deploy-preview-330--appccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 67 (🔴 down 8 from production)
Accessibility: 89 (🟢 up 1 from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 18, 2025

Deploy Preview for liveccc ready!

Name Link
🔨 Latest commit b5699bd
🔍 Latest deploy log https://app.netlify.com/projects/liveccc/deploys/693287e7bbfe1100087093d0
😎 Deploy Preview https://deploy-preview-330--liveccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 42 (🟢 up 22 from production)
Accessibility: 88 (no change from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @rivjc1025, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly expands the Common Chains Connector's (CCC) wallet support by integrating the SupeRISE wallet. It introduces a new, self-contained package for SupeRISE, complete with a CKB signer implementation, and seamlessly hooks it into the existing CCC framework. This enhancement allows developers and users to leverage SupeRISE for CKB transactions and message signing directly through CCC-powered applications.

Highlights

  • New SupeRISE Package: A new package, @ckb-ccc/superise, has been introduced to provide dedicated support for the SupeRISE wallet within the Common Chains Connector (CCC) ecosystem.
  • CKB Signer Implementation: The new superise package includes a CkbSigner class that extends ccc.Signer, enabling CKB-specific operations such as connecting to the wallet, retrieving addresses, signing messages, and preparing/signing transactions.
  • Integration with CCC Core: The SupeRISE wallet is now integrated into the main ccc package, allowing applications to discover and utilize it through the SignersController.
  • SupeRISE Wallet Asset: An SVG asset representing the SupeRISE wallet logo has been added to the ccc package for UI representation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@netlify
Copy link

netlify bot commented Nov 18, 2025

Deploy Preview for docsccc ready!

Name Link
🔨 Latest commit b5699bd
🔍 Latest deploy log https://app.netlify.com/projects/docsccc/deploys/693287e7f08d7c00089189fe
😎 Deploy Preview https://deploy-preview-330--docsccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 68 (🟢 up 1 from production)
Accessibility: 88 (no change from production)
Best Practices: 92 (no change from production)
SEO: 92 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for the SupeRISE wallet by adding a new package @ckb-ccc/superise and integrating it into the main ccc package. The new package provides a CkbSigner for interacting with the SupeRISE wallet extension. The changes are well-structured, but I've identified a critical issue with a missing file extension in an import that could lead to runtime errors, along with a few other suggestions to enhance correctness and maintainability.

Copy link
Member

@Hanssen0 Hanssen0 left a comment

Choose a reason for hiding this comment

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

Thank you for your contribution! It's happy to see CCC can help devs to integrate the SuperRISE wallet.

This should be a PR that works. However, I have some concerns about the security design of APIs. CCC, or other wallet SDKs, run in an insecure environment, which means code involving security/verification should not depend on the SDK's correctness.

BTW, where can I download the SupeRISE wallet to test this PR?


override async signMessageRaw(message: string) {
const signMessage = `Nervos Message:${message}`;
const sign = await this.bridge.signCkbMessage(signMessage);
Copy link
Member

Choose a reason for hiding this comment

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

This may be a security threat: If a website invokes the signCkbMessage API with a transaction hash, users may accidentally sign a tx while only knowing they're signing an arbitrary message.

I recommend adding the message prefix in the wallet to prevent this problem.

Copy link
Author

Choose a reason for hiding this comment

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

We added a prefix for signMessage on the wallet side.

const tx = ccc.Transaction.from(txLike);

const addressObj = await this.getAddressObj();
const acp = await ccc.Script.fromKnownScript(
Copy link
Member

Choose a reason for hiding this comment

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

Note: If new scripts are expected to be added in the future, I recommend doing this on the wallet side.

It's harder to let devs update their apps' SDKs than to let users update their wallets, which means we might end up with multiple SDK versions across different apps. As a result, this might cause the new address not to be supported by some apps.

Copy link
Author

Choose a reason for hiding this comment

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

Considering that updating the app is inconvenient, the current wallet design requires signTransaction to manually specify witnessIndexes.

if (signatureCache.has(message)) {
signature = signatureCache.get(message)!;
} else {
const sign = await this.bridge.signCkbHashAll(
Copy link
Member

Choose a reason for hiding this comment

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

As a recommendation, we usually pass the entire transaction to the wallet to allow it to display the transaction details. Blind signing is always dangerous, and we may want to avoid that.

Copy link
Author

Choose a reason for hiding this comment

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

We added a new signCkbTransaction method, which passes the entire transaction information and displays the transaction details for the user to confirm (similar to JoyID).

@Hanssen0
Copy link
Member

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for the Superise wallet by adding a new package @ckb-ccc/superise and integrating it. The changes are well-structured, but I've found a few issues in the new signer implementation concerning connection state management and transaction signing logic that should be addressed. There is also a minor configuration issue in the documentation generation setup.

Comment on lines 62 to 64
override async disconnect() {
this.connection = undefined;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The disconnect method only clears the in-memory connection property. It should also clear the connection details from localStorage to ensure a consistent disconnected state, especially after a page reload.

  override async disconnect() {
    this.connection = undefined;
    localStorage.removeItem(this.connectionStorageKey);
  }

Comment on lines 175 to 177
if (!info) {
return tx;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The code returns the transaction object from within the loop if getSignHashInfo returns no info for a script. This will prevent other scripts in the transaction from being signed. You should use continue to proceed to the next script in the loop instead of returning.

      if (!info) {
        continue;
      }

@@ -0,0 +1,6 @@
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["./src/index.ts", "./src/advanced.ts"],
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The entry point src/advanced.ts does not exist in the package. This will cause an error when generating documentation. Please remove it from the entryPoints array.

Suggested change
"entryPoints": ["./src/index.ts", "./src/advanced.ts"],
"entryPoints": ["./src/index.ts"],

"@ckb-ccc/utxo-global": "workspace:*",
"@ckb-ccc/xverse": "workspace:*"
"@ckb-ccc/xverse": "workspace:*",
"@ckb-ccc/superise": "workspace:*"
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For better maintainability, it's a good practice to keep dependencies sorted alphabetically. @ckb-ccc/superise should be placed after @ckb-ccc/shell.

Comment on lines +55 to +60
async isConnected() {
if (this.connection) return true;

this.restoreConnection();
return !!this.connection;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The isConnected method has a side effect of trying to restore a connection from localStorage. A method with a name like isConnected is generally expected to be a pure function that only checks the current state without modifying it. This can lead to unexpected behavior. Consider removing the call to restoreConnection() from this method, as the connection restoration logic is already present in getConnection(), which is the more appropriate place for it.

  async isConnected() {
    return !!this.connection;
  }

@rivjc1025
Copy link
Author

@Hanssen0
Thank you for your feedback! We’ve decided to redesign this part.

@rivjc1025
Copy link
Author

BTW, where can I download the SupeRISE wallet to test this PR?

The app is still in the testing phase, so we may not be able to provide a version for you to test at the moment. 😂

@Hanssen0
Copy link
Member

Hanssen0 commented Dec 9, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the integration of the SupeRISE wallet into the Common Chains Connector (CCC) project. The changes include adding the new @ckb-ccc/superise package, its assets, and integrating its signer functionality into the SignersController. A new verifySuperise.ts file has been added to handle CKB message verification using secp256k1 curves, which appears to be correctly implemented. The overall structure and implementation align with existing patterns in the codebase for wallet integrations.

@fghdotio
Copy link
Contributor

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for the SupeRISE wallet. This includes creating a new package @ckb-ccc/superise, adding the necessary assets and integration points in the main ccc package, and implementing the signing and verification logic in the core package.

My review identified two critical bugs related to signature verification that would cause it to fail. One is in the message hashing logic, and the other is in the signer type returned by the new signer class. I've also provided suggestions for code simplification and fixing a minor typo. A general point of feedback is to ensure consistent naming for "SupeRISE" throughout the codebase (e.g., superise, SupeRISE, SUPERISE) for better maintainability.

Comment on lines +12 to +19
function messageHashCkbSuperise(message: string | BytesLike): Hex {
const msg = typeof message === "string" ? message : hexFrom(message);
const buffer = bytesFrom(
`${SUPERISE_MESSAGE_PREFIX}${msg.length}${msg}`,
"utf8",
);
return hashCkb(buffer);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The implementation of messageHashCkbSuperise is incorrect for BytesLike messages. When message is BytesLike, it's converted to a hex string, and then msg.length is used. This uses the length of the hex string, not the byte length of the message. Additionally, the hex string itself is then encoded as UTF-8, which is not the standard way of hashing byte arrays.

This will cause signature verification to fail for any non-string messages.

The implementation should be similar to messageHashEvmPersonal, where the message is first converted to a Uint8Array to get its byte length, and then the parts are concatenated as bytes.

function messageHashCkbSuperise(message: string | BytesLike): Hex {
  const messageBytes = bytesFrom(
    message,
    typeof message === "string" ? "utf8" : undefined,
  );
  const prefix = `${SUPERISE_MESSAGE_PREFIX}${messageBytes.length}`;
  const prefixBytes = bytesFrom(prefix, "utf8");

  const buffer = new Uint8Array(prefixBytes.length + messageBytes.length);
  buffer.set(prefixBytes);
  buffer.set(messageBytes, prefixBytes.length);

  return hashCkb(buffer);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

msg.length has different behavior across programming languages. Some use byte length, other might use character count. This might cause problem when developers use different programming languages to interact with SupeRISE wallet. Common pattern is to use byte length as in Bitcoin and Ethereum.

Comment on lines +9 to +11
get signType() {
return ccc.SignerSignType.CkbSecp256k1;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The signType getter returns ccc.SignerSignType.CkbSecp256k1. However, a new SupeRISE sign type has been introduced, and the message hashing logic for SupeRISE is different from the standard CKB secp256k1 signing. Returning CkbSecp256k1 will cause Signer.verifyMessage to use verifyMessageCkbSecp256k1, which will fail because of the different message prefix.

This should return ccc.SignerSignType.SupeRISE to ensure the correct verification function (verifyMessageSuperise) is used.

Suggested change
get signType() {
return ccc.SignerSignType.CkbSecp256k1;
}
get signType() {
return ccc.SignerSignType.SupeRISE;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch by Gemini here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is returning CkbSecp256k1 intentional? Should this be ccc.SignerSignType.SupeRISE instead? Could you please clarify?

Comment on lines +30 to +39
return secp256k1.verify(
new secp256k1.Signature(
numFrom(signatureBytes.slice(0, 32)),
numFrom(signatureBytes.slice(32, 64)),
)
.addRecoveryBit(Number(numFrom(signatureBytes.slice(64, 65))))
.toBytes(),
bytesFrom(messageHashCkbSuperise(message)),
bytesFrom(publicKey),
);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The implementation of verifyMessageSuperise is a bit convoluted. The secp256k1.verify function from @noble/curves can directly accept a 64-byte signature (r and s components). The current code creates a Signature object, adds a recovery bit (which is not used for verification), and then converts it back to a 64-byte Uint8Array.

This can be simplified by slicing the first 64 bytes of the signature and passing it directly to secp256k1.verify. This is more efficient and easier to understand.

  return secp256k1.verify(
    signatureBytes.slice(0, 64),
    bytesFrom(messageHashCkbSuperise(message)),
    bytesFrom(publicKey),
  );

try {
this.connection = JSON.parse(connection) as CkbConnection;
} catch (error) {
console.error("Failed to restore superise connection:", error);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

There's a typo in the error message. "superise" should probably be "SupeRISE" to be consistent with the wallet's branding used elsewhere.

Suggested change
console.error("Failed to restore superise connection:", error);
console.error("Failed to restore SupeRISE connection:", error);

return [await this.getAddressObj()];
}

override async signMessageRaw(message: string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The signMessageRaw method signature should support both string and BytesLike to match the base class signature and other wallet implementations.

Comment on lines +140 to +145
await Promise.all(
scripts.map(async ({ script, cellDeps }) => {
await tx.prepareSighashAllWitness(script, 65, this.client);
await tx.addCellDepInfos(this.client, cellDeps);
}),
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Since tx is a shared mutable object, concurrent modifications via Promise.all cause race conditions. We should switch to sequential execution to ensure data integrity.

Comment on lines +24 to +29
private saveConnection() {
localStorage.setItem(
this.connectionStorageKey,
JSON.stringify(this.connection),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Since Superise is an extension wallet, storing the connection in localStorage can cause state inconsistency, like when the user switches accounts. It's better to rely entirely on the bridge's state without persistence.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you help clarify whether the Superise wallet supports address/account switching? If no, we can safely ignore the state inconsistency concern.

@fghdotio
Copy link
Contributor

BTW, where can I download the SupeRISE wallet to test this PR?

The app is still in the testing phase, so we may not be able to provide a version for you to test at the moment. 😂

Just want to follow up on this: is there now a testable Superise wallet version available, or any alternative testing methods?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants