-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds signWithEckoWallet and quicksignWithEckoWallet functions
- Loading branch information
Showing
12 changed files
with
713 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { ISigningCap } from '@kadena/types'; | ||
|
||
import { IPactCommand } from './IPactCommand'; | ||
|
||
export interface ISigningRequest { | ||
code: string; | ||
data?: Record<string, unknown>; | ||
caps: ISigningCap[]; | ||
nonce?: string; | ||
chainId?: IPactCommand['meta']['chainId']; | ||
gasLimit?: number; | ||
gasPrice?: number; | ||
ttl?: number; | ||
sender?: string; | ||
extraSigners?: string[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { | ||
IEckoAccountsResponse, | ||
IEckoConnectOrStatusResponse, | ||
} from './eckoTypes'; | ||
|
||
export const isInstalled = (): boolean => { | ||
const { kadena } = window; | ||
return Boolean(kadena && kadena.isKadena); | ||
}; | ||
|
||
export const isConnected = async (networkId: string): Promise<boolean> => { | ||
if (!isInstalled()) { | ||
return false; | ||
} | ||
|
||
const checkStatusResponse = | ||
await window.kadena?.request<IEckoConnectOrStatusResponse>({ | ||
method: 'kda_checkStatus', | ||
networkId, | ||
}); | ||
|
||
if (checkStatusResponse?.status === 'fail') { | ||
return false; | ||
} | ||
|
||
return true; | ||
}; | ||
|
||
export const connect = async (networkId: string): Promise<boolean> => { | ||
if (!isInstalled()) { | ||
throw new Error('Ecko Wallet is not installed'); | ||
} | ||
|
||
if (await isConnected(networkId)) { | ||
return true; | ||
} | ||
|
||
const connectResponse = | ||
await window.kadena?.request<IEckoConnectOrStatusResponse>({ | ||
method: 'kda_connect', | ||
networkId, | ||
}); | ||
|
||
if (connectResponse?.status === 'fail') { | ||
throw new Error('User declined connection'); | ||
} | ||
|
||
return true; | ||
}; | ||
|
||
export const getAccountInfo = async ( | ||
networkId: string, | ||
): Promise<IEckoAccountsResponse | undefined> => { | ||
return window.kadena?.request<IEckoAccountsResponse>({ | ||
method: 'kda_requestAccount', | ||
networkId, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { ICommand } from '@kadena/types'; | ||
|
||
import { IQuicksignResponseOutcomes } from '../../signing-api/v1/quicksign'; | ||
|
||
import { | ||
connect, | ||
getAccountInfo, | ||
isConnected, | ||
isInstalled, | ||
} from './eckoCommon'; | ||
|
||
export type EckoStatus = 'success' | 'fail'; | ||
|
||
export interface ICommonEckoFunctions { | ||
isInstalled: typeof isInstalled; | ||
isConnected: typeof isConnected; | ||
connect: typeof connect; | ||
getAccountInfo: typeof getAccountInfo; | ||
} | ||
|
||
export interface IEckoConnectOrStatusResponse { | ||
status: EckoStatus; | ||
message?: string; | ||
account?: { | ||
account: string; | ||
publicKey: string; | ||
connectedSites: string[]; | ||
}; | ||
} | ||
|
||
export interface IEckoSignResponse { | ||
status: EckoStatus; | ||
signedCmd: ICommand; | ||
} | ||
|
||
export interface IEckoQuicksignSuccessResponse { | ||
status: 'success'; | ||
quickSignData: IQuicksignResponseOutcomes['responses']; | ||
} | ||
|
||
export interface IEckoQuicksignFailResponse { | ||
status: 'fail'; | ||
error: string; | ||
} | ||
|
||
export type IEckoQuicksignResponse = | ||
| IEckoQuicksignSuccessResponse | ||
| IEckoQuicksignFailResponse; | ||
|
||
export interface IEckoAccountsResponse { | ||
status: EckoStatus; | ||
message?: string; | ||
wallet?: { | ||
account: string; | ||
publicKey: string; | ||
connectedSites: string[]; | ||
balance: number; | ||
}; | ||
} |
100 changes: 100 additions & 0 deletions
100
packages/libs/client/src/signing/eckoWallet/quicksignWithEckoWallet.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { ICommand, IUnsignedCommand } from '@kadena/types'; | ||
|
||
import { ISignFunction } from '../ISignFunction'; | ||
import { addSignatures } from '../utils/addSignature'; | ||
import { parseTransactionCommand } from '../utils/parseTransactionCommand'; | ||
|
||
import { | ||
connect, | ||
getAccountInfo, | ||
isConnected, | ||
isInstalled, | ||
} from './eckoCommon'; | ||
import { ICommonEckoFunctions, IEckoQuicksignResponse } from './eckoTypes'; | ||
|
||
interface IEckoSignFunction extends ISignFunction, ICommonEckoFunctions {} | ||
|
||
/** | ||
* Creates the quicksignWithWalletConnect function with interface {@link ISingleSignFunction} | ||
* | ||
* @public | ||
*/ | ||
export function createEckoWalletQuicksign(): IEckoSignFunction { | ||
const quicksignWithEckoWallet: IEckoSignFunction = (async ( | ||
transactionList: IUnsignedCommand | Array<IUnsignedCommand | ICommand>, | ||
) => { | ||
if (transactionList === undefined) { | ||
throw new Error('No transaction(s) to sign'); | ||
} | ||
const isList = Array.isArray(transactionList); | ||
const transactions = isList ? transactionList : [transactionList]; | ||
|
||
const transactionHashes: string[] = []; | ||
|
||
const { networkId } = parseTransactionCommand(transactions[0]); | ||
|
||
const commandSigDatas = transactions.map((pactCommand) => { | ||
const { cmd, hash } = pactCommand; | ||
const parsedTransaction = parseTransactionCommand(pactCommand); | ||
transactionHashes.push(hash); | ||
|
||
if (networkId !== parsedTransaction.networkId) { | ||
throw new Error('Network is not equal for all transactions'); | ||
} | ||
|
||
return { | ||
cmd, | ||
sigs: parsedTransaction.signers.map((signer, i) => ({ | ||
pubKey: signer.pubKey, | ||
sig: pactCommand.sigs[i]?.sig ?? null, | ||
})), | ||
}; | ||
}); | ||
|
||
const eckoResponse = await window.kadena?.request<IEckoQuicksignResponse>({ | ||
method: 'kda_requestQuickSign', | ||
data: { | ||
networkId, | ||
commandSigDatas, | ||
}, | ||
}); | ||
|
||
if (!eckoResponse || eckoResponse?.status === 'fail') { | ||
throw new Error('Error signing transaction'); | ||
} | ||
|
||
const response = { | ||
responses: eckoResponse.quickSignData, | ||
}; | ||
|
||
if ('responses' in response) { | ||
response.responses.map((signedCommand, i) => { | ||
if (signedCommand.outcome.result === 'success') { | ||
if (signedCommand.outcome.hash !== transactionHashes[i]) { | ||
throw new Error( | ||
`Hash of the transaction signed by the wallet does not match. Our hash: ${transactionHashes[i]}, wallet hash: ${signedCommand.outcome.hash}`, | ||
); | ||
} | ||
|
||
const sigs = signedCommand.commandSigData.sigs.filter( | ||
(sig) => sig.sig !== null, | ||
) as { pubKey: string; sig: string }[]; | ||
|
||
// Add the signature(s) that we received from the wallet to the PactCommand(s) | ||
transactions[i] = addSignatures(transactions[i], ...sigs); | ||
} | ||
}); | ||
} else { | ||
throw new Error('Error signing transaction'); | ||
} | ||
|
||
return isList ? transactions : transactions[0]; | ||
}) as IEckoSignFunction; | ||
|
||
quicksignWithEckoWallet.isInstalled = isInstalled; | ||
quicksignWithEckoWallet.isConnected = isConnected; | ||
quicksignWithEckoWallet.connect = connect; | ||
quicksignWithEckoWallet.getAccountInfo = getAccountInfo; | ||
|
||
return quicksignWithEckoWallet; | ||
} |
63 changes: 63 additions & 0 deletions
63
packages/libs/client/src/signing/eckoWallet/signWithEckoWallet.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { ISingleSignFunction } from '../ISignFunction'; | ||
import { pactCommandToSigningRequest } from '../utils/pactCommandToSigningRequest'; | ||
import { parseTransactionCommand } from '../utils/parseTransactionCommand'; | ||
|
||
import { | ||
connect, | ||
getAccountInfo, | ||
isConnected, | ||
isInstalled, | ||
} from './eckoCommon'; | ||
import { ICommonEckoFunctions, IEckoSignResponse } from './eckoTypes'; | ||
|
||
declare global { | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
interface Window { | ||
kadena?: { | ||
isKadena: boolean; | ||
request<T>(args: unknown): Promise<T>; | ||
}; | ||
} | ||
} | ||
|
||
interface IEckoSignSingleFunction | ||
extends ISingleSignFunction, | ||
ICommonEckoFunctions {} | ||
|
||
/** | ||
* Creates the signWithEckoWallet function with interface {@link ISingleSignFunction} | ||
* | ||
* @remarks | ||
* It is preferred to use the {@link createEckoWalletQuicksign} function | ||
* | ||
* @public | ||
*/ | ||
export function createEckoWalletSign(): IEckoSignSingleFunction { | ||
const signWithEckoWallet: IEckoSignSingleFunction = async (transaction) => { | ||
const parsedTransaction = parseTransactionCommand(transaction); | ||
const signingRequest = pactCommandToSigningRequest(parsedTransaction); | ||
|
||
await connect(parsedTransaction.networkId); | ||
|
||
const response = await window.kadena?.request<IEckoSignResponse>({ | ||
method: 'kda_requestSign', | ||
data: { | ||
networkId: parsedTransaction.networkId, | ||
signingCmd: signingRequest, | ||
}, | ||
}); | ||
|
||
if (response?.signedCmd === undefined) { | ||
throw new Error('Error signing transaction'); | ||
} | ||
|
||
return response.signedCmd; | ||
}; | ||
|
||
signWithEckoWallet.isInstalled = isInstalled; | ||
signWithEckoWallet.isConnected = isConnected; | ||
signWithEckoWallet.connect = connect; | ||
signWithEckoWallet.getAccountInfo = getAccountInfo; | ||
|
||
return signWithEckoWallet; | ||
} |
Oops, something went wrong.