From 29f34cb0cd965811c8fca3ce64d97cc029f240a2 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Thu, 13 Jun 2024 13:12:19 -0700 Subject: [PATCH 1/7] PKG -- [sdk] Restore previous account resolution ordering --- packages/sdk/src/resolve/resolve-accounts.ts | 35 +++++++++----------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/sdk/src/resolve/resolve-accounts.ts b/packages/sdk/src/resolve/resolve-accounts.ts index 2bc24e78b..d1d3a7105 100644 --- a/packages/sdk/src/resolve/resolve-accounts.ts +++ b/packages/sdk/src/resolve/resolve-accounts.ts @@ -200,21 +200,11 @@ async function recurseResolveAccount( account = addAccountToIx(ix, account) - const recursedAccounts = await Promise.all( - flatResolvedAccounts.map( - async (resolvedAccount: InteractionAccount) => { - return await recurseResolveAccount( - ix, - resolvedAccount.tempId, - depthLimit - 1, - {debugLogger} - ) - } - ) - ) - - return recursedAccounts - ? recurseFlatMap(recursedAccounts) + return flatResolvedAccounts + ? flatResolvedAccounts.map( + (flatResolvedAccount: InteractionAccount) => + flatResolvedAccount.tempId + ) : account.tempId } else { debugLogger( @@ -238,6 +228,7 @@ const getAccountTempIDs = (rawTempIds: string | string[] | null) => { async function resolveAccountType( ix: Interaction, type: ROLES, + depthLimit = MAX_DEPTH_LIMIT, {debugLogger}: {debugLogger: (msg?: string, indent?: number) => void} ) { invariant( @@ -261,7 +252,7 @@ async function resolveAccountType( let resolvedAccountTempIds = await recurseResolveAccount( ix, accountId, - MAX_DEPTH_LIMIT, + depthLimit, { debugLogger, } @@ -337,9 +328,15 @@ export async function resolveAccounts( } let [debugLogger, getDebugMessage] = debug() try { - await resolveAccountType(ix, ROLES.PROPOSER, {debugLogger}) - await resolveAccountType(ix, ROLES.AUTHORIZATIONS, {debugLogger}) - await resolveAccountType(ix, ROLES.PAYER, {debugLogger}) + let depthLimit = MAX_DEPTH_LIMIT + while (depthLimit > 0) { + await resolveAccountType(ix, ROLES.PROPOSER, depthLimit, {debugLogger}) + await resolveAccountType(ix, ROLES.AUTHORIZATIONS, depthLimit, { + debugLogger, + }) + await resolveAccountType(ix, ROLES.PAYER, depthLimit, {debugLogger}) + depthLimit-- + } await removeUnusedIxAccounts(ix, {debugLogger}) From 3242f52926b7591be0d10323c55b2b8e3d5bcd0f Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Thu, 13 Jun 2024 13:21:47 -0700 Subject: [PATCH 2/7] changeset --- .changeset/tasty-radios-buy.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tasty-radios-buy.md diff --git a/.changeset/tasty-radios-buy.md b/.changeset/tasty-radios-buy.md new file mode 100644 index 000000000..99d88595c --- /dev/null +++ b/.changeset/tasty-radios-buy.md @@ -0,0 +1,5 @@ +--- +"@onflow/sdk": patch +--- + +Restore account resolution ordering (fixes issue where FCL currentUser authz would run without knowing custom authz accounts) From 2de8774cf195d81c50fd48743365a43ec1987af6 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 14 Jun 2024 11:33:08 -0700 Subject: [PATCH 3/7] only iterate resolving again if more accounts pending --- packages/sdk/src/resolve/resolve-accounts.ts | 61 ++++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/packages/sdk/src/resolve/resolve-accounts.ts b/packages/sdk/src/resolve/resolve-accounts.ts index d1d3a7105..b3cb03f18 100644 --- a/packages/sdk/src/resolve/resolve-accounts.ts +++ b/packages/sdk/src/resolve/resolve-accounts.ts @@ -148,12 +148,12 @@ function uniqueAccountsFlatMap(accounts: InteractionAccount[]) { return uniqueAccountsFlatMapped } -async function recurseResolveAccount( +async function resolveSingleAccount( ix: Interaction, currentAccountTempId: string, depthLimit = MAX_DEPTH_LIMIT, {debugLogger}: {debugLogger: (msg?: string, indent?: number) => void} -) { +): Promise<[ids: string | string[], hasNewAccounts: boolean]> { if (depthLimit <= 0) { throw new Error( `recurseResolveAccount Error: Depth limit (${MAX_DEPTH_LIMIT}) reached. Ensure your authorization functions resolve to an account after ${MAX_DEPTH_LIMIT} resolves.` @@ -162,7 +162,7 @@ async function recurseResolveAccount( let account = ix.accounts[currentAccountTempId] - if (!account) return null + if (!account) return [[], false] debugLogger( `account: ${account.tempId}`, @@ -200,22 +200,25 @@ async function recurseResolveAccount( account = addAccountToIx(ix, account) - return flatResolvedAccounts - ? flatResolvedAccounts.map( - (flatResolvedAccount: InteractionAccount) => - flatResolvedAccount.tempId - ) - : account.tempId + return [ + flatResolvedAccounts + ? flatResolvedAccounts.map( + (flatResolvedAccount: InteractionAccount) => + flatResolvedAccount.tempId + ) + : account.tempId, + true, + ] } else { debugLogger( `account: ${account.tempId} -- cache HIT`, Math.max(MAX_DEPTH_LIMIT - depthLimit, 0) ) - return account.resolve + return [account.resolve, false] } } - return account.tempId + return [account.tempId, false] } const getAccountTempIDs = (rawTempIds: string | string[] | null) => { @@ -245,18 +248,16 @@ async function resolveAccountType( let accountTempIDs = getAccountTempIDs(ix[type]) let allResolvedAccounts: InteractionAccount[] = [] + let hasNewAccounts = false for (let accountId of accountTempIDs) { let account = ix.accounts[accountId] invariant(Boolean(account), `resolveAccountType Error: account not found`) - let resolvedAccountTempIds = await recurseResolveAccount( - ix, - accountId, - depthLimit, - { + let [resolvedAccountTempIds, acctHasNewAccounts] = + await resolveSingleAccount(ix, accountId, depthLimit, { debugLogger, - } - ) + }) + hasNewAccounts = acctHasNewAccounts || hasNewAccounts resolvedAccountTempIds = Array.isArray(resolvedAccountTempIds) ? resolvedAccountTempIds @@ -311,6 +312,8 @@ async function resolveAccountType( } } } + + return hasNewAccounts } export async function resolveAccounts( @@ -329,12 +332,22 @@ export async function resolveAccounts( let [debugLogger, getDebugMessage] = debug() try { let depthLimit = MAX_DEPTH_LIMIT - while (depthLimit > 0) { - await resolveAccountType(ix, ROLES.PROPOSER, depthLimit, {debugLogger}) - await resolveAccountType(ix, ROLES.AUTHORIZATIONS, depthLimit, { - debugLogger, - }) - await resolveAccountType(ix, ROLES.PAYER, depthLimit, {debugLogger}) + let shouldContinue = true + while (shouldContinue) { + if (depthLimit <= 0) { + throw new Error( + `resolveAccounts Error: Depth limit (${MAX_DEPTH_LIMIT}) reached. Ensure your authorization functions resolve to an account after ${MAX_DEPTH_LIMIT} resolves.` + ) + } + + shouldContinue = + (await resolveAccountType(ix, ROLES.PROPOSER, depthLimit, { + debugLogger, + })) || + (await resolveAccountType(ix, ROLES.AUTHORIZATIONS, depthLimit, { + debugLogger, + })) || + (await resolveAccountType(ix, ROLES.PAYER, depthLimit, {debugLogger})) depthLimit-- } From fe26273bd4777db3837e7483e00ce18757ad02c5 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Sun, 16 Jun 2024 11:40:35 -0700 Subject: [PATCH 4/7] Add working test --- .../sdk/src/resolve/resolve-accounts.test.js | 56 ++++++ packages/sdk/src/resolve/resolve-accounts.ts | 175 +++++++++--------- 2 files changed, 148 insertions(+), 83 deletions(-) diff --git a/packages/sdk/src/resolve/resolve-accounts.test.js b/packages/sdk/src/resolve/resolve-accounts.test.js index 4ef15e482..e3b71a187 100644 --- a/packages/sdk/src/resolve/resolve-accounts.test.js +++ b/packages/sdk/src/resolve/resolve-accounts.test.js @@ -499,3 +499,59 @@ test("Voucher in PreSignable multiple payer keys and multiple authorizers", asyn ], }) }) + +test("Voucher sent to Current User Pre-Authz includes other authorizer addresses", async () => { + const mockResolve = jest.fn().mockImplementation(async acct => [ + { + ...acct, + addr: "0x01", + keyId: 1, + sequenceNum: 123, + signingFunction: () => ({signature: "123"}), + role: {proposer: true, payer: true, authorizer: true}, + }, + ]) + const currentUser = account => ({ + ...account, + tempId: "CURRENT_USER", + resolve: mockResolve, + }) + const customAuthz = async acct => ({ + ...acct, + addr: "0x02", + keyId: 2, + sequenceNum: 234, + signingFunction: () => ({signature: "234"}), + }) + + const ix = await resolve( + await build([ + transaction``, + limit(156), + proposer(currentUser), + authorizations([customAuthz, currentUser]), + payer(currentUser), + ref("123"), + ]) + ) + + const ps = buildPreSignable(ix.accounts[ix.proposer], ix) + expect(ps.voucher).toEqual({ + cadence: "", + refBlock: "123", + computeLimit: 156, + arguments: [], + proposalKey: {address: "0x01", keyId: 1, sequenceNum: 123}, + payer: "0x01", + authorizers: ["0x02", "0x01"], + payloadSigs: [{address: "0x02", keyId: 2, sig: "234"}], + envelopeSigs: [{address: "0x01", keyId: 1, sig: "123"}], + }) + + expect(mockResolve).toHaveBeenCalledTimes(1) + // Verify pre-signable contains custom authz in the authorizers list + expect(mockResolve.mock.calls[0][1].voucher.authorizers).toEqual([ + "0x02", + null, + ]) +}) diff --git a/packages/sdk/src/resolve/resolve-accounts.ts b/packages/sdk/src/resolve/resolve-accounts.ts index b3cb03f18..49c6657d0 100644 --- a/packages/sdk/src/resolve/resolve-accounts.ts +++ b/packages/sdk/src/resolve/resolve-accounts.ts @@ -148,12 +148,13 @@ function uniqueAccountsFlatMap(accounts: InteractionAccount[]) { return uniqueAccountsFlatMapped } +// Resolve single account, returns new account tempIds (if they exist) async function resolveSingleAccount( ix: Interaction, currentAccountTempId: string, depthLimit = MAX_DEPTH_LIMIT, {debugLogger}: {debugLogger: (msg?: string, indent?: number) => void} -): Promise<[ids: string | string[], hasNewAccounts: boolean]> { +): Promise { if (depthLimit <= 0) { throw new Error( `recurseResolveAccount Error: Depth limit (${MAX_DEPTH_LIMIT}) reached. Ensure your authorization functions resolve to an account after ${MAX_DEPTH_LIMIT} resolves.` @@ -162,7 +163,7 @@ async function resolveSingleAccount( let account = ix.accounts[currentAccountTempId] - if (!account) return [[], false] + if (!account) return [] debugLogger( `account: ${account.tempId}`, @@ -200,25 +201,19 @@ async function resolveSingleAccount( account = addAccountToIx(ix, account) - return [ - flatResolvedAccounts - ? flatResolvedAccounts.map( - (flatResolvedAccount: InteractionAccount) => - flatResolvedAccount.tempId - ) - : account.tempId, - true, - ] + return flatResolvedAccounts.map( + (flatResolvedAccount: InteractionAccount) => flatResolvedAccount.tempId + ) } else { debugLogger( `account: ${account.tempId} -- cache HIT`, Math.max(MAX_DEPTH_LIMIT - depthLimit, 0) ) - return [account.resolve, false] + return account.resolve } } - return [account.tempId, false] + return [] } const getAccountTempIDs = (rawTempIds: string | string[] | null) => { @@ -228,9 +223,44 @@ const getAccountTempIDs = (rawTempIds: string | string[] | null) => { return Array.isArray(rawTempIds) ? rawTempIds : [rawTempIds] } -async function resolveAccountType( +async function replaceRoles( ix: Interaction, - type: ROLES, + oldAccountTempId: string, + newAccounts: InteractionAccount[] +) { + // Replace roles in the interaction with any resolved accounts + // e.g. payer -> [oldAccountTempId, anotherId] => payer -> [newAccountTempId, anotherId] + for (let role of Object.values(ROLES)) { + if (Array.isArray(ix[role])) { + ix[role] = (ix[role] as string[]).reduce((acc, acctTempId) => { + if (acctTempId === oldAccountTempId) { + return acc.concat( + ...newAccounts + .filter(x => { + return ( + (role === ROLES.PAYER && x.role.payer) || + (role === ROLES.AUTHORIZATIONS && x.role.authorizer) + ) + }) + .map(acct => acct.tempId) + ) + } + return acc.concat(acctTempId) + }, [] as string[]) as any + } else if (ix[role] === oldAccountTempId && ix[role] != null) { + if (newAccounts.length > 1) { + console.warn( + `replaceRoles Warning: Multiple accounts resolved for role ${role}. Only the first account will be used.` + ) + } + ix[role] = newAccounts[0].tempId as any + } + } +} + +async function resolveAccountsByIds( + ix: Interaction, + accountTempIds: Set, depthLimit = MAX_DEPTH_LIMIT, {debugLogger}: {debugLogger: (msg?: string, indent?: number) => void} ) { @@ -238,82 +268,47 @@ async function resolveAccountType( ix && typeof ix === "object", "resolveAccountType Error: ix not defined" ) - invariant( - type === ROLES.PAYER || - type === ROLES.PROPOSER || - type === ROLES.AUTHORIZATIONS, - "resolveAccountType Error: type must be 'payer', 'proposer' or 'authorizations'" - ) - - let accountTempIDs = getAccountTempIDs(ix[type]) - let allResolvedAccounts: InteractionAccount[] = [] - let hasNewAccounts = false - for (let accountId of accountTempIDs) { + let newTempIds = new Set() + for (let accountId of accountTempIds) { let account = ix.accounts[accountId] invariant(Boolean(account), `resolveAccountType Error: account not found`) - let [resolvedAccountTempIds, acctHasNewAccounts] = - await resolveSingleAccount(ix, accountId, depthLimit, { + const resolvedAccountTempIds = await resolveSingleAccount( + ix, + accountId, + depthLimit, + { debugLogger, - }) - hasNewAccounts = acctHasNewAccounts || hasNewAccounts - - resolvedAccountTempIds = Array.isArray(resolvedAccountTempIds) - ? resolvedAccountTempIds - : [resolvedAccountTempIds] + } + ) - let resolvedAccounts: InteractionAccount[] = resolvedAccountTempIds.map( + const resolvedAccounts: InteractionAccount[] = resolvedAccountTempIds.map( (resolvedAccountTempId: string) => ix.accounts[resolvedAccountTempId] ) - let flatResolvedAccounts = uniqueAccountsFlatMap(resolvedAccounts) + const flatResolvedAccounts = uniqueAccountsFlatMap(resolvedAccounts) - allResolvedAccounts = allResolvedAccounts.concat(flatResolvedAccounts) - } + // Add new tempIds to the set so they can be used next iteration + flatResolvedAccounts.forEach(x => newTempIds.add(x.tempId)) - invariant( - allResolvedAccounts.length > 0 || type === ROLES.AUTHORIZATIONS, - `resolveAccountType Error: no ${type} accounts were found` - ) - - if (type === ROLES.PAYER) { - allResolvedAccounts = allResolvedAccounts.filter( - acct => acct.role.payer === true - ) + // Update any roles in the interaction based on the new accounts + replaceRoles(ix, accountId, flatResolvedAccounts) } - if (type === ROLES.PROPOSER) { - allResolvedAccounts = allResolvedAccounts.filter( - acct => acct.role.proposer === true - ) - } - if (type === ROLES.AUTHORIZATIONS) { - allResolvedAccounts = allResolvedAccounts.filter( - acct => acct.role.authorizer === true - ) - } - - ix[type] = ( - Array.isArray(ix[type]) - ? [...new Set(allResolvedAccounts.map(acct => acct.tempId))] - : allResolvedAccounts[0].tempId - ) as string & string[] // Ensure all payers are of the same account - if (type === ROLES.PAYER) { - let address - for (const payerTempID of ix[ROLES.PAYER]) { - let pAcct = ix.accounts[payerTempID] - if (!address) address = pAcct.addr - else if (address !== pAcct.addr) { - throw new Error( - "resolveAccountType Error: payers from different accounts detected" - ) - } + let payerAddress + for (const payerTempID of ix[ROLES.PAYER]) { + let pAcct = ix.accounts[payerTempID] + if (!payerAddress) payerAddress = pAcct.addr + else if (payerAddress !== pAcct.addr) { + throw new Error( + "resolveAccountType Error: payers from different accounts detected" + ) } } - return hasNewAccounts + return newTempIds } export async function resolveAccounts( @@ -332,27 +327,41 @@ export async function resolveAccounts( let [debugLogger, getDebugMessage] = debug() try { let depthLimit = MAX_DEPTH_LIMIT - let shouldContinue = true - while (shouldContinue) { + let accountTempIds = new Set([ + ...getAccountTempIDs(ix[ROLES.PAYER]), + ...getAccountTempIDs(ix[ROLES.PROPOSER]), + ...getAccountTempIDs(ix[ROLES.AUTHORIZATIONS]), + ]) + + while (accountTempIds.size > 0) { if (depthLimit <= 0) { throw new Error( `resolveAccounts Error: Depth limit (${MAX_DEPTH_LIMIT}) reached. Ensure your authorization functions resolve to an account after ${MAX_DEPTH_LIMIT} resolves.` ) } - shouldContinue = - (await resolveAccountType(ix, ROLES.PROPOSER, depthLimit, { - debugLogger, - })) || - (await resolveAccountType(ix, ROLES.AUTHORIZATIONS, depthLimit, { + accountTempIds = await resolveAccountsByIds( + ix, + accountTempIds, + depthLimit, + { debugLogger, - })) || - (await resolveAccountType(ix, ROLES.PAYER, depthLimit, {debugLogger})) + } + ) depthLimit-- } await removeUnusedIxAccounts(ix, {debugLogger}) + // Ensure at least one account for each role is resolved (except for authorizations) + for (const role of Object.values(ROLES)) { + invariant( + getAccountTempIDs(ix[role]).length > 0 || + role === ROLES.AUTHORIZATIONS, + `resolveAccountType Error: no accounts for role "${role}" found` + ) + } + if (opts.enableDebug) { console.debug(getDebugMessage()) } From f66e0b57c9218b5cdc1ada0addaae7e81ee702d5 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Sun, 16 Jun 2024 12:57:58 -0700 Subject: [PATCH 5/7] fix tests --- packages/sdk/src/resolve/resolve-accounts.ts | 64 +++++++++++--------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/packages/sdk/src/resolve/resolve-accounts.ts b/packages/sdk/src/resolve/resolve-accounts.ts index 49c6657d0..b15daac58 100644 --- a/packages/sdk/src/resolve/resolve-accounts.ts +++ b/packages/sdk/src/resolve/resolve-accounts.ts @@ -154,7 +154,7 @@ async function resolveSingleAccount( currentAccountTempId: string, depthLimit = MAX_DEPTH_LIMIT, {debugLogger}: {debugLogger: (msg?: string, indent?: number) => void} -): Promise { +): Promise<[string[], boolean]> { if (depthLimit <= 0) { throw new Error( `recurseResolveAccount Error: Depth limit (${MAX_DEPTH_LIMIT}) reached. Ensure your authorization functions resolve to an account after ${MAX_DEPTH_LIMIT} resolves.` @@ -163,7 +163,7 @@ async function resolveSingleAccount( let account = ix.accounts[currentAccountTempId] - if (!account) return [] + if (!account) return [[], false] debugLogger( `account: ${account.tempId}`, @@ -201,19 +201,23 @@ async function resolveSingleAccount( account = addAccountToIx(ix, account) - return flatResolvedAccounts.map( - (flatResolvedAccount: InteractionAccount) => flatResolvedAccount.tempId - ) + return [ + flatResolvedAccounts.map( + (flatResolvedAccount: InteractionAccount) => + flatResolvedAccount.tempId + ), + true, + ] } else { debugLogger( `account: ${account.tempId} -- cache HIT`, Math.max(MAX_DEPTH_LIMIT - depthLimit, 0) ) - return account.resolve + return [account.resolve, false] } } - return [] + return [account.tempId ? [account.tempId] : [], false] } const getAccountTempIDs = (rawTempIds: string | string[] | null) => { @@ -231,8 +235,8 @@ async function replaceRoles( // Replace roles in the interaction with any resolved accounts // e.g. payer -> [oldAccountTempId, anotherId] => payer -> [newAccountTempId, anotherId] for (let role of Object.values(ROLES)) { - if (Array.isArray(ix[role])) { - ix[role] = (ix[role] as string[]).reduce((acc, acctTempId) => { + if (role === ROLES.AUTHORIZATIONS || role === ROLES.PAYER) { + ix[role] = getAccountTempIDs(ix[role]).reduce((acc, acctTempId) => { if (acctTempId === oldAccountTempId) { return acc.concat( ...newAccounts @@ -247,13 +251,16 @@ async function replaceRoles( } return acc.concat(acctTempId) }, [] as string[]) as any - } else if (ix[role] === oldAccountTempId && ix[role] != null) { - if (newAccounts.length > 1) { - console.warn( - `replaceRoles Warning: Multiple accounts resolved for role ${role}. Only the first account will be used.` + } else if (role === ROLES.PROPOSER) { + const proposerAccts = newAccounts.filter(x => x.role.proposer) + + if (proposerAccts.length > 1) { + throw new Error( + `replaceRoles Error: Multiple proposer keys were resolved, but only one is allowed` ) } - ix[role] = newAccounts[0].tempId as any + + ix[role] = proposerAccts[0]?.tempId ?? ix[role] } } } @@ -274,26 +281,25 @@ async function resolveAccountsByIds( let account = ix.accounts[accountId] invariant(Boolean(account), `resolveAccountType Error: account not found`) - const resolvedAccountTempIds = await resolveSingleAccount( - ix, - accountId, - depthLimit, - { + const [resolvedAccountTempIds, foundNewAccounts] = + await resolveSingleAccount(ix, accountId, depthLimit, { debugLogger, - } - ) + }) - const resolvedAccounts: InteractionAccount[] = resolvedAccountTempIds.map( - (resolvedAccountTempId: string) => ix.accounts[resolvedAccountTempId] - ) + // If new accounts were resolved, add them to the set so they can be explored next iteration + if (foundNewAccounts) { + const resolvedAccounts: InteractionAccount[] = resolvedAccountTempIds.map( + (resolvedAccountTempId: string) => ix.accounts[resolvedAccountTempId] + ) - const flatResolvedAccounts = uniqueAccountsFlatMap(resolvedAccounts) + const flatResolvedAccounts = uniqueAccountsFlatMap(resolvedAccounts) - // Add new tempIds to the set so they can be used next iteration - flatResolvedAccounts.forEach(x => newTempIds.add(x.tempId)) + // Add new tempIds to the set so they can be used next iteration + flatResolvedAccounts.forEach(x => newTempIds.add(x.tempId)) - // Update any roles in the interaction based on the new accounts - replaceRoles(ix, accountId, flatResolvedAccounts) + // Update any roles in the interaction based on the new accounts + replaceRoles(ix, accountId, flatResolvedAccounts) + } } // Ensure all payers are of the same account From 840dfadc3cfa73e976d391b43fc45cff0e00746d Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Sun, 16 Jun 2024 13:09:20 -0700 Subject: [PATCH 6/7] naming/comments --- packages/sdk/src/resolve/resolve-accounts.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/sdk/src/resolve/resolve-accounts.ts b/packages/sdk/src/resolve/resolve-accounts.ts index b15daac58..1c6e832c2 100644 --- a/packages/sdk/src/resolve/resolve-accounts.ts +++ b/packages/sdk/src/resolve/resolve-accounts.ts @@ -332,28 +332,24 @@ export async function resolveAccounts( } let [debugLogger, getDebugMessage] = debug() try { + // BFS, resolving one level of accounts at a time let depthLimit = MAX_DEPTH_LIMIT - let accountTempIds = new Set([ + let frontier = new Set([ ...getAccountTempIDs(ix[ROLES.PAYER]), ...getAccountTempIDs(ix[ROLES.PROPOSER]), ...getAccountTempIDs(ix[ROLES.AUTHORIZATIONS]), ]) - while (accountTempIds.size > 0) { + while (frontier.size > 0) { if (depthLimit <= 0) { throw new Error( `resolveAccounts Error: Depth limit (${MAX_DEPTH_LIMIT}) reached. Ensure your authorization functions resolve to an account after ${MAX_DEPTH_LIMIT} resolves.` ) } - accountTempIds = await resolveAccountsByIds( - ix, - accountTempIds, - depthLimit, - { - debugLogger, - } - ) + frontier = await resolveAccountsByIds(ix, frontier, depthLimit, { + debugLogger, + }) depthLimit-- } From 86eb487a2945efd6b41773e849b078ce18268f46 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 18 Jun 2024 00:04:17 +0300 Subject: [PATCH 7/7] Relax non-null first authorization in voucher requirement --- packages/sdk/src/resolve/voucher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/src/resolve/voucher.ts b/packages/sdk/src/resolve/voucher.ts index f5cff68f5..4e0cf97e4 100644 --- a/packages/sdk/src/resolve/voucher.ts +++ b/packages/sdk/src/resolve/voucher.ts @@ -29,7 +29,7 @@ export const createSignableVoucher = (ix: Interaction) => { .reduce((prev: (string | null)[], current) => { return prev.find(item => item === current) ? prev : [...prev, current] }, []) - return authorizations?.[0] ? authorizations : [] + return authorizations } const buildInsideSigners = () =>