diff --git a/packages/fcl/src/wallet-provider-spec/draft-v3.md b/packages/fcl/src/wallet-provider-spec/draft-v3.md index 80e19a87f..363e80a69 100644 --- a/packages/fcl/src/wallet-provider-spec/draft-v3.md +++ b/packages/fcl/src/wallet-provider-spec/draft-v3.md @@ -471,7 +471,7 @@ An authorization service is expected to know the Account and the Key that will b FCL will use the `method` provided to request an array of composite signature from authorization service (Wrapped in a `PollingResponse`). The authorization service will be sent a `Signable`. The service is expected to construct an encoded message to sign from `Signable.voucher`. -It then needs to hash the encoded message, and prepend a required [transaction domain tag](https://github.com/onflow/flow-js-sdk/blob/master/packages/sdk/src/encode/encode.js#L12-L13). +It then needs to hash the encoded message, and prepend a required [transaction domain tag](../../../sdk/src/encode/encode.ts#L12-L13). Finally it signs the payload with the user/s keys, producing a signature. This signature, as a HEX string, is sent back to FCL as part of the `CompositeSignature` which includes the user address and keyID in the data property of a `PollingResponse`. diff --git a/packages/fcl/src/wallet-provider-spec/draft-v4.md b/packages/fcl/src/wallet-provider-spec/draft-v4.md index 30f49a414..b78519324 100644 --- a/packages/fcl/src/wallet-provider-spec/draft-v4.md +++ b/packages/fcl/src/wallet-provider-spec/draft-v4.md @@ -732,7 +732,7 @@ An authorization service is expected to know the Account and the Key that will b FCL will use the `method` provided to request an array of composite signature from authorization service (Wrapped in a `PollingResponse`). The authorization service will be sent a `Signable`. The service is expected to construct an encoded message to sign from `Signable.voucher`. -It then needs to hash the encoded message, and prepend a required [transaction domain tag](https://github.com/onflow/flow-js-sdk/blob/master/packages/sdk/src/encode/encode.js#L12-L13). +It then needs to hash the encoded message, and prepend a required [transaction domain tag](../../../sdk/src/encode/encode.ts#L12-L13). Finally it signs the payload with the user/s keys, producing a signature. This signature, as a HEX string, is sent back to FCL as part of the `CompositeSignature` which includes the user address and keyID in the data property of a `PollingResponse`. diff --git a/packages/rlp/src/index.ts b/packages/rlp/src/index.ts index 1ed6933f2..9260f5ccc 100644 --- a/packages/rlp/src/index.ts +++ b/packages/rlp/src/index.ts @@ -2,7 +2,7 @@ import {Buffer} from "buffer" export {Buffer} -type EncodeInput = +export type EncodeInput = | Buffer | string | number diff --git a/packages/sdk/readme.md b/packages/sdk/readme.md index a85319f90..650e0a752 100644 --- a/packages/sdk/readme.md +++ b/packages/sdk/readme.md @@ -509,7 +509,7 @@ const response = await sdk.send(await sdk.build([ - [`sdk.getCollection`](./src/build/build-get-collection) - [`sdk.getEvents`](./src/build/build-get-events.js) - [`sdk.getEventsAtBlockHeightRange`](./src/build/build-get-events-at-block-height-range.js) - - [`sdk.getEventsAtBlockIds`](./src/build/build-get-events-at-block-ids) + - [`sdk.getEventsAtBlockIds`](./src/build/build-get-events-at-block-ids.js) - [`sdk.getLatestBlock`](./src/build/build-get-latest-block.js) - [`sdk.getTransactionStatus`](./src/build/build-get-transaction-status.js) - [`sdk.getTransaction`](./src/build/build-get-transaction.js) @@ -526,15 +526,15 @@ const response = await sdk.send(await sdk.build([ - [`sdk.validator`](./src/build/build-validator.js) - [Resolvers](./src/resolve) - - [`sdk.resolveAccounts`](./src/resolve/resolve-accounts.js) + - [`sdk.resolveAccounts`](./src/resolve/resolve-accounts.ts) - [`sdk.resolveArguments`](./src/resolve/resolve-arguments.js) - [`sdk.resolveCadence`](./src/resolve/resolve-cadence.js) - [`sdk.resolveFinalNormalization`](./src/resolve/resolve-final-normalization.js) - [`sdk.resolveVoucherIntercept`](./src/resolve/resolve-voucher-intercept.js) - [`sdk.resolveProposerSequenceNumber`](./src/resolve/resolve-proposer-sequence-number.js) - [`sdk.resolveRefBlockId`](./src/resolve/resolve-ref-block-id.js) - - [`sdk.resolveSignatures`](./src/resolve/resolve-signatures.js) + - [`sdk.resolveSignatures`](./src/resolve/resolve-signatures.ts) - [`sdk.resolveValidators`](./src/resolve/resolve-validators.js) - [Other Utils](./src/) - - [`sdk.voucherToTxId`](./src/resolve/voucher.js) + - [`sdk.voucherToTxId`](./src/resolve/voucher.ts) diff --git a/packages/sdk/src/contract.test.js b/packages/sdk/src/contract.test.js index 4a9001db4..40f95cea8 100644 --- a/packages/sdk/src/contract.test.js +++ b/packages/sdk/src/contract.test.js @@ -1,6 +1,6 @@ import * as root from "./sdk" import * as decode from "./decode/decode.js" -import * as encode from "./encode/encode.js" +import * as encode from "./encode/encode" import * as interaction from "./interaction/interaction" import * as send from "./send/send.js" import * as template from "@onflow/util-template" diff --git a/packages/sdk/src/encode/encode.test.js b/packages/sdk/src/encode/encode.test.js index bb8d8ada7..8c224fe61 100644 --- a/packages/sdk/src/encode/encode.test.js +++ b/packages/sdk/src/encode/encode.test.js @@ -4,8 +4,8 @@ import { encodeTransactionPayload, encodeTransactionEnvelope, encodeTxIdFromVoucher, -} from "./encode.js" -import * as root from "./encode.js" +} from "./encode" +import * as root from "./encode" it("export contract interface", () => { expect(root).toStrictEqual( diff --git a/packages/sdk/src/encode/encode.js b/packages/sdk/src/encode/encode.ts similarity index 53% rename from packages/sdk/src/encode/encode.js rename to packages/sdk/src/encode/encode.ts index 388487ced..46d274d9b 100644 --- a/packages/sdk/src/encode/encode.js +++ b/packages/sdk/src/encode/encode.ts @@ -1,54 +1,54 @@ import {SHA3} from "sha3" -import {encode, Buffer} from "@onflow/rlp" +import {encode, Buffer, EncodeInput} from "@onflow/rlp" import {sansPrefix} from "@onflow/util-address" -export const encodeTransactionPayload = tx => +export const encodeTransactionPayload = (tx: Transaction) => prependTransactionDomainTag(rlpEncode(preparePayload(tx))) -export const encodeTransactionEnvelope = tx => +export const encodeTransactionEnvelope = (tx: Transaction) => prependTransactionDomainTag(rlpEncode(prepareEnvelope(tx))) -export const encodeTxIdFromVoucher = voucher => +export const encodeTxIdFromVoucher = (voucher: Voucher) => sha3_256(rlpEncode(prepareVoucher(voucher))) -const rightPaddedHexBuffer = (value, pad) => - Buffer.from(value.padEnd(pad * 2, 0), "hex") +const rightPaddedHexBuffer = (value: string, pad: number) => + Buffer.from(value.padEnd(pad * 2, "0"), "hex") -const leftPaddedHexBuffer = (value, pad) => - Buffer.from(value.padStart(pad * 2, 0), "hex") +const leftPaddedHexBuffer = (value: string, pad: number) => + Buffer.from(value.padStart(pad * 2, "0"), "hex") const TRANSACTION_DOMAIN_TAG = rightPaddedHexBuffer( Buffer.from("FLOW-V0.0-transaction").toString("hex"), 32 ).toString("hex") -const prependTransactionDomainTag = tx => TRANSACTION_DOMAIN_TAG + tx +const prependTransactionDomainTag = (tx: string) => TRANSACTION_DOMAIN_TAG + tx -const addressBuffer = addr => leftPaddedHexBuffer(addr, 8) +const addressBuffer = (addr: string) => leftPaddedHexBuffer(addr, 8) -const blockBuffer = block => leftPaddedHexBuffer(block, 32) +const blockBuffer = (block: string) => leftPaddedHexBuffer(block, 32) -const argumentToString = arg => Buffer.from(JSON.stringify(arg), "utf8") +const argumentToString = (arg: Record) => Buffer.from(JSON.stringify(arg), "utf8") -const scriptBuffer = script => Buffer.from(script, "utf8") -const signatureBuffer = signature => Buffer.from(signature, "hex") +const scriptBuffer = (script: string) => Buffer.from(script, "utf8") +const signatureBuffer = (signature: string) => Buffer.from(signature, "hex") -const rlpEncode = v => { +const rlpEncode = (v: EncodeInput) => { return encode(v).toString("hex") } -const sha3_256 = msg => { +const sha3_256 = (msg: string) => { const sha = new SHA3(256) sha.update(Buffer.from(msg, "hex")) return sha.digest().toString("hex") } -const preparePayload = tx => { +const preparePayload = (tx: Transaction) => { validatePayload(tx) return [ - scriptBuffer(tx.cadence), + scriptBuffer(tx.cadence || ''), tx.arguments.map(argumentToString), - blockBuffer(tx.refBlock), + blockBuffer(tx.refBlock || ''), tx.computeLimit, - addressBuffer(sansPrefix(tx.proposalKey.address)), + addressBuffer(sansPrefix(tx.proposalKey.address || '')), tx.proposalKey.keyId, tx.proposalKey.sequenceNum, addressBuffer(sansPrefix(tx.payer)), @@ -56,19 +56,18 @@ const preparePayload = tx => { ] } -const prepareEnvelope = tx => { +const prepareEnvelope = (tx: Transaction) => { validateEnvelope(tx) return [preparePayload(tx), preparePayloadSignatures(tx)] } -const preparePayloadSignatures = tx => { +const preparePayloadSignatures = (tx: Transaction) => { const signers = collectSigners(tx) - return tx.payloadSigs - .map(sig => { + return tx.payloadSigs?.map((sig: Sig) => { return { - signerIndex: signers.get(sig.address), + signerIndex: signers.get(sig.address) || '', keyId: sig.keyId, sig: sig.sig, } @@ -79,45 +78,51 @@ const preparePayloadSignatures = tx => { if (a.keyId > b.keyId) return 1 if (a.keyId < b.keyId) return -1 + + return 0 }) .map(sig => { return [sig.signerIndex, sig.keyId, signatureBuffer(sig.sig)] }) } -const collectSigners = tx => { - const signers = new Map() +const collectSigners = (tx: Voucher | Transaction) => { + const signers = new Map() let i = 0 - const addSigner = addr => { + const addSigner = (addr: string) => { if (!signers.has(addr)) { signers.set(addr, i) i++ } } - addSigner(tx.proposalKey.address) + if (tx.proposalKey.address){ + addSigner(tx.proposalKey.address) + } addSigner(tx.payer) tx.authorizers.forEach(addSigner) return signers } -const prepareVoucher = voucher => { +const prepareVoucher = (voucher: Voucher) => { validateVoucher(voucher) const signers = collectSigners(voucher) - const prepareSigs = sigs => { + const prepareSigs = (sigs: Sig[]) => { return sigs - .map(({address, keyId, sig}) => { - return {signerIndex: signers.get(address), keyId, sig} + .map(({ address, keyId, sig }) => { + return { signerIndex: signers.get(address) || '', keyId, sig } }) .sort((a, b) => { if (a.signerIndex > b.signerIndex) return 1 if (a.signerIndex < b.signerIndex) return -1 if (a.keyId > b.keyId) return 1 if (a.keyId < b.keyId) return -1 + + return 0 }) .map(sig => { return [sig.signerIndex, sig.keyId, signatureBuffer(sig.sig)] @@ -143,23 +148,23 @@ const prepareVoucher = voucher => { ] } -const validatePayload = tx => { +const validatePayload = (tx: Transaction) => { payloadFields.forEach(field => checkField(tx, field)) proposalKeyFields.forEach(field => checkField(tx.proposalKey, field, "proposalKey") ) } -const validateEnvelope = tx => { +const validateEnvelope = (tx: Transaction) => { payloadSigsFields.forEach(field => checkField(tx, field)) - tx.payloadSigs.forEach((sig, index) => { + tx.payloadSigs?.forEach((sig, index) => { payloadSigFields.forEach(field => checkField(sig, field, "payloadSigs", index) ) }) } -const validateVoucher = voucher => { +const validateVoucher = (voucher: Voucher) => { payloadFields.forEach(field => checkField(voucher, field)) proposalKeyFields.forEach(field => checkField(voucher.proposalKey, field, "proposalKey") @@ -178,12 +183,64 @@ const validateVoucher = voucher => { }) } -const isNumber = v => typeof v === "number" -const isString = v => typeof v === "string" -const isObject = v => v !== null && typeof v === "object" -const isArray = v => isObject(v) && v instanceof Array +const isNumber = (v: any): v is number => typeof v === "number" +const isString = (v: any): v is string => typeof v === "string" +const isObject = (v: any) => v !== null && typeof v === "object" +const isArray = (v: any) => isObject(v) && v instanceof Array + +interface VoucherArgument { + type: string + value: string +} + +interface VoucherProposalKey { + address: string + keyId: number | null + sequenceNum: number | null +} + +interface Sig { + address: string, + keyId: number | string, + sig: string, +} + +export interface TransactionProposalKey { + address?: string + keyId?: number | string + sequenceNum?: number +} +export interface Transaction { + cadence: string | null; + refBlock: string | null; + computeLimit: string | null; + arguments: VoucherArgument[] + proposalKey: TransactionProposalKey + payer: string + authorizers: string[] + payloadSigs?: Sig[] + envelopeSigs?: TransactionProposalKey[] +} + +export interface Voucher { + cadence: string; + refBlock: string; + computeLimit: number; + arguments: VoucherArgument[] + proposalKey: VoucherProposalKey + payer: string + authorizers: string[] + payloadSigs: Sig[] + envelopeSigs: Sig[] +} + +interface PayloadField { + name: string, + check: (v: any) => boolean, + defaultVal?: string +} -const payloadFields = [ +const payloadFields: PayloadField[] = [ {name: "cadence", check: isString}, {name: "arguments", check: isArray}, {name: "refBlock", check: isString, defaultVal: "0"}, @@ -193,42 +250,47 @@ const payloadFields = [ {name: "authorizers", check: isArray}, ] -const proposalKeyFields = [ +const proposalKeyFields: PayloadField[] = [ {name: "address", check: isString}, {name: "keyId", check: isNumber}, {name: "sequenceNum", check: isNumber}, ] -const payloadSigsFields = [{name: "payloadSigs", check: isArray}] +const payloadSigsFields: PayloadField[] = [{name: "payloadSigs", check: isArray}] -const payloadSigFields = [ +const payloadSigFields: PayloadField[] = [ {name: "address", check: isString}, {name: "keyId", check: isNumber}, {name: "sig", check: isString}, ] -const envelopeSigsFields = [{name: "envelopeSigs", check: isArray}] +const envelopeSigsFields: PayloadField[] = [{ name: "envelopeSigs", check: isArray }] -const envelopeSigFields = [ +const envelopeSigFields: PayloadField[] = [ {name: "address", check: isString}, {name: "keyId", check: isNumber}, {name: "sig", check: isString}, ] -const checkField = (obj, field, base, index) => { +const checkField = ( + obj: Record, + field: PayloadField, + base?: string, + index?: number +) => { const {name, check, defaultVal} = field if (obj[name] == null && defaultVal != null) obj[name] = defaultVal if (obj[name] == null) throw missingFieldError(name, base, index) if (!check(obj[name])) throw invalidFieldError(name, base, index) } -const printFieldName = (field, base, index) => { +const printFieldName = (field: string, base?: string, index?: number) => { if (!!base) return index == null ? `${base}.${field}` : `${base}.${index}.${field}` return field } -const missingFieldError = (field, base, index) => +const missingFieldError = (field: string, base?: string, index?: number) => new Error(`Missing field ${printFieldName(field, base, index)}`) -const invalidFieldError = (field, base, index) => +const invalidFieldError = (field: string, base?: string, index?: number) => new Error(`Invalid field ${printFieldName(field, base, index)}`) diff --git a/packages/sdk/src/resolve/resolve-accounts.js b/packages/sdk/src/resolve/resolve-accounts.ts similarity index 76% rename from packages/sdk/src/resolve/resolve-accounts.js rename to packages/sdk/src/resolve/resolve-accounts.ts index 15deaa7ec..1617bbdcd 100644 --- a/packages/sdk/src/resolve/resolve-accounts.js +++ b/packages/sdk/src/resolve/resolve-accounts.ts @@ -2,32 +2,33 @@ import {sansPrefix, withPrefix} from "@onflow/util-address" import {invariant} from "@onflow/util-invariant" import {log} from "@onflow/util-logger" import {isTransaction} from "../interaction/interaction" -import {createSignableVoucher} from "./voucher.js" +import { Interaction, InteractionAccount } from "@onflow/typedefs"; +import {createSignableVoucher} from "./voucher" import {v4 as uuidv4} from "uuid" const MAX_DEPTH_LIMIT = 5 -const idof = acct => `${withPrefix(acct.addr)}-${acct.keyId}` -const isFn = v => +const idof = (acct: InteractionAccount) => `${withPrefix(acct.addr)}-${acct.keyId}` +const isFn = (v: any): v is Function => v && (Object.prototype.toString.call(v) === "[object Function]" || "function" === typeof v || v instanceof Function) -const genAccountId = (...ids) => ids.join("-") +const genAccountId = (...ids: (string | boolean | undefined)[]) => ids.join("-") -const ROLES = { - PAYER: "payer", - PROPOSER: "proposer", - AUTHORIZATIONS: "authorizations", +enum ROLES { + PAYER = "payer", + PROPOSER = "proposer", + AUTHORIZATIONS = "authorizations", } function debug() { const SPACE = " " const SPACE_COUNT_PER_INDENT = 4 - const DEBUG_MESSAGE = [] + const DEBUG_MESSAGE: string[] = [] return [ - function (msg, indent = 0) { + function (msg = '', indent = 0) { DEBUG_MESSAGE.push( Array(indent * SPACE_COUNT_PER_INDENT) .fill(SPACE) @@ -40,7 +41,7 @@ function debug() { ] } -function recurseFlatMap(el, depthLimit = 3) { +function recurseFlatMap(el: T, depthLimit = 3) { if (depthLimit <= 0) return el if (!Array.isArray(el)) return el return recurseFlatMap( @@ -49,7 +50,7 @@ function recurseFlatMap(el, depthLimit = 3) { ) } -export function buildPreSignable(acct, ix) { +export function buildPreSignable(acct: Partial, ix: Interaction) { try { return { f_type: "PreSignable", @@ -67,12 +68,14 @@ export function buildPreSignable(acct, ix) { } } -async function removeUnusedIxAccounts(ix) { +async function removeUnusedIxAccounts(ix: Interaction, opts: Record) { const payerTempIds = Array.isArray(ix.payer) ? ix.payer : [ix.payer] const authorizersTempIds = Array.isArray(ix.authorizations) ? ix.authorizations : [ix.authorizations] - const proposerTempIds = Array.isArray(ix.proposer) + const proposerTempIds = ix.proposer === null + ? [] + : Array.isArray(ix.proposer) ? ix.proposer : [ix.proposer] @@ -88,7 +91,7 @@ async function removeUnusedIxAccounts(ix) { } } -function addAccountToIx(ix, newAccount) { +function addAccountToIx(ix: Interaction, newAccount: InteractionAccount) { if ( typeof newAccount.addr === "string" && (typeof newAccount.keyId === "number" || @@ -115,7 +118,7 @@ function addAccountToIx(ix, newAccount) { return ix.accounts[newAccount.tempId] } -function uniqueAccountsFlatMap(accounts) { +function uniqueAccountsFlatMap(accounts: InteractionAccount[]) { const flatMapped = recurseFlatMap(accounts) const seen = new Set() @@ -132,16 +135,16 @@ function uniqueAccountsFlatMap(accounts) { seen.add(accountId) return account }) - .filter(e => e !== null) + .filter(e => e !== null) as InteractionAccount[] return uniqueAccountsFlatMapped } async function recurseResolveAccount( - ix, - currentAccountTempId, + ix: Interaction, + currentAccountTempId: string, depthLimit = MAX_DEPTH_LIMIT, - {debugLogger} + {debugLogger}: {debugLogger: (msg?: string, indent?: number) => void} ) { if (depthLimit <= 0) { throw new Error( @@ -178,18 +181,18 @@ async function recurseResolveAccount( let flatResolvedAccounts = recurseFlatMap(resolvedAccounts) - flatResolvedAccounts = flatResolvedAccounts.map(flatResolvedAccount => + flatResolvedAccounts = flatResolvedAccounts.map((flatResolvedAccount: InteractionAccount) => addAccountToIx(ix, flatResolvedAccount) ) account.resolve = flatResolvedAccounts.map( - flatResolvedAccount => flatResolvedAccount.tempId + (flatResolvedAccount: InteractionAccount) => flatResolvedAccount.tempId ) account = addAccountToIx(ix, account) const recursedAccounts = await Promise.all( - flatResolvedAccounts.map(async resolvedAccount => { + flatResolvedAccounts.map(async (resolvedAccount: InteractionAccount) => { return await recurseResolveAccount( ix, resolvedAccount.tempId, @@ -214,7 +217,14 @@ async function recurseResolveAccount( return account.tempId } -async function resolveAccountType(ix, type, {debugLogger}) { +const getAccountTempIDs = (rawTempIds: string | string[] | null) => { + if (rawTempIds === null) { + return [] + } + return Array.isArray(rawTempIds) ? rawTempIds : [rawTempIds] +} + +async function resolveAccountType(ix: Interaction, type: ROLES, {debugLogger}: {debugLogger: (msg?: string, indent?: number) => void}) { invariant( ix && typeof ix === "object", "resolveAccountType Error: ix not defined" @@ -226,12 +236,12 @@ async function resolveAccountType(ix, type, {debugLogger}) { "resolveAccountType Error: type must be 'payer', 'proposer' or 'authorizations'" ) - let accountTempIDs = Array.isArray(ix[type]) ? ix[type] : [ix[type]] + let accountTempIDs = getAccountTempIDs(ix[type]) - let allResolvedAccounts = [] + let allResolvedAccounts: InteractionAccount[] = [] for (let accountId of accountTempIDs) { let account = ix.accounts[accountId] - invariant(account, `resolveAccountType Error: account not found`) + invariant(Boolean(account), `resolveAccountType Error: account not found`) let resolvedAccountTempIds = await recurseResolveAccount( ix, @@ -246,8 +256,8 @@ async function resolveAccountType(ix, type, {debugLogger}) { ? resolvedAccountTempIds : [resolvedAccountTempIds] - let resolvedAccounts = resolvedAccountTempIds.map( - resolvedAccountTempId => ix.accounts[resolvedAccountTempId] + let resolvedAccounts: InteractionAccount[] = resolvedAccountTempIds.map( + (resolvedAccountTempId: string) => ix.accounts[resolvedAccountTempId] ) let flatResolvedAccounts = uniqueAccountsFlatMap(resolvedAccounts) @@ -276,9 +286,9 @@ async function resolveAccountType(ix, type, {debugLogger}) { ) } - ix[type] = Array.isArray(ix[type]) + ix[type] = (Array.isArray(ix[type]) ? [...new Set(allResolvedAccounts.map(acct => acct.tempId))] - : allResolvedAccounts[0].tempId + : allResolvedAccounts[0].tempId) as string & string[] // Ensure all payers are of the same account if (type === ROLES.PAYER) { @@ -295,7 +305,7 @@ async function resolveAccountType(ix, type, {debugLogger}) { } } -export async function resolveAccounts(ix, opts = {}) { +export async function resolveAccounts(ix: Interaction, opts: Record = {}) { if (isTransaction(ix)) { if (!Array.isArray(ix.payer)) { log.deprecate({ diff --git a/packages/sdk/src/resolve/resolve-signatures.test.js b/packages/sdk/src/resolve/resolve-signatures.test.js index 30465ad7e..074c03d7d 100644 --- a/packages/sdk/src/resolve/resolve-signatures.test.js +++ b/packages/sdk/src/resolve/resolve-signatures.test.js @@ -1,4 +1,4 @@ -import {resolveSignatures, buildSignable} from "./resolve-signatures.js" +import {resolveSignatures, buildSignable} from "./resolve-signatures" import { build, resolve, diff --git a/packages/sdk/src/resolve/resolve-signatures.js b/packages/sdk/src/resolve/resolve-signatures.ts similarity index 73% rename from packages/sdk/src/resolve/resolve-signatures.js rename to packages/sdk/src/resolve/resolve-signatures.ts index 9254bb9a2..70c2464af 100644 --- a/packages/sdk/src/resolve/resolve-signatures.js +++ b/packages/sdk/src/resolve/resolve-signatures.ts @@ -1,16 +1,19 @@ import {isTransaction} from "../interaction/interaction" +import { Interaction, InteractionAccount } from "@onflow/typedefs"; import {sansPrefix} from "@onflow/util-address" import { + Transaction, + TransactionProposalKey, encodeTransactionPayload as encodeInsideMessage, encodeTransactionEnvelope as encodeOutsideMessage, -} from "../encode/encode.js" +} from "../encode/encode" import { createSignableVoucher, findInsideSigners, findOutsideSigners, -} from "./voucher.js" +} from "./voucher" -export async function resolveSignatures(ix) { +export async function resolveSignatures(ix: Interaction) { if (isTransaction(ix)) { try { let insideSigners = findInsideSigners(ix) @@ -28,9 +31,9 @@ export async function resolveSignatures(ix) { const outsidePayload = encodeOutsideMessage({ ...prepForEncoding(ix), payloadSigs: insideSigners.map(id => ({ - address: ix.accounts[id].addr, - keyId: ix.accounts[id].keyId, - sig: ix.accounts[id].signature, + address: ix.accounts[id].addr || '', + keyId: ix.accounts[id].keyId || 0, + sig: ix.accounts[id].signature || '', })), }) @@ -49,8 +52,8 @@ export async function resolveSignatures(ix) { return ix } -function fetchSignature(ix, payload) { - return async function innerFetchSignature(id) { +function fetchSignature(ix: Interaction, payload: string) { + return async function innerFetchSignature(id: string) { const acct = ix.accounts[id] if (acct.signature != null && acct.signature !== undefined) return const {signature} = await acct.signingFunction( @@ -60,7 +63,7 @@ function fetchSignature(ix, payload) { } } -export function buildSignable(acct, message, ix) { +export function buildSignable(acct: InteractionAccount, message: string, ix: Interaction) { try { return { f_type: "Signable", @@ -81,25 +84,28 @@ export function buildSignable(acct, message, ix) { } } -function prepForEncoding(ix) { +function prepForEncoding(ix: Interaction): Transaction { const payerAddress = sansPrefix( (Array.isArray(ix.payer) ? ix.accounts[ix.payer[0]] : ix.accounts[ix.payer]) - .addr + .addr || "" ) + + const proposalKey: TransactionProposalKey = ix.proposer ? { + address: sansPrefix(ix.accounts[ix.proposer].addr) || '', + keyId: ix.accounts[ix.proposer].keyId || 0, + sequenceNum: ix.accounts[ix.proposer].sequenceNum || 0, + } : {} + return { cadence: ix.message.cadence, - refBlock: ix.message.refBlock || null, + refBlock: ix.message.refBlock, computeLimit: ix.message.computeLimit, arguments: ix.message.arguments.map(id => ix.arguments[id].asArgument), - proposalKey: { - address: sansPrefix(ix.accounts[ix.proposer].addr), - keyId: ix.accounts[ix.proposer].keyId, - sequenceNum: ix.accounts[ix.proposer].sequenceNum, - }, + proposalKey, payer: payerAddress, authorizers: ix.authorizations - .map(cid => sansPrefix(ix.accounts[cid].addr)) - .reduce((prev, current) => { + .map(cid => sansPrefix(ix.accounts[cid].addr) || '') + .reduce((prev: string[], current) => { return prev.find(item => item === current) ? prev : [...prev, current] }, []), } diff --git a/packages/sdk/src/resolve/resolve-voucher-intercept.js b/packages/sdk/src/resolve/resolve-voucher-intercept.js index 45ede7224..f1f6ac211 100644 --- a/packages/sdk/src/resolve/resolve-voucher-intercept.js +++ b/packages/sdk/src/resolve/resolve-voucher-intercept.js @@ -1,5 +1,5 @@ import {get, isFn} from "../interaction/interaction" -import {createSignableVoucher} from "./voucher.js" +import {createSignableVoucher} from "./voucher" export async function resolveVoucherIntercept(ix) { const fn = get(ix, "ix.voucher-intercept") diff --git a/packages/sdk/src/resolve/resolve.js b/packages/sdk/src/resolve/resolve.js index 4fa5fd526..b357c7cea 100644 --- a/packages/sdk/src/resolve/resolve.js +++ b/packages/sdk/src/resolve/resolve.js @@ -12,8 +12,8 @@ import {decodeResponse as decode} from "../decode/decode.js" import {resolveCadence} from "./resolve-cadence.js" import {resolveArguments} from "./resolve-arguments.js" -import {resolveAccounts} from "./resolve-accounts.js" -import {resolveSignatures} from "./resolve-signatures.js" +import {resolveAccounts} from "./resolve-accounts" +import {resolveSignatures} from "./resolve-signatures" import {resolveValidators} from "./resolve-validators.js" import {resolveFinalNormalization} from "./resolve-final-normalization.js" import {resolveVoucherIntercept} from "./resolve-voucher-intercept.js" diff --git a/packages/sdk/src/resolve/voucher.js b/packages/sdk/src/resolve/voucher.ts similarity index 68% rename from packages/sdk/src/resolve/voucher.js rename to packages/sdk/src/resolve/voucher.ts index 0a07cbfaf..02fd1a329 100644 --- a/packages/sdk/src/resolve/voucher.js +++ b/packages/sdk/src/resolve/voucher.ts @@ -1,10 +1,13 @@ import {withPrefix} from "@onflow/util-address" -import {encodeTxIdFromVoucher} from "../encode/encode.js" +import {Voucher, encodeTxIdFromVoucher} from "../encode/encode" +import { Interaction } from "@onflow/typedefs" -export function findInsideSigners(ix) { +export function findInsideSigners(ix: Interaction) { // Inside Signers Are: (authorizers + proposer) - payer let inside = new Set(ix.authorizations) - inside.add(ix.proposer) + if (ix.proposer) { + inside.add(ix.proposer) + } if (Array.isArray(ix.payer)) { ix.payer.forEach(p => inside.delete(p)) } else { @@ -13,20 +16,20 @@ export function findInsideSigners(ix) { return Array.from(inside) } -export function findOutsideSigners(ix) { +export function findOutsideSigners(ix: Interaction) { // Outside Signers Are: (payer) let outside = new Set(Array.isArray(ix.payer) ? ix.payer : [ix.payer]) return Array.from(outside) } -export const createSignableVoucher = ix => { +export const createSignableVoucher = (ix: Interaction) => { const buildAuthorizers = () => { const authorizations = ix.authorizations .map(cid => withPrefix(ix.accounts[cid].addr)) - .reduce((prev, current) => { + .reduce((prev: (string | null)[], current) => { return prev.find(item => item === current) ? prev : [...prev, current] }, []) - return authorizations[0] ? authorizations : [] + return authorizations?.[0] ? authorizations : [] } const buildInsideSigners = () => @@ -43,16 +46,18 @@ export const createSignableVoucher = ix => { sig: ix.accounts[id].signature, })) + const proposalKey = ix.proposer ? { + address: withPrefix(ix.accounts[ix.proposer].addr), + keyId: ix.accounts[ix.proposer].keyId, + sequenceNum: ix.accounts[ix.proposer].sequenceNum, + } : {} + return { cadence: ix.message.cadence, refBlock: ix.message.refBlock || null, computeLimit: ix.message.computeLimit, arguments: ix.message.arguments.map(id => ix.arguments[id].asArgument), - proposalKey: { - address: withPrefix(ix.accounts[ix.proposer].addr), - keyId: ix.accounts[ix.proposer].keyId, - sequenceNum: ix.accounts[ix.proposer].sequenceNum, - }, + proposalKey, payer: withPrefix( ix.accounts[Array.isArray(ix.payer) ? ix.payer[0] : ix.payer].addr ), @@ -62,6 +67,6 @@ export const createSignableVoucher = ix => { } } -export const voucherToTxId = voucher => { +export const voucherToTxId = (voucher: Voucher) => { return encodeTxIdFromVoucher(voucher) } diff --git a/packages/sdk/src/sdk.ts b/packages/sdk/src/sdk.ts index 769188d47..31776b9ac 100644 --- a/packages/sdk/src/sdk.ts +++ b/packages/sdk/src/sdk.ts @@ -34,7 +34,7 @@ export { isGetCollection, isGetNetworkParameters, } from "./interaction/interaction" -export {createSignableVoucher, voucherToTxId} from "./resolve/voucher.js" +export {createSignableVoucher, voucherToTxId} from "./resolve/voucher" export {encodeMessageFromSignable} from "./wallet-utils/encode-signable.js" export {template as cadence} from "@onflow/util-template" export {template as cdc} from "@onflow/util-template" @@ -74,9 +74,9 @@ export {resolveCadence} from "./resolve/resolve-cadence.js" export {resolveFinalNormalization} from "./resolve/resolve-final-normalization" export {resolveProposerSequenceNumber} from "./resolve/resolve-proposer-sequence-number" export {resolveArguments} from "./resolve/resolve-arguments.js" -export {resolveAccounts} from "./resolve/resolve-accounts.js" +export {resolveAccounts} from "./resolve/resolve-accounts" export {response} from "./response/response" -export {resolveSignatures} from "./resolve/resolve-signatures.js" +export {resolveSignatures} from "./resolve/resolve-signatures" export {resolveValidators} from "./resolve/resolve-validators.js" export {resolveRefBlockId} from "./resolve/resolve-ref-block-id.js" export {resolveVoucherIntercept} from "./resolve/resolve-voucher-intercept.js" diff --git a/packages/sdk/src/wallet-utils/encode-signable.js b/packages/sdk/src/wallet-utils/encode-signable.js index 006d2ed4d..724c78d0a 100644 --- a/packages/sdk/src/wallet-utils/encode-signable.js +++ b/packages/sdk/src/wallet-utils/encode-signable.js @@ -2,7 +2,7 @@ import {withPrefix, sansPrefix} from "@onflow/util-address" import { encodeTransactionPayload, encodeTransactionEnvelope, -} from "../encode/encode.js" +} from "../encode/encode" const findPayloadSigners = voucher => { // Payload Signers Are: (authorizers + proposer) - payer diff --git a/packages/sdk/src/wallet-utils/encode-signable.test.js b/packages/sdk/src/wallet-utils/encode-signable.test.js index bbf8298a9..f6d88aa44 100644 --- a/packages/sdk/src/wallet-utils/encode-signable.test.js +++ b/packages/sdk/src/wallet-utils/encode-signable.test.js @@ -5,7 +5,7 @@ import { import { encodeTransactionPayload as encodeInsideMessage, encodeTransactionEnvelope as encodeOutsideMessage, -} from "../encode/encode.js" +} from "../encode/encode" const MESSAGE = { cadence: "transaction()...", diff --git a/packages/sdk/src/wallet-utils/validate-tx.js b/packages/sdk/src/wallet-utils/validate-tx.js index f56551510..03b639d0c 100644 --- a/packages/sdk/src/wallet-utils/validate-tx.js +++ b/packages/sdk/src/wallet-utils/validate-tx.js @@ -1,7 +1,7 @@ import { encodeTransactionPayload as encodeInsideMessage, encodeTransactionEnvelope as encodeOutsideMessage, -} from "../encode/encode.js" +} from "../encode/encode" import {invariant} from "@onflow/util-invariant" const isPayer = signable => { diff --git a/packages/sdk/src/wallet-utils/validate-tx.test.js b/packages/sdk/src/wallet-utils/validate-tx.test.js index fa63122f8..b0ae56c26 100644 --- a/packages/sdk/src/wallet-utils/validate-tx.test.js +++ b/packages/sdk/src/wallet-utils/validate-tx.test.js @@ -2,7 +2,7 @@ import {validateSignableTransaction} from "./" import { encodeTransactionPayload as encodeInsideMessage, encodeTransactionEnvelope as encodeOutsideMessage, -} from "../encode/encode.js" +} from "../encode/encode" const MESSAGE = { cadence: "", diff --git a/packages/typedefs/src/interaction.ts b/packages/typedefs/src/interaction.ts index 255ab74ed..9752a7b46 100644 --- a/packages/typedefs/src/interaction.ts +++ b/packages/typedefs/src/interaction.ts @@ -31,11 +31,11 @@ export enum InteractionResolverKind { export interface InteractionAccount { kind: InteractionResolverKind.ACCOUNT - tempId: string | null + tempId: string addr: string | null keyId: number | string | null sequenceNum: number | null - signature: any | null + signature: string | null signingFunction: any | null resolve: any | null role: { @@ -52,7 +52,7 @@ export interface Interaction { assigns: Record status: InteractionStatus reason: string | null - accounts: Record + accounts: Record params: Record arguments: Record message: { diff --git a/packages/util-address/src/index.ts b/packages/util-address/src/index.ts index 33e05de1a..b59ec3763 100644 --- a/packages/util-address/src/index.ts +++ b/packages/util-address/src/index.ts @@ -1,3 +1,7 @@ + +export function sansPrefix(address: null): null; +export function sansPrefix(address: string): string; +export function sansPrefix(address: string | null): string | null; /** * @description Removes 0x from address if present * @param address - Flow address