Skip to content

Support decoding codama program events#25

Merged
stwiname merged 3 commits into
mainfrom
decode-codama-events
Sep 15, 2025
Merged

Support decoding codama program events#25
stwiname merged 3 commits into
mainfrom
decode-codama-events

Conversation

@stwiname
Copy link
Copy Markdown
Collaborator

@stwiname stwiname commented Sep 15, 2025

Description

Support decoding logs from codama programs

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist

  • I have tested locally
  • I have performed a self review of my changes
  • Updated any relevant documentation
  • Linked to any relevant issues
  • I have added tests relevant to my changes
  • Any dependent changes have been merged and published in downstream modules
  • My code is up to date with the base branch
  • I have updated relevant changelogs. We suggest using chan

@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented Sep 15, 2025

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
20833600 Triggered Generic High Entropy Secret a9f5992 packages/node/src/solana/decoder.spec.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 15, 2025

Coverage report

Caution

Test run failed

St.
Category Percentage Covered / Total
🔴 Statements 52.03% 2219/4265
🟡 Branches 67.92% 235/346
🔴 Functions 50.72% 106/209
🔴 Lines 52.03% 2219/4265

Test suite run failed

Failed tests: 4/54. Failed suites: 1/8.
  ● test solana project.yaml › could get options in template from its deployment

    expect(received).toContain(expected) // indexOf

    Expected substring: "abi: Pool"
    Received string:    "dataSources:
      - kind: solana/Runtime
        mapping:
          file: ./dist/index.js
          handlers:
            - filter: {}
              handler: handlePoolCreated
              kind: solana/LogHandler
        startBlock: 12369621
      - kind: solana/Runtime
        mapping:
          file: ./dist/index.js
          handlers:
            - filter: {}
              handler: handleIncreaseLiquidity
              kind: solana/LogHandler
            - filter: {}
              handler: handleDecreaseLiquidity
              kind: solana/LogHandler
            - filter: {}
              handler: handleCollect
              kind: solana/LogHandler
            - filter: {}
              handler: handleTransfer
              kind: solana/LogHandler
        startBlock: 12369651
    network:
      chainId: '1'
    runner:
      node:
        name: '@subql/node-solana'
        version: '*'
      query:
        name: '@subql/query'
        version: '*'
    schema:
      file: ./schema.graphql
    specVersion: 1.0.0
    templates:
      - kind: solana/Runtime
        mapping:
          file: ./dist/index.js
          handlers:
            - filter: {}
              handler: handleInitialize
              kind: solana/LogHandler
            - filter: {}
              handler: handleSwap
              kind: solana/LogHandler
            - filter: {}
              handler: handleMint
              kind: solana/LogHandler
            - filter: {}
              handler: handleBurn
              kind: solana/LogHandler
            - filter: {}
              handler: handleFlash
              kind: solana/LogHandler
        name: Pool
    "

      41 |     const manifest = loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0.yaml'));
      42 |     const deployment = manifest.toDeployment();
    > 43 |     expect(deployment).toContain('abi: Pool');
         |                        ^
      44 |   });
      45 | });
      46 |

      at Object.<anonymous> (packages/common-solana/src/project/project.spec.ts:43:24)

  ● project.yaml › can validate project.yaml

    Not implemented

      48 |   it('can validate project.yaml', () => {
      49 |     // TODO this should catch a specific error, the file doesn't currently exist
    > 50 |     throw new Error('Not implemented');
         |           ^
      51 |     expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_falsy.yaml'))).toThrow();
      52 |     expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_falsy_array.yaml'))).toThrow();
      53 |   });

      at Object.<anonymous> (packages/common-solana/src/project/project.spec.ts:50:11)

  ● project.yaml › can fail validation if version not supported

    Not implemented

      55 |   it('can fail validation if version not supported', () => {
      56 |     // TODO this should catch a specific error, the file doesn't currently exist
    > 57 |     throw new Error('Not implemented');
         |           ^
      58 |     expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_invalid_version.yaml'))).toThrow('');
      59 |   });
      60 |

      at Object.<anonymous> (packages/common-solana/src/project/project.spec.ts:57:11)

  ● project.yaml › get v1.0.0 deployment mapping filter

    expect(received).toContain(expected) // indexOf

    Expected substring: "Transfer (address from, address to, uint256 tokenId)"
    Received string:    "dataSources:
      - kind: solana/Runtime
        mapping:
          file: ./dist/index.js
          handlers:
            - filter: {}
              handler: handlePoolCreated
              kind: solana/LogHandler
        startBlock: 12369621
      - kind: solana/Runtime
        mapping:
          file: ./dist/index.js
          handlers:
            - filter: {}
              handler: handleIncreaseLiquidity
              kind: solana/LogHandler
            - filter: {}
              handler: handleDecreaseLiquidity
              kind: solana/LogHandler
            - filter: {}
              handler: handleCollect
              kind: solana/LogHandler
            - filter: {}
              handler: handleTransfer
              kind: solana/LogHandler
        startBlock: 12369651
    network:
      chainId: '1'
    runner:
      node:
        name: '@subql/node-solana'
        version: '*'
      query:
        name: '@subql/query'
        version: '*'
    schema:
      file: ./schema.graphql
    specVersion: 1.0.0
    templates:
      - kind: solana/Runtime
        mapping:
          file: ./dist/index.js
          handlers:
            - filter: {}
              handler: handleInitialize
              kind: solana/LogHandler
            - filter: {}
              handler: handleSwap
              kind: solana/LogHandler
            - filter: {}
              handler: handleMint
              kind: solana/LogHandler
            - filter: {}
              handler: handleBurn
              kind: solana/LogHandler
            - filter: {}
              handler: handleFlash
              kind: solana/LogHandler
        name: Pool
    "

      70 |     const deploymentString = manifestVersioned.toDeployment();
      71 |     expect(filter).not.toBeNull();
    > 72 |     expect(deploymentString).toContain('Transfer (address from, address to, uint256 tokenId)');
         |                              ^
      73 |   });
      74 |
      75 |   it('can convert genesis hash in v1.0.0 to chainId in deployment', () => {

      at Object.<anonymous> (packages/common-solana/src/project/project.spec.ts:72:30)

Report generated by 🧪jest coverage report action from db2ea61

@stwiname stwiname requested a review from Copilot September 15, 2025 04:27
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds support for decoding logs from Codama programs by implementing event decoding functionality. The implementation extends the existing decoder to handle Codama IDL events alongside the already supported Anchor IDL v0.1.0 events.

  • Enhanced log decoding to support Codama program events
  • Added discriminator generation utility for event name matching
  • Included comprehensive test coverage for the new functionality

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/node/test/8t2R21V3vjS1ucZzmX2memtGptjYZi2yGY3cYVa8dak7.idl.json Added test IDL file for Codama program event testing
packages/node/src/solana/decoder.ts Enhanced log decoding logic to support Codama IDL events
packages/node/src/solana/decoder.spec.ts Added test case for Codama event decoding
packages/node/CHANGELOG.md Documented the new feature addition
packages/common-solana/src/codegen/idl.ts Added discriminator generation utility function
packages/common-solana/CHANGELOG.md Documented the new utility function

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +128 to +133
return root.program.definedTypes.find(
(t) =>
// Warning this is fragile as there can be various casings of the type names, but because it requires hashing data we can only go one way
logDisc.indexOf(getDiscriminator(t.name)) === 0 ||
logDisc.indexOf(getDiscriminator(pascalCase(t.name))) === 0,
);
Copy link

Copilot AI Sep 15, 2025

Choose a reason for hiding this comment

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

[nitpick] The comment indicates this is fragile due to casing variations. Consider using a more robust approach by trying additional common naming conventions (camelCase, titleCase) or storing discriminators in a map to avoid repeated hash computations.

Suggested change
return root.program.definedTypes.find(
(t) =>
// Warning this is fragile as there can be various casings of the type names, but because it requires hashing data we can only go one way
logDisc.indexOf(getDiscriminator(t.name)) === 0 ||
logDisc.indexOf(getDiscriminator(pascalCase(t.name))) === 0,
);
// Build a map from all possible discriminators to their type nodes, covering common casing conventions
const discriminatorToType = new Map<string, typeof root.program.definedTypes[0]>();
for (const t of root.program.definedTypes) {
const nameVariants = [
t.name,
pascalCase(t.name),
camelCase(t.name),
titleCase(t.name),
];
for (const variant of nameVariants) {
const disc = getDiscriminator(variant);
discriminatorToType.set(disc.toString('hex'), t);
}
}
const logDiscHex = logDisc.toString('hex');
return discriminatorToType.get(logDiscHex);

Copilot uses AI. Check for mistakes.
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0

import {createHash} from 'node:crypto';
Copy link

Copilot AI Sep 15, 2025

Choose a reason for hiding this comment

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

The import is correctly using the 'node:' prefix for the built-in crypto module, which is good practice for Node.js 16+.

Copilot uses AI. Check for mistakes.
@stwiname stwiname merged commit da14a89 into main Sep 15, 2025
1 of 3 checks passed
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.

2 participants