Skip to content

Support Lit-claimed ORCID sandbox accounts - #100

Open
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid
Open

Support Lit-claimed ORCID sandbox accounts#100
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid

Conversation

@efstajas

Copy link
Copy Markdown
Contributor

Summary

  • Recognizes sourceId=4 (Lit oracle) as a valid ORCID source ID alongside the existing sourceId=2 (legacy)
  • Adds getAllPossibleOrcidAccountIds to compute both legacy and Lit account IDs for a given ORCID, enabling backwards-compatible resolution
  • Updates the orcidLinkedIdentityByOrcid resolver to try all candidate account IDs, returning the first match

Context: The Lit oracle creates sandbox ORCID accounts with calcAccountId(4, "0009-...") (sourceId=4, plain ORCID iD), while legacy accounts use calcAccountId(2, "sandbox-0009-...") (sourceId=2, prefixed name). These produce different on-chain account IDs. The resolver now tries both so either type of account can be resolved.

Test plan

  • Verify Lit-claimed sandbox ORCID accounts resolve correctly via orcidLinkedIdentityByOrcid
  • Verify legacy sandbox ORCID accounts still resolve correctly
  • Verify production ORCID accounts are unaffected

Lit oracle uses sourceId=4 with plain ORCID iDs for sandbox accounts,
while legacy accounts use sourceId=2 with "sandbox-" prefixed names.
This produces different on-chain account IDs.
- Update orcidAccountIdUtils to recognize sourceId=4 as ORCID
- Add getAllPossibleOrcidAccountIds to compute both legacy and Lit
account IDs for backwards-compatible resolution
- Update resolver to try all candidate account IDs
@railway-app
railway-appBottemporarily deployed to Drips App / graphql-api-pr-100 February 15, 2026 13:05 Destroyed
@railway-app

railway-appBot commented Feb 15, 2026

Copy link
Copy Markdown

🚅 Deployed to the graphql-api-pr-100 environment in Drips App

ServiceStatusWebUpdated (UTC)
GraphQL API◻️ Removed (View Logs)WebFeb 16, 2026 at 10:08 am

CopilotAI left a comment

Copy link
Copy Markdown

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 Lit-claimed ORCID sandbox accounts that use a different source ID (sourceId=4) than legacy ORCID accounts (sourceId=2). The Lit oracle creates sandbox accounts with plain ORCID IDs like "0009-...", while legacy sandbox accounts use the "sandbox-0009-..." prefix. The changes enable the resolver to compute and try all possible account IDs for backwards compatibility.

Changes:

  • Introduces ORCID_SOURCE_IDS array [2, 4] replacing the single ORCID_FORGE_ID constant
  • Adds getAllPossibleOrcidAccountIds function to generate both legacy and Lit account ID candidates
  • Updates orcidLinkedIdentityByOrcid resolver to iterate through candidate account IDs and return the first match

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

FileDescription
src/orcid-account/orcidAccountIdUtils.tsReplaces ORCID_FORGE_ID with ORCID_SOURCE_IDS array, updates extractSourceIdFromAccountId to properly decode 7-bit sourceId, and modifies isOrcidAccount to check multiple valid source IDs
src/common/dripsContracts.tsAdds getAllPossibleOrcidAccountIds function to compute both legacy (sourceId=2) and Lit (sourceId=4) account IDs for a given ORCID
src/linked-identity/linkedIdentityResolvers.tsUpdates resolver to try multiple candidate account IDs sequentially, returning first match or falling back to unclaimed entry
tests/linked-identity/linkedIdentityResolvers.test.tsUpdates test mocks to use getAllPossibleOrcidAccountIds instead of getCrossChainOrcidAccountIdByOrcidId

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +125
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

While getAllPossibleOrcidAccountIds always returns at least one account ID (the legacy sourceId=2 variant), the code would be more robust with an explicit check before accessing candidateAccountIds[0] on line 124. Consider adding an assertion or throw statement if the array is unexpectedly empty, or adding a comment explaining why the array is guaranteed to be non-empty. This improves code maintainability and makes the assumption explicit.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +121
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The resolver now makes sequential database queries for each candidate account ID until a match is found. In the worst case with sandbox ORCIDs, this means two sequential database lookups per request. For better performance, consider fetching all candidate account IDs in a single batched query using SQL IN clause or Promise.all with individual queries, then returning the first non-null result. This would reduce latency, especially on high-latency database connections.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +8
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The comment describing sourceId=2 as "regular ORCID" is misleading. According to the PR description and the code in getAllPossibleOrcidAccountIds, sourceId=2 is used for both regular production ORCID accounts AND legacy sandbox accounts (with "sandbox-" prefix). The comment should clarify that sourceId=2 is used for all legacy ORCID accounts (both production and sandbox with prefix), while sourceId=4 is specifically for Lit-claimed sandbox accounts without the prefix.

Suggested change
*-2: regularORCID
*-4: sandboxORCID(LitoracleusesaseparatesourceIdinsteadofanameprefix)
*-2: legacyORCIDaccounts(bothproductionORCIDandlegacysandboxaccountswith"sandbox-"prefix)
*-4: Lit-claimedsandboxORCIDaccountswithoutthe "sandbox-" prefix (use separate sourceId instead)

Copilot uses AI. Check for mistakes.
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)
*/
export const ORCID_SOURCE_IDS = [2, 4];

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The test file tests/orcid-account/orcidAccountIdUtils.test.ts imports ORCID_FORGE_ID which was removed in this PR and replaced with ORCID_SOURCE_IDS. This change will break the existing test suite when this PR is merged. The test file needs to be updated to import and test ORCID_SOURCE_IDS instead, and the test should verify that it contains both 2 and 4.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to +297
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The function getAllPossibleOrcidAccountIds always adds sourceId=4 (Lit sandbox) candidates for production ORCID IDs that don't start with "sandbox-". However, based on the PR description, sourceId=4 is specifically for Lit-claimed sandbox accounts. This means for production ORCID IDs like "0000-0002-1825-0097", the function will compute and return a sourceId=4 account ID that would never be valid. Consider only computing the sourceId=4 account ID when the orcidId starts with "sandbox-" or when it looks like a sandbox ORCID pattern (starts with "0009-").

Suggested change
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
// Only compute this for sandbox-style ORCID iDs:
// - Legacy sandbox IDs: start with "sandbox-"
// - Sandbox ORCID pattern: starts with "0009-"
if(orcidId.startsWith('sandbox-')||orcidId.startsWith('0009-')){
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
}

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +301
export async function getAllPossibleOrcidAccountIds(
orcidId: string,
chainsToQuery: DbSchema[],
): Promise<RepoDriverId[]> {
const availableChain = chainsToQuery.find(
(chain) =>
dripsContracts[dbSchemaToChain[chain]] &&
dripsContracts[dbSchemaToChain[chain]]!.repoDriver,
);

if (!availableChain) {
throw new Error('No available chain with initialized contracts.');
}

const { repoDriver } = dripsContracts[dbSchemaToChain[availableChain]]!;

const accountIds: RepoDriverId[] = [];

// Legacy account ID: sourceId=2, with the orcid string as provided
// (may include "sandbox-" prefix for legacy sandbox accounts)
const legacyAccountId = (
await repoDriver.calcAccountId(2, ethers.toUtf8Bytes(orcidId))
).toString() as RepoDriverId;
accountIds.push(legacyAccountId);

// Lit sandbox account ID: sourceId=4, with plain ORCID (no "sandbox-" prefix)
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);
}

return accountIds;
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The new function getAllPossibleOrcidAccountIds lacks direct unit test coverage. Given that this codebase uses comprehensive automated testing with vitest, and this is a critical function that handles ORCID account ID generation with complex logic for backwards compatibility, it should have dedicated unit tests. Consider adding tests that verify: 1) production ORCID IDs generate correct account IDs, 2) sandbox ORCID IDs with "sandbox-" prefix are handled correctly, 3) the deduplication logic works when both sourceIds produce the same account ID, and 4) error handling for unavailable chains.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +125
const candidateAccountIds = await getAllPossibleOrcidAccountIds(orcid, [
chainToDbSchema[chain],
]);

// Try each candidate account ID, return the first match found
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

return identity
? toGqlLinkedIdentity(identity)
: toFakeUnclaimedOrcid(orcid, orcidAccountId, chain);
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The existing integration tests only mock getAllPossibleOrcidAccountIds to return a single account ID. To properly test the new multi-candidate resolution logic, additional test cases should be added that mock getAllPossibleOrcidAccountIds to return multiple candidate IDs and verify: 1) the resolver tries each candidate in order, 2) it returns the first match found, 3) when no match is found, it correctly falls back to toFakeUnclaimedOrcid with the first candidate. These scenarios are critical for ensuring the backwards compatibility feature works correctly.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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

@efstajas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Support Lit-claimed ORCID sandbox accounts by efstajas · Pull Request #100 · drips-network/graphql-api · GitHub
Skip to content

Support Lit-claimed ORCID sandbox accounts - #100

Open
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid
Open

Support Lit-claimed ORCID sandbox accounts#100
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid

Conversation

@efstajas

Copy link
Copy Markdown
Contributor

Summary

  • Recognizes sourceId=4 (Lit oracle) as a valid ORCID source ID alongside the existing sourceId=2 (legacy)
  • Adds getAllPossibleOrcidAccountIds to compute both legacy and Lit account IDs for a given ORCID, enabling backwards-compatible resolution
  • Updates the orcidLinkedIdentityByOrcid resolver to try all candidate account IDs, returning the first match

Context: The Lit oracle creates sandbox ORCID accounts with calcAccountId(4, "0009-...") (sourceId=4, plain ORCID iD), while legacy accounts use calcAccountId(2, "sandbox-0009-...") (sourceId=2, prefixed name). These produce different on-chain account IDs. The resolver now tries both so either type of account can be resolved.

Test plan

  • Verify Lit-claimed sandbox ORCID accounts resolve correctly via orcidLinkedIdentityByOrcid
  • Verify legacy sandbox ORCID accounts still resolve correctly
  • Verify production ORCID accounts are unaffected

Lit oracle uses sourceId=4 with plain ORCID iDs for sandbox accounts,
while legacy accounts use sourceId=2 with "sandbox-" prefixed names.
This produces different on-chain account IDs.
- Update orcidAccountIdUtils to recognize sourceId=4 as ORCID
- Add getAllPossibleOrcidAccountIds to compute both legacy and Lit
account IDs for backwards-compatible resolution
- Update resolver to try all candidate account IDs
@railway-app
railway-appBottemporarily deployed to Drips App / graphql-api-pr-100 February 15, 2026 13:05 Destroyed
@railway-app

railway-appBot commented Feb 15, 2026

Copy link
Copy Markdown

🚅 Deployed to the graphql-api-pr-100 environment in Drips App

ServiceStatusWebUpdated (UTC)
GraphQL API◻️ Removed (View Logs)WebFeb 16, 2026 at 10:08 am

CopilotAI left a comment

Copy link
Copy Markdown

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 Lit-claimed ORCID sandbox accounts that use a different source ID (sourceId=4) than legacy ORCID accounts (sourceId=2). The Lit oracle creates sandbox accounts with plain ORCID IDs like "0009-...", while legacy sandbox accounts use the "sandbox-0009-..." prefix. The changes enable the resolver to compute and try all possible account IDs for backwards compatibility.

Changes:

  • Introduces ORCID_SOURCE_IDS array [2, 4] replacing the single ORCID_FORGE_ID constant
  • Adds getAllPossibleOrcidAccountIds function to generate both legacy and Lit account ID candidates
  • Updates orcidLinkedIdentityByOrcid resolver to iterate through candidate account IDs and return the first match

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

FileDescription
src/orcid-account/orcidAccountIdUtils.tsReplaces ORCID_FORGE_ID with ORCID_SOURCE_IDS array, updates extractSourceIdFromAccountId to properly decode 7-bit sourceId, and modifies isOrcidAccount to check multiple valid source IDs
src/common/dripsContracts.tsAdds getAllPossibleOrcidAccountIds function to compute both legacy (sourceId=2) and Lit (sourceId=4) account IDs for a given ORCID
src/linked-identity/linkedIdentityResolvers.tsUpdates resolver to try multiple candidate account IDs sequentially, returning first match or falling back to unclaimed entry
tests/linked-identity/linkedIdentityResolvers.test.tsUpdates test mocks to use getAllPossibleOrcidAccountIds instead of getCrossChainOrcidAccountIdByOrcidId

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +125
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

While getAllPossibleOrcidAccountIds always returns at least one account ID (the legacy sourceId=2 variant), the code would be more robust with an explicit check before accessing candidateAccountIds[0] on line 124. Consider adding an assertion or throw statement if the array is unexpectedly empty, or adding a comment explaining why the array is guaranteed to be non-empty. This improves code maintainability and makes the assumption explicit.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +121
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The resolver now makes sequential database queries for each candidate account ID until a match is found. In the worst case with sandbox ORCIDs, this means two sequential database lookups per request. For better performance, consider fetching all candidate account IDs in a single batched query using SQL IN clause or Promise.all with individual queries, then returning the first non-null result. This would reduce latency, especially on high-latency database connections.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +8
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The comment describing sourceId=2 as "regular ORCID" is misleading. According to the PR description and the code in getAllPossibleOrcidAccountIds, sourceId=2 is used for both regular production ORCID accounts AND legacy sandbox accounts (with "sandbox-" prefix). The comment should clarify that sourceId=2 is used for all legacy ORCID accounts (both production and sandbox with prefix), while sourceId=4 is specifically for Lit-claimed sandbox accounts without the prefix.

Suggested change
*-2: regularORCID
*-4: sandboxORCID(LitoracleusesaseparatesourceIdinsteadofanameprefix)
*-2: legacyORCIDaccounts(bothproductionORCIDandlegacysandboxaccountswith"sandbox-"prefix)
*-4: Lit-claimedsandboxORCIDaccountswithoutthe "sandbox-" prefix (use separate sourceId instead)

Copilot uses AI. Check for mistakes.
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)
*/
export const ORCID_SOURCE_IDS = [2, 4];

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The test file tests/orcid-account/orcidAccountIdUtils.test.ts imports ORCID_FORGE_ID which was removed in this PR and replaced with ORCID_SOURCE_IDS. This change will break the existing test suite when this PR is merged. The test file needs to be updated to import and test ORCID_SOURCE_IDS instead, and the test should verify that it contains both 2 and 4.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to +297
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The function getAllPossibleOrcidAccountIds always adds sourceId=4 (Lit sandbox) candidates for production ORCID IDs that don't start with "sandbox-". However, based on the PR description, sourceId=4 is specifically for Lit-claimed sandbox accounts. This means for production ORCID IDs like "0000-0002-1825-0097", the function will compute and return a sourceId=4 account ID that would never be valid. Consider only computing the sourceId=4 account ID when the orcidId starts with "sandbox-" or when it looks like a sandbox ORCID pattern (starts with "0009-").

Suggested change
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
// Only compute this for sandbox-style ORCID iDs:
// - Legacy sandbox IDs: start with "sandbox-"
// - Sandbox ORCID pattern: starts with "0009-"
if(orcidId.startsWith('sandbox-')||orcidId.startsWith('0009-')){
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
}

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +301
export async function getAllPossibleOrcidAccountIds(
orcidId: string,
chainsToQuery: DbSchema[],
): Promise<RepoDriverId[]> {
const availableChain = chainsToQuery.find(
(chain) =>
dripsContracts[dbSchemaToChain[chain]] &&
dripsContracts[dbSchemaToChain[chain]]!.repoDriver,
);

if (!availableChain) {
throw new Error('No available chain with initialized contracts.');
}

const { repoDriver } = dripsContracts[dbSchemaToChain[availableChain]]!;

const accountIds: RepoDriverId[] = [];

// Legacy account ID: sourceId=2, with the orcid string as provided
// (may include "sandbox-" prefix for legacy sandbox accounts)
const legacyAccountId = (
await repoDriver.calcAccountId(2, ethers.toUtf8Bytes(orcidId))
).toString() as RepoDriverId;
accountIds.push(legacyAccountId);

// Lit sandbox account ID: sourceId=4, with plain ORCID (no "sandbox-" prefix)
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);
}

return accountIds;
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The new function getAllPossibleOrcidAccountIds lacks direct unit test coverage. Given that this codebase uses comprehensive automated testing with vitest, and this is a critical function that handles ORCID account ID generation with complex logic for backwards compatibility, it should have dedicated unit tests. Consider adding tests that verify: 1) production ORCID IDs generate correct account IDs, 2) sandbox ORCID IDs with "sandbox-" prefix are handled correctly, 3) the deduplication logic works when both sourceIds produce the same account ID, and 4) error handling for unavailable chains.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +125
const candidateAccountIds = await getAllPossibleOrcidAccountIds(orcid, [
chainToDbSchema[chain],
]);

// Try each candidate account ID, return the first match found
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

return identity
? toGqlLinkedIdentity(identity)
: toFakeUnclaimedOrcid(orcid, orcidAccountId, chain);
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The existing integration tests only mock getAllPossibleOrcidAccountIds to return a single account ID. To properly test the new multi-candidate resolution logic, additional test cases should be added that mock getAllPossibleOrcidAccountIds to return multiple candidate IDs and verify: 1) the resolver tries each candidate in order, 2) it returns the first match found, 3) when no match is found, it correctly falls back to toFakeUnclaimedOrcid with the first candidate. These scenarios are critical for ensuring the backwards compatibility feature works correctly.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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

@efstajas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Support Lit-claimed ORCID sandbox accounts by efstajas · Pull Request #100 · drips-network/graphql-api · GitHub
Skip to content

Support Lit-claimed ORCID sandbox accounts - #100

Open
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid
Open

Support Lit-claimed ORCID sandbox accounts#100
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid

Conversation

@efstajas

Copy link
Copy Markdown
Contributor

Summary

  • Recognizes sourceId=4 (Lit oracle) as a valid ORCID source ID alongside the existing sourceId=2 (legacy)
  • Adds getAllPossibleOrcidAccountIds to compute both legacy and Lit account IDs for a given ORCID, enabling backwards-compatible resolution
  • Updates the orcidLinkedIdentityByOrcid resolver to try all candidate account IDs, returning the first match

Context: The Lit oracle creates sandbox ORCID accounts with calcAccountId(4, "0009-...") (sourceId=4, plain ORCID iD), while legacy accounts use calcAccountId(2, "sandbox-0009-...") (sourceId=2, prefixed name). These produce different on-chain account IDs. The resolver now tries both so either type of account can be resolved.

Test plan

  • Verify Lit-claimed sandbox ORCID accounts resolve correctly via orcidLinkedIdentityByOrcid
  • Verify legacy sandbox ORCID accounts still resolve correctly
  • Verify production ORCID accounts are unaffected

Lit oracle uses sourceId=4 with plain ORCID iDs for sandbox accounts,
while legacy accounts use sourceId=2 with "sandbox-" prefixed names.
This produces different on-chain account IDs.
- Update orcidAccountIdUtils to recognize sourceId=4 as ORCID
- Add getAllPossibleOrcidAccountIds to compute both legacy and Lit
account IDs for backwards-compatible resolution
- Update resolver to try all candidate account IDs
@railway-app
railway-appBottemporarily deployed to Drips App / graphql-api-pr-100 February 15, 2026 13:05 Destroyed
@railway-app

railway-appBot commented Feb 15, 2026

Copy link
Copy Markdown

🚅 Deployed to the graphql-api-pr-100 environment in Drips App

ServiceStatusWebUpdated (UTC)
GraphQL API◻️ Removed (View Logs)WebFeb 16, 2026 at 10:08 am

CopilotAI left a comment

Copy link
Copy Markdown

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 Lit-claimed ORCID sandbox accounts that use a different source ID (sourceId=4) than legacy ORCID accounts (sourceId=2). The Lit oracle creates sandbox accounts with plain ORCID IDs like "0009-...", while legacy sandbox accounts use the "sandbox-0009-..." prefix. The changes enable the resolver to compute and try all possible account IDs for backwards compatibility.

Changes:

  • Introduces ORCID_SOURCE_IDS array [2, 4] replacing the single ORCID_FORGE_ID constant
  • Adds getAllPossibleOrcidAccountIds function to generate both legacy and Lit account ID candidates
  • Updates orcidLinkedIdentityByOrcid resolver to iterate through candidate account IDs and return the first match

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

FileDescription
src/orcid-account/orcidAccountIdUtils.tsReplaces ORCID_FORGE_ID with ORCID_SOURCE_IDS array, updates extractSourceIdFromAccountId to properly decode 7-bit sourceId, and modifies isOrcidAccount to check multiple valid source IDs
src/common/dripsContracts.tsAdds getAllPossibleOrcidAccountIds function to compute both legacy (sourceId=2) and Lit (sourceId=4) account IDs for a given ORCID
src/linked-identity/linkedIdentityResolvers.tsUpdates resolver to try multiple candidate account IDs sequentially, returning first match or falling back to unclaimed entry
tests/linked-identity/linkedIdentityResolvers.test.tsUpdates test mocks to use getAllPossibleOrcidAccountIds instead of getCrossChainOrcidAccountIdByOrcidId

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +125
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

While getAllPossibleOrcidAccountIds always returns at least one account ID (the legacy sourceId=2 variant), the code would be more robust with an explicit check before accessing candidateAccountIds[0] on line 124. Consider adding an assertion or throw statement if the array is unexpectedly empty, or adding a comment explaining why the array is guaranteed to be non-empty. This improves code maintainability and makes the assumption explicit.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +121
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The resolver now makes sequential database queries for each candidate account ID until a match is found. In the worst case with sandbox ORCIDs, this means two sequential database lookups per request. For better performance, consider fetching all candidate account IDs in a single batched query using SQL IN clause or Promise.all with individual queries, then returning the first non-null result. This would reduce latency, especially on high-latency database connections.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +8
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The comment describing sourceId=2 as "regular ORCID" is misleading. According to the PR description and the code in getAllPossibleOrcidAccountIds, sourceId=2 is used for both regular production ORCID accounts AND legacy sandbox accounts (with "sandbox-" prefix). The comment should clarify that sourceId=2 is used for all legacy ORCID accounts (both production and sandbox with prefix), while sourceId=4 is specifically for Lit-claimed sandbox accounts without the prefix.

Suggested change
*-2: regularORCID
*-4: sandboxORCID(LitoracleusesaseparatesourceIdinsteadofanameprefix)
*-2: legacyORCIDaccounts(bothproductionORCIDandlegacysandboxaccountswith"sandbox-"prefix)
*-4: Lit-claimedsandboxORCIDaccountswithoutthe "sandbox-" prefix (use separate sourceId instead)

Copilot uses AI. Check for mistakes.
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)
*/
export const ORCID_SOURCE_IDS = [2, 4];

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The test file tests/orcid-account/orcidAccountIdUtils.test.ts imports ORCID_FORGE_ID which was removed in this PR and replaced with ORCID_SOURCE_IDS. This change will break the existing test suite when this PR is merged. The test file needs to be updated to import and test ORCID_SOURCE_IDS instead, and the test should verify that it contains both 2 and 4.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to +297
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The function getAllPossibleOrcidAccountIds always adds sourceId=4 (Lit sandbox) candidates for production ORCID IDs that don't start with "sandbox-". However, based on the PR description, sourceId=4 is specifically for Lit-claimed sandbox accounts. This means for production ORCID IDs like "0000-0002-1825-0097", the function will compute and return a sourceId=4 account ID that would never be valid. Consider only computing the sourceId=4 account ID when the orcidId starts with "sandbox-" or when it looks like a sandbox ORCID pattern (starts with "0009-").

Suggested change
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
// Only compute this for sandbox-style ORCID iDs:
// - Legacy sandbox IDs: start with "sandbox-"
// - Sandbox ORCID pattern: starts with "0009-"
if(orcidId.startsWith('sandbox-')||orcidId.startsWith('0009-')){
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
}

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +301
export async function getAllPossibleOrcidAccountIds(
orcidId: string,
chainsToQuery: DbSchema[],
): Promise<RepoDriverId[]> {
const availableChain = chainsToQuery.find(
(chain) =>
dripsContracts[dbSchemaToChain[chain]] &&
dripsContracts[dbSchemaToChain[chain]]!.repoDriver,
);

if (!availableChain) {
throw new Error('No available chain with initialized contracts.');
}

const { repoDriver } = dripsContracts[dbSchemaToChain[availableChain]]!;

const accountIds: RepoDriverId[] = [];

// Legacy account ID: sourceId=2, with the orcid string as provided
// (may include "sandbox-" prefix for legacy sandbox accounts)
const legacyAccountId = (
await repoDriver.calcAccountId(2, ethers.toUtf8Bytes(orcidId))
).toString() as RepoDriverId;
accountIds.push(legacyAccountId);

// Lit sandbox account ID: sourceId=4, with plain ORCID (no "sandbox-" prefix)
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);
}

return accountIds;
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The new function getAllPossibleOrcidAccountIds lacks direct unit test coverage. Given that this codebase uses comprehensive automated testing with vitest, and this is a critical function that handles ORCID account ID generation with complex logic for backwards compatibility, it should have dedicated unit tests. Consider adding tests that verify: 1) production ORCID IDs generate correct account IDs, 2) sandbox ORCID IDs with "sandbox-" prefix are handled correctly, 3) the deduplication logic works when both sourceIds produce the same account ID, and 4) error handling for unavailable chains.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +125
const candidateAccountIds = await getAllPossibleOrcidAccountIds(orcid, [
chainToDbSchema[chain],
]);

// Try each candidate account ID, return the first match found
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

return identity
? toGqlLinkedIdentity(identity)
: toFakeUnclaimedOrcid(orcid, orcidAccountId, chain);
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The existing integration tests only mock getAllPossibleOrcidAccountIds to return a single account ID. To properly test the new multi-candidate resolution logic, additional test cases should be added that mock getAllPossibleOrcidAccountIds to return multiple candidate IDs and verify: 1) the resolver tries each candidate in order, 2) it returns the first match found, 3) when no match is found, it correctly falls back to toFakeUnclaimedOrcid with the first candidate. These scenarios are critical for ensuring the backwards compatibility feature works correctly.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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

@efstajas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Support Lit-claimed ORCID sandbox accounts by efstajas · Pull Request #100 · drips-network/graphql-api · GitHub
Skip to content

Support Lit-claimed ORCID sandbox accounts - #100

Open
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid
Open

Support Lit-claimed ORCID sandbox accounts#100
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid

Conversation

@efstajas

Copy link
Copy Markdown
Contributor

Summary

  • Recognizes sourceId=4 (Lit oracle) as a valid ORCID source ID alongside the existing sourceId=2 (legacy)
  • Adds getAllPossibleOrcidAccountIds to compute both legacy and Lit account IDs for a given ORCID, enabling backwards-compatible resolution
  • Updates the orcidLinkedIdentityByOrcid resolver to try all candidate account IDs, returning the first match

Context: The Lit oracle creates sandbox ORCID accounts with calcAccountId(4, "0009-...") (sourceId=4, plain ORCID iD), while legacy accounts use calcAccountId(2, "sandbox-0009-...") (sourceId=2, prefixed name). These produce different on-chain account IDs. The resolver now tries both so either type of account can be resolved.

Test plan

  • Verify Lit-claimed sandbox ORCID accounts resolve correctly via orcidLinkedIdentityByOrcid
  • Verify legacy sandbox ORCID accounts still resolve correctly
  • Verify production ORCID accounts are unaffected

Lit oracle uses sourceId=4 with plain ORCID iDs for sandbox accounts,
while legacy accounts use sourceId=2 with "sandbox-" prefixed names.
This produces different on-chain account IDs.
- Update orcidAccountIdUtils to recognize sourceId=4 as ORCID
- Add getAllPossibleOrcidAccountIds to compute both legacy and Lit
account IDs for backwards-compatible resolution
- Update resolver to try all candidate account IDs
@railway-app
railway-appBottemporarily deployed to Drips App / graphql-api-pr-100 February 15, 2026 13:05 Destroyed
@railway-app

railway-appBot commented Feb 15, 2026

Copy link
Copy Markdown

🚅 Deployed to the graphql-api-pr-100 environment in Drips App

ServiceStatusWebUpdated (UTC)
GraphQL API◻️ Removed (View Logs)WebFeb 16, 2026 at 10:08 am

CopilotAI left a comment

Copy link
Copy Markdown

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 Lit-claimed ORCID sandbox accounts that use a different source ID (sourceId=4) than legacy ORCID accounts (sourceId=2). The Lit oracle creates sandbox accounts with plain ORCID IDs like "0009-...", while legacy sandbox accounts use the "sandbox-0009-..." prefix. The changes enable the resolver to compute and try all possible account IDs for backwards compatibility.

Changes:

  • Introduces ORCID_SOURCE_IDS array [2, 4] replacing the single ORCID_FORGE_ID constant
  • Adds getAllPossibleOrcidAccountIds function to generate both legacy and Lit account ID candidates
  • Updates orcidLinkedIdentityByOrcid resolver to iterate through candidate account IDs and return the first match

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

FileDescription
src/orcid-account/orcidAccountIdUtils.tsReplaces ORCID_FORGE_ID with ORCID_SOURCE_IDS array, updates extractSourceIdFromAccountId to properly decode 7-bit sourceId, and modifies isOrcidAccount to check multiple valid source IDs
src/common/dripsContracts.tsAdds getAllPossibleOrcidAccountIds function to compute both legacy (sourceId=2) and Lit (sourceId=4) account IDs for a given ORCID
src/linked-identity/linkedIdentityResolvers.tsUpdates resolver to try multiple candidate account IDs sequentially, returning first match or falling back to unclaimed entry
tests/linked-identity/linkedIdentityResolvers.test.tsUpdates test mocks to use getAllPossibleOrcidAccountIds instead of getCrossChainOrcidAccountIdByOrcidId

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +125
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

While getAllPossibleOrcidAccountIds always returns at least one account ID (the legacy sourceId=2 variant), the code would be more robust with an explicit check before accessing candidateAccountIds[0] on line 124. Consider adding an assertion or throw statement if the array is unexpectedly empty, or adding a comment explaining why the array is guaranteed to be non-empty. This improves code maintainability and makes the assumption explicit.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +121
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The resolver now makes sequential database queries for each candidate account ID until a match is found. In the worst case with sandbox ORCIDs, this means two sequential database lookups per request. For better performance, consider fetching all candidate account IDs in a single batched query using SQL IN clause or Promise.all with individual queries, then returning the first non-null result. This would reduce latency, especially on high-latency database connections.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +8
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The comment describing sourceId=2 as "regular ORCID" is misleading. According to the PR description and the code in getAllPossibleOrcidAccountIds, sourceId=2 is used for both regular production ORCID accounts AND legacy sandbox accounts (with "sandbox-" prefix). The comment should clarify that sourceId=2 is used for all legacy ORCID accounts (both production and sandbox with prefix), while sourceId=4 is specifically for Lit-claimed sandbox accounts without the prefix.

Suggested change
*-2: regularORCID
*-4: sandboxORCID(LitoracleusesaseparatesourceIdinsteadofanameprefix)
*-2: legacyORCIDaccounts(bothproductionORCIDandlegacysandboxaccountswith"sandbox-"prefix)
*-4: Lit-claimedsandboxORCIDaccountswithoutthe "sandbox-" prefix (use separate sourceId instead)

Copilot uses AI. Check for mistakes.
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)
*/
export const ORCID_SOURCE_IDS = [2, 4];

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The test file tests/orcid-account/orcidAccountIdUtils.test.ts imports ORCID_FORGE_ID which was removed in this PR and replaced with ORCID_SOURCE_IDS. This change will break the existing test suite when this PR is merged. The test file needs to be updated to import and test ORCID_SOURCE_IDS instead, and the test should verify that it contains both 2 and 4.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to +297
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The function getAllPossibleOrcidAccountIds always adds sourceId=4 (Lit sandbox) candidates for production ORCID IDs that don't start with "sandbox-". However, based on the PR description, sourceId=4 is specifically for Lit-claimed sandbox accounts. This means for production ORCID IDs like "0000-0002-1825-0097", the function will compute and return a sourceId=4 account ID that would never be valid. Consider only computing the sourceId=4 account ID when the orcidId starts with "sandbox-" or when it looks like a sandbox ORCID pattern (starts with "0009-").

Suggested change
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
// Only compute this for sandbox-style ORCID iDs:
// - Legacy sandbox IDs: start with "sandbox-"
// - Sandbox ORCID pattern: starts with "0009-"
if(orcidId.startsWith('sandbox-')||orcidId.startsWith('0009-')){
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
}

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +301
export async function getAllPossibleOrcidAccountIds(
orcidId: string,
chainsToQuery: DbSchema[],
): Promise<RepoDriverId[]> {
const availableChain = chainsToQuery.find(
(chain) =>
dripsContracts[dbSchemaToChain[chain]] &&
dripsContracts[dbSchemaToChain[chain]]!.repoDriver,
);

if (!availableChain) {
throw new Error('No available chain with initialized contracts.');
}

const { repoDriver } = dripsContracts[dbSchemaToChain[availableChain]]!;

const accountIds: RepoDriverId[] = [];

// Legacy account ID: sourceId=2, with the orcid string as provided
// (may include "sandbox-" prefix for legacy sandbox accounts)
const legacyAccountId = (
await repoDriver.calcAccountId(2, ethers.toUtf8Bytes(orcidId))
).toString() as RepoDriverId;
accountIds.push(legacyAccountId);

// Lit sandbox account ID: sourceId=4, with plain ORCID (no "sandbox-" prefix)
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);
}

return accountIds;
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The new function getAllPossibleOrcidAccountIds lacks direct unit test coverage. Given that this codebase uses comprehensive automated testing with vitest, and this is a critical function that handles ORCID account ID generation with complex logic for backwards compatibility, it should have dedicated unit tests. Consider adding tests that verify: 1) production ORCID IDs generate correct account IDs, 2) sandbox ORCID IDs with "sandbox-" prefix are handled correctly, 3) the deduplication logic works when both sourceIds produce the same account ID, and 4) error handling for unavailable chains.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +125
const candidateAccountIds = await getAllPossibleOrcidAccountIds(orcid, [
chainToDbSchema[chain],
]);

// Try each candidate account ID, return the first match found
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

return identity
? toGqlLinkedIdentity(identity)
: toFakeUnclaimedOrcid(orcid, orcidAccountId, chain);
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The existing integration tests only mock getAllPossibleOrcidAccountIds to return a single account ID. To properly test the new multi-candidate resolution logic, additional test cases should be added that mock getAllPossibleOrcidAccountIds to return multiple candidate IDs and verify: 1) the resolver tries each candidate in order, 2) it returns the first match found, 3) when no match is found, it correctly falls back to toFakeUnclaimedOrcid with the first candidate. These scenarios are critical for ensuring the backwards compatibility feature works correctly.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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

@efstajas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Support Lit-claimed ORCID sandbox accounts by efstajas · Pull Request #100 · drips-network/graphql-api · GitHub
Skip to content

Support Lit-claimed ORCID sandbox accounts - #100

Open
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid
Open

Support Lit-claimed ORCID sandbox accounts#100
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid

Conversation

@efstajas

Copy link
Copy Markdown
Contributor

Summary

  • Recognizes sourceId=4 (Lit oracle) as a valid ORCID source ID alongside the existing sourceId=2 (legacy)
  • Adds getAllPossibleOrcidAccountIds to compute both legacy and Lit account IDs for a given ORCID, enabling backwards-compatible resolution
  • Updates the orcidLinkedIdentityByOrcid resolver to try all candidate account IDs, returning the first match

Context: The Lit oracle creates sandbox ORCID accounts with calcAccountId(4, "0009-...") (sourceId=4, plain ORCID iD), while legacy accounts use calcAccountId(2, "sandbox-0009-...") (sourceId=2, prefixed name). These produce different on-chain account IDs. The resolver now tries both so either type of account can be resolved.

Test plan

  • Verify Lit-claimed sandbox ORCID accounts resolve correctly via orcidLinkedIdentityByOrcid
  • Verify legacy sandbox ORCID accounts still resolve correctly
  • Verify production ORCID accounts are unaffected

Lit oracle uses sourceId=4 with plain ORCID iDs for sandbox accounts,
while legacy accounts use sourceId=2 with "sandbox-" prefixed names.
This produces different on-chain account IDs.
- Update orcidAccountIdUtils to recognize sourceId=4 as ORCID
- Add getAllPossibleOrcidAccountIds to compute both legacy and Lit
account IDs for backwards-compatible resolution
- Update resolver to try all candidate account IDs
@railway-app
railway-appBottemporarily deployed to Drips App / graphql-api-pr-100 February 15, 2026 13:05 Destroyed
@railway-app

railway-appBot commented Feb 15, 2026

Copy link
Copy Markdown

🚅 Deployed to the graphql-api-pr-100 environment in Drips App

ServiceStatusWebUpdated (UTC)
GraphQL API◻️ Removed (View Logs)WebFeb 16, 2026 at 10:08 am

CopilotAI left a comment

Copy link
Copy Markdown

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 Lit-claimed ORCID sandbox accounts that use a different source ID (sourceId=4) than legacy ORCID accounts (sourceId=2). The Lit oracle creates sandbox accounts with plain ORCID IDs like "0009-...", while legacy sandbox accounts use the "sandbox-0009-..." prefix. The changes enable the resolver to compute and try all possible account IDs for backwards compatibility.

Changes:

  • Introduces ORCID_SOURCE_IDS array [2, 4] replacing the single ORCID_FORGE_ID constant
  • Adds getAllPossibleOrcidAccountIds function to generate both legacy and Lit account ID candidates
  • Updates orcidLinkedIdentityByOrcid resolver to iterate through candidate account IDs and return the first match

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

FileDescription
src/orcid-account/orcidAccountIdUtils.tsReplaces ORCID_FORGE_ID with ORCID_SOURCE_IDS array, updates extractSourceIdFromAccountId to properly decode 7-bit sourceId, and modifies isOrcidAccount to check multiple valid source IDs
src/common/dripsContracts.tsAdds getAllPossibleOrcidAccountIds function to compute both legacy (sourceId=2) and Lit (sourceId=4) account IDs for a given ORCID
src/linked-identity/linkedIdentityResolvers.tsUpdates resolver to try multiple candidate account IDs sequentially, returning first match or falling back to unclaimed entry
tests/linked-identity/linkedIdentityResolvers.test.tsUpdates test mocks to use getAllPossibleOrcidAccountIds instead of getCrossChainOrcidAccountIdByOrcidId

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +125
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

While getAllPossibleOrcidAccountIds always returns at least one account ID (the legacy sourceId=2 variant), the code would be more robust with an explicit check before accessing candidateAccountIds[0] on line 124. Consider adding an assertion or throw statement if the array is unexpectedly empty, or adding a comment explaining why the array is guaranteed to be non-empty. This improves code maintainability and makes the assumption explicit.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +121
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The resolver now makes sequential database queries for each candidate account ID until a match is found. In the worst case with sandbox ORCIDs, this means two sequential database lookups per request. For better performance, consider fetching all candidate account IDs in a single batched query using SQL IN clause or Promise.all with individual queries, then returning the first non-null result. This would reduce latency, especially on high-latency database connections.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +8
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The comment describing sourceId=2 as "regular ORCID" is misleading. According to the PR description and the code in getAllPossibleOrcidAccountIds, sourceId=2 is used for both regular production ORCID accounts AND legacy sandbox accounts (with "sandbox-" prefix). The comment should clarify that sourceId=2 is used for all legacy ORCID accounts (both production and sandbox with prefix), while sourceId=4 is specifically for Lit-claimed sandbox accounts without the prefix.

Suggested change
*-2: regularORCID
*-4: sandboxORCID(LitoracleusesaseparatesourceIdinsteadofanameprefix)
*-2: legacyORCIDaccounts(bothproductionORCIDandlegacysandboxaccountswith"sandbox-"prefix)
*-4: Lit-claimedsandboxORCIDaccountswithoutthe "sandbox-" prefix (use separate sourceId instead)

Copilot uses AI. Check for mistakes.
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)
*/
export const ORCID_SOURCE_IDS = [2, 4];

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The test file tests/orcid-account/orcidAccountIdUtils.test.ts imports ORCID_FORGE_ID which was removed in this PR and replaced with ORCID_SOURCE_IDS. This change will break the existing test suite when this PR is merged. The test file needs to be updated to import and test ORCID_SOURCE_IDS instead, and the test should verify that it contains both 2 and 4.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to +297
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The function getAllPossibleOrcidAccountIds always adds sourceId=4 (Lit sandbox) candidates for production ORCID IDs that don't start with "sandbox-". However, based on the PR description, sourceId=4 is specifically for Lit-claimed sandbox accounts. This means for production ORCID IDs like "0000-0002-1825-0097", the function will compute and return a sourceId=4 account ID that would never be valid. Consider only computing the sourceId=4 account ID when the orcidId starts with "sandbox-" or when it looks like a sandbox ORCID pattern (starts with "0009-").

Suggested change
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
// Only compute this for sandbox-style ORCID iDs:
// - Legacy sandbox IDs: start with "sandbox-"
// - Sandbox ORCID pattern: starts with "0009-"
if(orcidId.startsWith('sandbox-')||orcidId.startsWith('0009-')){
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
}

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +301
export async function getAllPossibleOrcidAccountIds(
orcidId: string,
chainsToQuery: DbSchema[],
): Promise<RepoDriverId[]> {
const availableChain = chainsToQuery.find(
(chain) =>
dripsContracts[dbSchemaToChain[chain]] &&
dripsContracts[dbSchemaToChain[chain]]!.repoDriver,
);

if (!availableChain) {
throw new Error('No available chain with initialized contracts.');
}

const { repoDriver } = dripsContracts[dbSchemaToChain[availableChain]]!;

const accountIds: RepoDriverId[] = [];

// Legacy account ID: sourceId=2, with the orcid string as provided
// (may include "sandbox-" prefix for legacy sandbox accounts)
const legacyAccountId = (
await repoDriver.calcAccountId(2, ethers.toUtf8Bytes(orcidId))
).toString() as RepoDriverId;
accountIds.push(legacyAccountId);

// Lit sandbox account ID: sourceId=4, with plain ORCID (no "sandbox-" prefix)
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);
}

return accountIds;
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The new function getAllPossibleOrcidAccountIds lacks direct unit test coverage. Given that this codebase uses comprehensive automated testing with vitest, and this is a critical function that handles ORCID account ID generation with complex logic for backwards compatibility, it should have dedicated unit tests. Consider adding tests that verify: 1) production ORCID IDs generate correct account IDs, 2) sandbox ORCID IDs with "sandbox-" prefix are handled correctly, 3) the deduplication logic works when both sourceIds produce the same account ID, and 4) error handling for unavailable chains.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +125
const candidateAccountIds = await getAllPossibleOrcidAccountIds(orcid, [
chainToDbSchema[chain],
]);

// Try each candidate account ID, return the first match found
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

return identity
? toGqlLinkedIdentity(identity)
: toFakeUnclaimedOrcid(orcid, orcidAccountId, chain);
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The existing integration tests only mock getAllPossibleOrcidAccountIds to return a single account ID. To properly test the new multi-candidate resolution logic, additional test cases should be added that mock getAllPossibleOrcidAccountIds to return multiple candidate IDs and verify: 1) the resolver tries each candidate in order, 2) it returns the first match found, 3) when no match is found, it correctly falls back to toFakeUnclaimedOrcid with the first candidate. These scenarios are critical for ensuring the backwards compatibility feature works correctly.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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

@efstajas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Support Lit-claimed ORCID sandbox accounts by efstajas · Pull Request #100 · drips-network/graphql-api · GitHub
Skip to content

Support Lit-claimed ORCID sandbox accounts - #100

Open
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid
Open

Support Lit-claimed ORCID sandbox accounts#100
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid

Conversation

@efstajas

Copy link
Copy Markdown
Contributor

Summary

  • Recognizes sourceId=4 (Lit oracle) as a valid ORCID source ID alongside the existing sourceId=2 (legacy)
  • Adds getAllPossibleOrcidAccountIds to compute both legacy and Lit account IDs for a given ORCID, enabling backwards-compatible resolution
  • Updates the orcidLinkedIdentityByOrcid resolver to try all candidate account IDs, returning the first match

Context: The Lit oracle creates sandbox ORCID accounts with calcAccountId(4, "0009-...") (sourceId=4, plain ORCID iD), while legacy accounts use calcAccountId(2, "sandbox-0009-...") (sourceId=2, prefixed name). These produce different on-chain account IDs. The resolver now tries both so either type of account can be resolved.

Test plan

  • Verify Lit-claimed sandbox ORCID accounts resolve correctly via orcidLinkedIdentityByOrcid
  • Verify legacy sandbox ORCID accounts still resolve correctly
  • Verify production ORCID accounts are unaffected

Lit oracle uses sourceId=4 with plain ORCID iDs for sandbox accounts,
while legacy accounts use sourceId=2 with "sandbox-" prefixed names.
This produces different on-chain account IDs.
- Update orcidAccountIdUtils to recognize sourceId=4 as ORCID
- Add getAllPossibleOrcidAccountIds to compute both legacy and Lit
account IDs for backwards-compatible resolution
- Update resolver to try all candidate account IDs
@railway-app
railway-appBottemporarily deployed to Drips App / graphql-api-pr-100 February 15, 2026 13:05 Destroyed
@railway-app

railway-appBot commented Feb 15, 2026

Copy link
Copy Markdown

🚅 Deployed to the graphql-api-pr-100 environment in Drips App

ServiceStatusWebUpdated (UTC)
GraphQL API◻️ Removed (View Logs)WebFeb 16, 2026 at 10:08 am

CopilotAI left a comment

Copy link
Copy Markdown

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 Lit-claimed ORCID sandbox accounts that use a different source ID (sourceId=4) than legacy ORCID accounts (sourceId=2). The Lit oracle creates sandbox accounts with plain ORCID IDs like "0009-...", while legacy sandbox accounts use the "sandbox-0009-..." prefix. The changes enable the resolver to compute and try all possible account IDs for backwards compatibility.

Changes:

  • Introduces ORCID_SOURCE_IDS array [2, 4] replacing the single ORCID_FORGE_ID constant
  • Adds getAllPossibleOrcidAccountIds function to generate both legacy and Lit account ID candidates
  • Updates orcidLinkedIdentityByOrcid resolver to iterate through candidate account IDs and return the first match

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

FileDescription
src/orcid-account/orcidAccountIdUtils.tsReplaces ORCID_FORGE_ID with ORCID_SOURCE_IDS array, updates extractSourceIdFromAccountId to properly decode 7-bit sourceId, and modifies isOrcidAccount to check multiple valid source IDs
src/common/dripsContracts.tsAdds getAllPossibleOrcidAccountIds function to compute both legacy (sourceId=2) and Lit (sourceId=4) account IDs for a given ORCID
src/linked-identity/linkedIdentityResolvers.tsUpdates resolver to try multiple candidate account IDs sequentially, returning first match or falling back to unclaimed entry
tests/linked-identity/linkedIdentityResolvers.test.tsUpdates test mocks to use getAllPossibleOrcidAccountIds instead of getCrossChainOrcidAccountIdByOrcidId

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +125
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

While getAllPossibleOrcidAccountIds always returns at least one account ID (the legacy sourceId=2 variant), the code would be more robust with an explicit check before accessing candidateAccountIds[0] on line 124. Consider adding an assertion or throw statement if the array is unexpectedly empty, or adding a comment explaining why the array is guaranteed to be non-empty. This improves code maintainability and makes the assumption explicit.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +121
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The resolver now makes sequential database queries for each candidate account ID until a match is found. In the worst case with sandbox ORCIDs, this means two sequential database lookups per request. For better performance, consider fetching all candidate account IDs in a single batched query using SQL IN clause or Promise.all with individual queries, then returning the first non-null result. This would reduce latency, especially on high-latency database connections.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +8
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The comment describing sourceId=2 as "regular ORCID" is misleading. According to the PR description and the code in getAllPossibleOrcidAccountIds, sourceId=2 is used for both regular production ORCID accounts AND legacy sandbox accounts (with "sandbox-" prefix). The comment should clarify that sourceId=2 is used for all legacy ORCID accounts (both production and sandbox with prefix), while sourceId=4 is specifically for Lit-claimed sandbox accounts without the prefix.

Suggested change
*-2: regularORCID
*-4: sandboxORCID(LitoracleusesaseparatesourceIdinsteadofanameprefix)
*-2: legacyORCIDaccounts(bothproductionORCIDandlegacysandboxaccountswith"sandbox-"prefix)
*-4: Lit-claimedsandboxORCIDaccountswithoutthe "sandbox-" prefix (use separate sourceId instead)

Copilot uses AI. Check for mistakes.
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)
*/
export const ORCID_SOURCE_IDS = [2, 4];

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The test file tests/orcid-account/orcidAccountIdUtils.test.ts imports ORCID_FORGE_ID which was removed in this PR and replaced with ORCID_SOURCE_IDS. This change will break the existing test suite when this PR is merged. The test file needs to be updated to import and test ORCID_SOURCE_IDS instead, and the test should verify that it contains both 2 and 4.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to +297
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The function getAllPossibleOrcidAccountIds always adds sourceId=4 (Lit sandbox) candidates for production ORCID IDs that don't start with "sandbox-". However, based on the PR description, sourceId=4 is specifically for Lit-claimed sandbox accounts. This means for production ORCID IDs like "0000-0002-1825-0097", the function will compute and return a sourceId=4 account ID that would never be valid. Consider only computing the sourceId=4 account ID when the orcidId starts with "sandbox-" or when it looks like a sandbox ORCID pattern (starts with "0009-").

Suggested change
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
// Only compute this for sandbox-style ORCID iDs:
// - Legacy sandbox IDs: start with "sandbox-"
// - Sandbox ORCID pattern: starts with "0009-"
if(orcidId.startsWith('sandbox-')||orcidId.startsWith('0009-')){
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
}

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +301
export async function getAllPossibleOrcidAccountIds(
orcidId: string,
chainsToQuery: DbSchema[],
): Promise<RepoDriverId[]> {
const availableChain = chainsToQuery.find(
(chain) =>
dripsContracts[dbSchemaToChain[chain]] &&
dripsContracts[dbSchemaToChain[chain]]!.repoDriver,
);

if (!availableChain) {
throw new Error('No available chain with initialized contracts.');
}

const { repoDriver } = dripsContracts[dbSchemaToChain[availableChain]]!;

const accountIds: RepoDriverId[] = [];

// Legacy account ID: sourceId=2, with the orcid string as provided
// (may include "sandbox-" prefix for legacy sandbox accounts)
const legacyAccountId = (
await repoDriver.calcAccountId(2, ethers.toUtf8Bytes(orcidId))
).toString() as RepoDriverId;
accountIds.push(legacyAccountId);

// Lit sandbox account ID: sourceId=4, with plain ORCID (no "sandbox-" prefix)
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);
}

return accountIds;
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The new function getAllPossibleOrcidAccountIds lacks direct unit test coverage. Given that this codebase uses comprehensive automated testing with vitest, and this is a critical function that handles ORCID account ID generation with complex logic for backwards compatibility, it should have dedicated unit tests. Consider adding tests that verify: 1) production ORCID IDs generate correct account IDs, 2) sandbox ORCID IDs with "sandbox-" prefix are handled correctly, 3) the deduplication logic works when both sourceIds produce the same account ID, and 4) error handling for unavailable chains.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +125
const candidateAccountIds = await getAllPossibleOrcidAccountIds(orcid, [
chainToDbSchema[chain],
]);

// Try each candidate account ID, return the first match found
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

return identity
? toGqlLinkedIdentity(identity)
: toFakeUnclaimedOrcid(orcid, orcidAccountId, chain);
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The existing integration tests only mock getAllPossibleOrcidAccountIds to return a single account ID. To properly test the new multi-candidate resolution logic, additional test cases should be added that mock getAllPossibleOrcidAccountIds to return multiple candidate IDs and verify: 1) the resolver tries each candidate in order, 2) it returns the first match found, 3) when no match is found, it correctly falls back to toFakeUnclaimedOrcid with the first candidate. These scenarios are critical for ensuring the backwards compatibility feature works correctly.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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

@efstajas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Support Lit-claimed ORCID sandbox accounts by efstajas · Pull Request #100 · drips-network/graphql-api · GitHub
Skip to content

Support Lit-claimed ORCID sandbox accounts - #100

Open
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid
Open

Support Lit-claimed ORCID sandbox accounts#100
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid

Conversation

@efstajas

Copy link
Copy Markdown
Contributor

Summary

  • Recognizes sourceId=4 (Lit oracle) as a valid ORCID source ID alongside the existing sourceId=2 (legacy)
  • Adds getAllPossibleOrcidAccountIds to compute both legacy and Lit account IDs for a given ORCID, enabling backwards-compatible resolution
  • Updates the orcidLinkedIdentityByOrcid resolver to try all candidate account IDs, returning the first match

Context: The Lit oracle creates sandbox ORCID accounts with calcAccountId(4, "0009-...") (sourceId=4, plain ORCID iD), while legacy accounts use calcAccountId(2, "sandbox-0009-...") (sourceId=2, prefixed name). These produce different on-chain account IDs. The resolver now tries both so either type of account can be resolved.

Test plan

  • Verify Lit-claimed sandbox ORCID accounts resolve correctly via orcidLinkedIdentityByOrcid
  • Verify legacy sandbox ORCID accounts still resolve correctly
  • Verify production ORCID accounts are unaffected

Lit oracle uses sourceId=4 with plain ORCID iDs for sandbox accounts,
while legacy accounts use sourceId=2 with "sandbox-" prefixed names.
This produces different on-chain account IDs.
- Update orcidAccountIdUtils to recognize sourceId=4 as ORCID
- Add getAllPossibleOrcidAccountIds to compute both legacy and Lit
account IDs for backwards-compatible resolution
- Update resolver to try all candidate account IDs
@railway-app
railway-appBottemporarily deployed to Drips App / graphql-api-pr-100 February 15, 2026 13:05 Destroyed
@railway-app

railway-appBot commented Feb 15, 2026

Copy link
Copy Markdown

🚅 Deployed to the graphql-api-pr-100 environment in Drips App

ServiceStatusWebUpdated (UTC)
GraphQL API◻️ Removed (View Logs)WebFeb 16, 2026 at 10:08 am

CopilotAI left a comment

Copy link
Copy Markdown

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 Lit-claimed ORCID sandbox accounts that use a different source ID (sourceId=4) than legacy ORCID accounts (sourceId=2). The Lit oracle creates sandbox accounts with plain ORCID IDs like "0009-...", while legacy sandbox accounts use the "sandbox-0009-..." prefix. The changes enable the resolver to compute and try all possible account IDs for backwards compatibility.

Changes:

  • Introduces ORCID_SOURCE_IDS array [2, 4] replacing the single ORCID_FORGE_ID constant
  • Adds getAllPossibleOrcidAccountIds function to generate both legacy and Lit account ID candidates
  • Updates orcidLinkedIdentityByOrcid resolver to iterate through candidate account IDs and return the first match

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

FileDescription
src/orcid-account/orcidAccountIdUtils.tsReplaces ORCID_FORGE_ID with ORCID_SOURCE_IDS array, updates extractSourceIdFromAccountId to properly decode 7-bit sourceId, and modifies isOrcidAccount to check multiple valid source IDs
src/common/dripsContracts.tsAdds getAllPossibleOrcidAccountIds function to compute both legacy (sourceId=2) and Lit (sourceId=4) account IDs for a given ORCID
src/linked-identity/linkedIdentityResolvers.tsUpdates resolver to try multiple candidate account IDs sequentially, returning first match or falling back to unclaimed entry
tests/linked-identity/linkedIdentityResolvers.test.tsUpdates test mocks to use getAllPossibleOrcidAccountIds instead of getCrossChainOrcidAccountIdByOrcidId

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +125
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

While getAllPossibleOrcidAccountIds always returns at least one account ID (the legacy sourceId=2 variant), the code would be more robust with an explicit check before accessing candidateAccountIds[0] on line 124. Consider adding an assertion or throw statement if the array is unexpectedly empty, or adding a comment explaining why the array is guaranteed to be non-empty. This improves code maintainability and makes the assumption explicit.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +121
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The resolver now makes sequential database queries for each candidate account ID until a match is found. In the worst case with sandbox ORCIDs, this means two sequential database lookups per request. For better performance, consider fetching all candidate account IDs in a single batched query using SQL IN clause or Promise.all with individual queries, then returning the first non-null result. This would reduce latency, especially on high-latency database connections.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +8
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The comment describing sourceId=2 as "regular ORCID" is misleading. According to the PR description and the code in getAllPossibleOrcidAccountIds, sourceId=2 is used for both regular production ORCID accounts AND legacy sandbox accounts (with "sandbox-" prefix). The comment should clarify that sourceId=2 is used for all legacy ORCID accounts (both production and sandbox with prefix), while sourceId=4 is specifically for Lit-claimed sandbox accounts without the prefix.

Suggested change
*-2: regularORCID
*-4: sandboxORCID(LitoracleusesaseparatesourceIdinsteadofanameprefix)
*-2: legacyORCIDaccounts(bothproductionORCIDandlegacysandboxaccountswith"sandbox-"prefix)
*-4: Lit-claimedsandboxORCIDaccountswithoutthe "sandbox-" prefix (use separate sourceId instead)

Copilot uses AI. Check for mistakes.
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)
*/
export const ORCID_SOURCE_IDS = [2, 4];

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The test file tests/orcid-account/orcidAccountIdUtils.test.ts imports ORCID_FORGE_ID which was removed in this PR and replaced with ORCID_SOURCE_IDS. This change will break the existing test suite when this PR is merged. The test file needs to be updated to import and test ORCID_SOURCE_IDS instead, and the test should verify that it contains both 2 and 4.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to +297
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The function getAllPossibleOrcidAccountIds always adds sourceId=4 (Lit sandbox) candidates for production ORCID IDs that don't start with "sandbox-". However, based on the PR description, sourceId=4 is specifically for Lit-claimed sandbox accounts. This means for production ORCID IDs like "0000-0002-1825-0097", the function will compute and return a sourceId=4 account ID that would never be valid. Consider only computing the sourceId=4 account ID when the orcidId starts with "sandbox-" or when it looks like a sandbox ORCID pattern (starts with "0009-").

Suggested change
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
// Only compute this for sandbox-style ORCID iDs:
// - Legacy sandbox IDs: start with "sandbox-"
// - Sandbox ORCID pattern: starts with "0009-"
if(orcidId.startsWith('sandbox-')||orcidId.startsWith('0009-')){
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
}

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +301
export async function getAllPossibleOrcidAccountIds(
orcidId: string,
chainsToQuery: DbSchema[],
): Promise<RepoDriverId[]> {
const availableChain = chainsToQuery.find(
(chain) =>
dripsContracts[dbSchemaToChain[chain]] &&
dripsContracts[dbSchemaToChain[chain]]!.repoDriver,
);

if (!availableChain) {
throw new Error('No available chain with initialized contracts.');
}

const { repoDriver } = dripsContracts[dbSchemaToChain[availableChain]]!;

const accountIds: RepoDriverId[] = [];

// Legacy account ID: sourceId=2, with the orcid string as provided
// (may include "sandbox-" prefix for legacy sandbox accounts)
const legacyAccountId = (
await repoDriver.calcAccountId(2, ethers.toUtf8Bytes(orcidId))
).toString() as RepoDriverId;
accountIds.push(legacyAccountId);

// Lit sandbox account ID: sourceId=4, with plain ORCID (no "sandbox-" prefix)
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);
}

return accountIds;
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The new function getAllPossibleOrcidAccountIds lacks direct unit test coverage. Given that this codebase uses comprehensive automated testing with vitest, and this is a critical function that handles ORCID account ID generation with complex logic for backwards compatibility, it should have dedicated unit tests. Consider adding tests that verify: 1) production ORCID IDs generate correct account IDs, 2) sandbox ORCID IDs with "sandbox-" prefix are handled correctly, 3) the deduplication logic works when both sourceIds produce the same account ID, and 4) error handling for unavailable chains.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +125
const candidateAccountIds = await getAllPossibleOrcidAccountIds(orcid, [
chainToDbSchema[chain],
]);

// Try each candidate account ID, return the first match found
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

return identity
? toGqlLinkedIdentity(identity)
: toFakeUnclaimedOrcid(orcid, orcidAccountId, chain);
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The existing integration tests only mock getAllPossibleOrcidAccountIds to return a single account ID. To properly test the new multi-candidate resolution logic, additional test cases should be added that mock getAllPossibleOrcidAccountIds to return multiple candidate IDs and verify: 1) the resolver tries each candidate in order, 2) it returns the first match found, 3) when no match is found, it correctly falls back to toFakeUnclaimedOrcid with the first candidate. These scenarios are critical for ensuring the backwards compatibility feature works correctly.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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

@efstajas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Support Lit-claimed ORCID sandbox accounts by efstajas · Pull Request #100 · drips-network/graphql-api · GitHub
Skip to content

Support Lit-claimed ORCID sandbox accounts - #100

Open
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid
Open

Support Lit-claimed ORCID sandbox accounts#100
efstajas wants to merge 1 commit into
mainfrom
jason/support-lit-orcid

Conversation

@efstajas

Copy link
Copy Markdown
Contributor

Summary

  • Recognizes sourceId=4 (Lit oracle) as a valid ORCID source ID alongside the existing sourceId=2 (legacy)
  • Adds getAllPossibleOrcidAccountIds to compute both legacy and Lit account IDs for a given ORCID, enabling backwards-compatible resolution
  • Updates the orcidLinkedIdentityByOrcid resolver to try all candidate account IDs, returning the first match

Context: The Lit oracle creates sandbox ORCID accounts with calcAccountId(4, "0009-...") (sourceId=4, plain ORCID iD), while legacy accounts use calcAccountId(2, "sandbox-0009-...") (sourceId=2, prefixed name). These produce different on-chain account IDs. The resolver now tries both so either type of account can be resolved.

Test plan

  • Verify Lit-claimed sandbox ORCID accounts resolve correctly via orcidLinkedIdentityByOrcid
  • Verify legacy sandbox ORCID accounts still resolve correctly
  • Verify production ORCID accounts are unaffected

Lit oracle uses sourceId=4 with plain ORCID iDs for sandbox accounts,
while legacy accounts use sourceId=2 with "sandbox-" prefixed names.
This produces different on-chain account IDs.
- Update orcidAccountIdUtils to recognize sourceId=4 as ORCID
- Add getAllPossibleOrcidAccountIds to compute both legacy and Lit
account IDs for backwards-compatible resolution
- Update resolver to try all candidate account IDs
@railway-app
railway-appBottemporarily deployed to Drips App / graphql-api-pr-100 February 15, 2026 13:05 Destroyed
@railway-app

railway-appBot commented Feb 15, 2026

Copy link
Copy Markdown

🚅 Deployed to the graphql-api-pr-100 environment in Drips App

ServiceStatusWebUpdated (UTC)
GraphQL API◻️ Removed (View Logs)WebFeb 16, 2026 at 10:08 am

CopilotAI left a comment

Copy link
Copy Markdown

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 Lit-claimed ORCID sandbox accounts that use a different source ID (sourceId=4) than legacy ORCID accounts (sourceId=2). The Lit oracle creates sandbox accounts with plain ORCID IDs like "0009-...", while legacy sandbox accounts use the "sandbox-0009-..." prefix. The changes enable the resolver to compute and try all possible account IDs for backwards compatibility.

Changes:

  • Introduces ORCID_SOURCE_IDS array [2, 4] replacing the single ORCID_FORGE_ID constant
  • Adds getAllPossibleOrcidAccountIds function to generate both legacy and Lit account ID candidates
  • Updates orcidLinkedIdentityByOrcid resolver to iterate through candidate account IDs and return the first match

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

FileDescription
src/orcid-account/orcidAccountIdUtils.tsReplaces ORCID_FORGE_ID with ORCID_SOURCE_IDS array, updates extractSourceIdFromAccountId to properly decode 7-bit sourceId, and modifies isOrcidAccount to check multiple valid source IDs
src/common/dripsContracts.tsAdds getAllPossibleOrcidAccountIds function to compute both legacy (sourceId=2) and Lit (sourceId=4) account IDs for a given ORCID
src/linked-identity/linkedIdentityResolvers.tsUpdates resolver to try multiple candidate account IDs sequentially, returning first match or falling back to unclaimed entry
tests/linked-identity/linkedIdentityResolvers.test.tsUpdates test mocks to use getAllPossibleOrcidAccountIds instead of getCrossChainOrcidAccountIdByOrcidId

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +125
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

While getAllPossibleOrcidAccountIds always returns at least one account ID (the legacy sourceId=2 variant), the code would be more robust with an explicit check before accessing candidateAccountIds[0] on line 124. Consider adding an assertion or throw statement if the array is unexpectedly empty, or adding a comment explaining why the array is guaranteed to be non-empty. This improves code maintainability and makes the assumption explicit.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +121
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The resolver now makes sequential database queries for each candidate account ID until a match is found. In the worst case with sandbox ORCIDs, this means two sequential database lookups per request. For better performance, consider fetching all candidate account IDs in a single batched query using SQL IN clause or Promise.all with individual queries, then returning the first non-null result. This would reduce latency, especially on high-latency database connections.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +8
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The comment describing sourceId=2 as "regular ORCID" is misleading. According to the PR description and the code in getAllPossibleOrcidAccountIds, sourceId=2 is used for both regular production ORCID accounts AND legacy sandbox accounts (with "sandbox-" prefix). The comment should clarify that sourceId=2 is used for all legacy ORCID accounts (both production and sandbox with prefix), while sourceId=4 is specifically for Lit-claimed sandbox accounts without the prefix.

Suggested change
*-2: regularORCID
*-4: sandboxORCID(LitoracleusesaseparatesourceIdinsteadofanameprefix)
*-2: legacyORCIDaccounts(bothproductionORCIDandlegacysandboxaccountswith"sandbox-"prefix)
*-4: Lit-claimedsandboxORCIDaccountswithoutthe "sandbox-" prefix (use separate sourceId instead)

Copilot uses AI. Check for mistakes.
* - 2: regular ORCID
* - 4: sandbox ORCID (Lit oracle uses a separate sourceId instead of a name prefix)
*/
export const ORCID_SOURCE_IDS = [2, 4];

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The test file tests/orcid-account/orcidAccountIdUtils.test.ts imports ORCID_FORGE_ID which was removed in this PR and replaced with ORCID_SOURCE_IDS. This change will break the existing test suite when this PR is merged. The test file needs to be updated to import and test ORCID_SOURCE_IDS instead, and the test should verify that it contains both 2 and 4.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to +297
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The function getAllPossibleOrcidAccountIds always adds sourceId=4 (Lit sandbox) candidates for production ORCID IDs that don't start with "sandbox-". However, based on the PR description, sourceId=4 is specifically for Lit-claimed sandbox accounts. This means for production ORCID IDs like "0000-0002-1825-0097", the function will compute and return a sourceId=4 account ID that would never be valid. Consider only computing the sourceId=4 account ID when the orcidId starts with "sandbox-" or when it looks like a sandbox ORCID pattern (starts with "0009-").

Suggested change
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
// Only compute this for sandbox-style ORCID iDs:
// - Legacy sandbox IDs: start with "sandbox-"
// - Sandbox ORCID pattern: starts with "0009-"
if(orcidId.startsWith('sandbox-')||orcidId.startsWith('0009-')){
constplainOrcid=orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
constlitSandboxAccountId=(
awaitrepoDriver.calcAccountId(4,ethers.toUtf8Bytes(plainOrcid))
).toString()asRepoDriverId;
if(litSandboxAccountId!==legacyAccountId){
accountIds.push(litSandboxAccountId);
}

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +301
export async function getAllPossibleOrcidAccountIds(
orcidId: string,
chainsToQuery: DbSchema[],
): Promise<RepoDriverId[]> {
const availableChain = chainsToQuery.find(
(chain) =>
dripsContracts[dbSchemaToChain[chain]] &&
dripsContracts[dbSchemaToChain[chain]]!.repoDriver,
);

if (!availableChain) {
throw new Error('No available chain with initialized contracts.');
}

const { repoDriver } = dripsContracts[dbSchemaToChain[availableChain]]!;

const accountIds: RepoDriverId[] = [];

// Legacy account ID: sourceId=2, with the orcid string as provided
// (may include "sandbox-" prefix for legacy sandbox accounts)
const legacyAccountId = (
await repoDriver.calcAccountId(2, ethers.toUtf8Bytes(orcidId))
).toString() as RepoDriverId;
accountIds.push(legacyAccountId);

// Lit sandbox account ID: sourceId=4, with plain ORCID (no "sandbox-" prefix)
const plainOrcid = orcidId.startsWith('sandbox-')
? orcidId.slice('sandbox-'.length)
: orcidId;
const litSandboxAccountId = (
await repoDriver.calcAccountId(4, ethers.toUtf8Bytes(plainOrcid))
).toString() as RepoDriverId;

if (litSandboxAccountId !== legacyAccountId) {
accountIds.push(litSandboxAccountId);
}

return accountIds;
}

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The new function getAllPossibleOrcidAccountIds lacks direct unit test coverage. Given that this codebase uses comprehensive automated testing with vitest, and this is a critical function that handles ORCID account ID generation with complex logic for backwards compatibility, it should have dedicated unit tests. Consider adding tests that verify: 1) production ORCID IDs generate correct account IDs, 2) sandbox ORCID IDs with "sandbox-" prefix are handled correctly, 3) the deduplication logic works when both sourceIds produce the same account ID, and 4) error handling for unavailable chains.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +125
const candidateAccountIds = await getAllPossibleOrcidAccountIds(orcid, [
chainToDbSchema[chain],
]);

// Try each candidate account ID, return the first match found
for (const accountId of candidateAccountIds) {
assertIsLinkedIdentityId(accountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
accountId,
[chainToDbSchema[chain]],
);

assertIsLinkedIdentityId(orcidAccountId);
const identity = await linkedIdentitiesDataSource.getLinkedIdentityById(
orcidAccountId,
[chainToDbSchema[chain]],
);
if (identity) {
return toGqlLinkedIdentity(identity);
}
}

return identity
? toGqlLinkedIdentity(identity)
: toFakeUnclaimedOrcid(orcid, orcidAccountId, chain);
// No identity found — return a fake unclaimed entry using the first candidate
assertIsLinkedIdentityId(candidateAccountIds[0]);
return toFakeUnclaimedOrcid(orcid, candidateAccountIds[0], chain);

CopilotAIFeb 15, 2026

Copy link

Choose a reason for hiding this comment

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

The existing integration tests only mock getAllPossibleOrcidAccountIds to return a single account ID. To properly test the new multi-candidate resolution logic, additional test cases should be added that mock getAllPossibleOrcidAccountIds to return multiple candidate IDs and verify: 1) the resolver tries each candidate in order, 2) it returns the first match found, 3) when no match is found, it correctly falls back to toFakeUnclaimedOrcid with the first candidate. These scenarios are critical for ensuring the backwards compatibility feature works correctly.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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

@efstajas