diff --git a/src/client/app.ts b/src/client/app.ts index 97574df1..2a4bfedd 100644 --- a/src/client/app.ts +++ b/src/client/app.ts @@ -10,7 +10,9 @@ import type { AppSecretResponse, AppBillingResponse, } from './types/app'; +import { Keystore } from './types'; import { buildClient } from './utils/client'; +import { getOwnershipTransferTipBody, signEd25519PIN, signTipBody } from './utils'; // TODO add app api for developer document /** @@ -20,7 +22,7 @@ import { buildClient } from './utils/client'; * * Each Mixin user can only create two free apps * https://developers.mixin.one/ */ -export const AppKeystoreClient = (axiosInstance: AxiosInstance) => ({ +export const AppKeystoreClient = (axiosInstance: AxiosInstance, keystore: Keystore | undefined) => ({ /** Get information of current user's a specific app */ fetch: (appID: string): Promise => axiosInstance.get(`/apps/${appID}`), @@ -74,6 +76,19 @@ export const AppKeystoreClient = (axiosInstance: AxiosInstance) => ({ /** Removing from your share list */ unfavorite: (appID: string): Promise => axiosInstance.post(`/apps/${appID}/unfavorite`), + + // Migrate app to receiver id with the keystore of this app + // pin is the spend private key of this app + migrate: (pin: string, receiverID: string) => { + if (!keystore) throw new Error('invalid keystore to migrate app'); + const msg = getOwnershipTransferTipBody(receiverID); + const signedTipPin = signTipBody(pin, msg); + const pin_base64 = signEd25519PIN(signedTipPin, keystore); + return axiosInstance.post(`/apps/${keystore.app_id}/transfer`, { + pin_base64, + user_id: receiverID, + }); + }, }); export const AppClient = buildClient(AppKeystoreClient); diff --git a/src/client/mixin-client.ts b/src/client/mixin-client.ts index 74c67be3..8ad5fe24 100644 --- a/src/client/mixin-client.ts +++ b/src/client/mixin-client.ts @@ -26,7 +26,7 @@ import { SafeKeystoreClient } from './safe'; const KeystoreClient = (axiosInstance: AxiosInstance, keystore: Keystore | undefined, config: HTTPConfig) => ({ address: AddressKeystoreClient(axiosInstance, keystore), - app: AppKeystoreClient(axiosInstance), + app: AppKeystoreClient(axiosInstance, keystore), asset: AssetKeystoreClient(axiosInstance), blaze: BlazeKeystoreClient(keystore, config.blazeOptions), attachment: AttachmentKeystoreClient(axiosInstance), diff --git a/src/client/types/utxo.ts b/src/client/types/utxo.ts index 722a88bb..0438a33d 100644 --- a/src/client/types/utxo.ts +++ b/src/client/types/utxo.ts @@ -19,9 +19,9 @@ export interface UtxoOutput { } export interface KernelDeposit { - chain: string; - deposit_hash: string; - deposit_index: number; + chain: string; + deposit_hash: string; + deposit_index: number; } export interface SafeUtxoOutput extends UtxoOutput { diff --git a/src/client/utils/pin.ts b/src/client/utils/pin.ts index e67670f2..d38b5f08 100644 --- a/src/client/utils/pin.ts +++ b/src/client/utils/pin.ts @@ -69,6 +69,11 @@ export const getVerifyPinTipBody = (timestamp: number) => { return Buffer.from(msg); }; +export const getOwnershipTransferTipBody = (user_id: string) => { + const msg = `TIP:APP:OWNERSHIP:TRANSFER:${user_id}`; + return sha256Hash(Buffer.from(msg)); +}; + export const signTipBody = (pin: string, msg: Buffer) => { const signData = Buffer.from(ed25519.sign(msg, pin)); return signData.toString('hex');