Logos Kit

QML SDK

LogosKit for Basecamp apps: connect, read, propose, follow.

Copy sdk/qml/LogosKit/ into your module's qml/ folder (the template already has it) and add one object:

import "LogosKit"

LogosKit {
    id: kit
    visible: root.visible        // status polling pauses while hidden
    onApiChanged: if (api) restore()
}

kit.api is null until the SDK is ready, and it's rebuilt when the wallet switches networks: react in onApiChanged, and read kit.api each time rather than keeping a copy. kit.sdk holds helpers (isUserRejection, isAccountId, parseUnits, formatUnits, program builders).

Properties

PropertyDefaultMeaning
chain"lez:testnet"The network. Follows the wallet unless followWallet is off.
followWallettrueCheck the wallet's network on start, when shown, and every 5 s while visible.
visibletrueBind to your view; hidden apps don't poll.
module"logos_kit_wallet"The wallet core module.

Calls

Every call returns a promise. User-facing ones open the wallet; the rest are reads.

CallKindResolves to
connect({ accountKinds, capabilities, signIn })wallet sheetSession: { sessionId, accounts[], chains, capabilities }
getSession()readSession or null
getAccounts()readthe accounts shared with you
getChainId()readthe wallet's network, e.g. "lez:testnet"
getWalletBalance(account, asset?)read{ amount, asset, synced, asOfBlock }
readAccount(account, program)read{ nonce, data } (a public account's data for one program)
transfer(from, to, amount, token?)wallet sheet{ handle } once approved
sendCall(account, call)wallet sheet{ handle }: any program call built with kit.sdk
sendTransaction(proposal)wallet sheet{ handle }: a raw LWS-0 proposal
postTestimonial({ program, author, text, username? })wallet sheet{ handle }
getTestimonials(program, { limit })read{ count, monthly, latest[] }
getTestimonial(program, author)readthe author's post, or null
getTransactionStatus(handle)readTransactionStatus
watchTransaction(handle, onUpdate, onError?)read (polls)a handle with stop(); onUpdate on each change, stops at the first final lifecycle (included counts)
waitForTransactionStatus(handle)read (polls)the first final TransactionStatus (included, rejected, dropped, expired)
signMessage(account, base64)wallet sheet{ signature, publicKey, tag }
signIn(request)wallet sheet{ account, signedMessage, signature }
requestFunds(account)wallet sheet{ status, amount?, retryAfterSeconds?, shieldHandle? }
openExplorer({ txHash } | { account })readopens the explorer in the browser
isBusy()–a wallet sheet is open for this app

Amounts

Amounts are strings in base units: lepta for the native token (1 LGO = 10^9 lepta). That holds for what transfer takes and for what getWalletBalance and a funded requestFunds return. Show them as LGO with kit.sdk.formatUnits(amount, 9), and turn what the user types into lepta with kit.sdk.parseUnits(text, 9). Never Number() them.

kit.sdk.formatUnits("2500000000", 9)   // "2.5" (LGO)
kit.sdk.parseUnits("2.5", 9)           // "2500000000" (lepta)

Transaction lifecycle

A proposal resolves when the user approves, not when it lands. Follow the handle:

awaiting_approval → building → proving → signing → submitted → included
                                                          ↘ rejected | dropped | expired

watchTransaction and waitForTransactionStatus stop at the first final lifecycle: included, rejected, dropped or expired. The protocol also reserves finalized, but the Logos Kit wallet doesn't report it yet: a transaction's status ends at included, with block.id set to the block it landed in. If your app needs depth, compare status.block.id with the chain's current block later (a node read) instead of waiting on the watcher.

Inclusion alone isn't success: LEZ 0.3 includes failed transactions too. Read outcome on the final status:

outcomeMeaningShow
successThe wallet proved the effect (e.g. your record exists, the balance moved by the amount).Done
failureIncluded, but the program refused it. The fee was charged.The error, and a retry
unknownIncluded, but the wallet couldn't prove the effect either way."Included, outcome unconfirmed", then re-read the state you expected to change

Only success should show a Done screen. proving.phase is set while a private transaction proves (minutes on a laptop). Public calls skip proving.

One sheet at a time

While a wallet sheet is open for your app, another user-facing call rejects with 6107 (RequestPending). A call that waits 45 s rejects with 6108 (Timeout), but the wallet may still be showing it. A timeout does not mean nothing was sent: the user can still approve, and the SDK keeps new requests blocked until the wallet really answers.

That late answer arrives on the lateResult signal. Handle it to recover the handle of a send the user approved after the timeout:

LogosKit {
    id: kit
    onLateResult: function (intent, ok, data) {
        // intent: e.g. "lez.transaction.send"; data: the result, or a LezError when !ok
        if (intent === "lez.transaction.send" && ok && data.handle)
            root.follow(data.handle)          // same path as an on-time approval
        else if (!ok && !kit.sdk.isUserRejection(data))
            root.showError(data)
    }
}

When a call rejects with 6108, show "Waiting for the wallet" rather than an error, and let onLateResult finish the flow.

On this page