Signing Transactions from third party dApps¶
A normal flow on blockchain applications is to have dApps that interact with the blockchain on the clients behalf, these flows usually require the user to sign transactions that the dApp prepares and submit it. To faciliate this in Canton it is required that the prepared transaction is sent to the wallet for signing. An easy way of supporting this is to expose a dApp API (OpenRPC spec can be found here: https://github.com/hyperledger-labs/splice-wallet-kernel/blob/main/api-specs/openrpc-dapp-api.json ).
The specs are in OpenRPC to conform with traditional standards like for ethereum.
A client can provide access to a Wallet Providers dApp API by either embedding a wallet provider in the dApp or by connecting to an external wallet provider via a browser extension or other means. Then the dApp is able to funnel transactions through to the wallet provider for signing.
Receiving a Transaction¶
A dApp would usually call the prepareReturn endpoint or the prepareExecute endpoint. In both cases the Wallet Provider
would prepare and sign the transaction (but for the prepareExecute it would also submit it to the ledger).
You can prepare the incoming transaction using the Wallet SDK:
import {
WalletSDKImpl,
localNetAuthDefault,
localNetLedgerDefault,
signTransactionHash,
} from '@canton-network/wallet-sdk'
import { v4 } from 'uuid'
export default async function () {
const sdk = new WalletSDKImpl().configure({
logger: console,
authFactory: localNetAuthDefault,
ledgerFactory: localNetLedgerDefault,
})
const preparedCommand = global.PREPARED_COMMAND
const keys = global.EXISTING_PARTY_1_KEYS
const myParty = global.EXISTING_PARTY_1
await sdk.connect()
await sdk.setPartyId(myParty)
const preparedTransaction = await sdk.userLedger!.prepareSubmission(
preparedCommand, //the incoming command
v4() //a unique deduplication id for this transaction
)
const signature = signTransactionHash(
preparedTransaction!.preparedTransactionHash ?? '',
keys.privateKey
)
//if client calls ``prepareExecute`` then this is how they would call ``execute``
await sdk.userLedger!.executeSubmission(
preparedTransaction!,
signature,
keys.publicKey,
v4()
)
}
Reading and Visualising the Transaction¶
It is important when integrating with third party dApps to showcase the User exactly what is being signed. Once the signature is applied the transaction can be considered valid (and executed). The easiest would be to create a visualizer that takes a JSON representation of the transaction. The Json for a prepared transaction (before signature is applied) can be obtained using the Wallet SDK:
import {
WalletSDKImpl,
localNetAuthDefault,
localNetLedgerDefault,
decodePreparedTransaction,
PreparedTransaction,
} from '@canton-network/wallet-sdk'
import { v4 } from 'uuid'
export default async function () {
const sdk = new WalletSDKImpl().configure({
logger: console,
authFactory: localNetAuthDefault,
ledgerFactory: localNetLedgerDefault,
})
await sdk.connect()
const myParty = global.EXISTING_PARTY_1
const preparedCommand = global.PREPARED_COMMAND
await sdk.setPartyId(myParty)
const preparedTransaction = await sdk.userLedger!.prepareSubmission(
preparedCommand, //the incoming command
v4() //a unique deduplication id for this transaction
)
const decodedTransaction = decodePreparedTransaction(
preparedTransaction!.preparedTransaction!
)
PreparedTransaction.toJson(decodedTransaction)
// Here you can use your choice of JSON visualizer
}