Skip to content

Commit

Permalink
added runes rpc methods
Browse files Browse the repository at this point in the history
  • Loading branch information
m-aboelenein committed Apr 5, 2024
1 parent b77cd17 commit 990e23e
Show file tree
Hide file tree
Showing 9 changed files with 376 additions and 101 deletions.
5 changes: 3 additions & 2 deletions src/adapters/BaseAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Params, Requests } from '../request';
import { SatsConnectAdapter } from './satsConnectAdapter';
import { request } from '../request';
import { RpcResult } from '../types';
import { RpcErrorCode, RpcResult } from '../types';
import RunesApi from 'src/runes';

class BaseAdapter extends SatsConnectAdapter {
id = '';
Expand All @@ -11,7 +12,7 @@ class BaseAdapter extends SatsConnectAdapter {
this.id = providerId;
}

request = async <Method extends keyof Requests>(
requestInternal = async <Method extends keyof Requests>(
method: Method,
params: Params<Method>
): Promise<RpcResult<Method> | undefined> => {
Expand Down
134 changes: 132 additions & 2 deletions src/adapters/satsConnectAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,140 @@
import { RunesApi } from '../runes';
import { Params, Requests } from '../request';
import { RpcResult } from '../types';
import { RpcErrorCode, RpcResult } from '../types';
import { EstimateRunesEtchParams, EstimateRunesMintParams } from '../request/types/runesMethods';

abstract class SatsConnectAdapter {
abstract readonly id: string;

abstract request<Method extends keyof Requests>(
private async mintRunes(params: Params<'runes_mint'>): Promise<RpcResult<'runes_mint'>> {
try {
const orderResponse = await new RunesApi(params.network).createMintOrder(params);
if (orderResponse.data) {
const paymentResponse = await this.requestInternal('sendTransfer', {
recipients: [
{
address: orderResponse.data.fundAddress,
amount: orderResponse.data.fundAmount,
},
],
});
if (paymentResponse?.status === 'success') {
await new RunesApi(params.network).executeMint(
orderResponse.data.orderId,
paymentResponse.result.txid
);
return {
status: 'success',
result: {
orderId: orderResponse.data.orderId,
fundTransactionId: paymentResponse.result.txid,
},
};
} else {
return {
status: 'error',
error: {
code: RpcErrorCode.USER_REJECTION,
message: 'User rejected the payment request',
},
};
}
} else {
return {
status: 'error',
error: {
code: RpcErrorCode.INTERNAL_ERROR,
message: orderResponse.error,
},
};
}
} catch (error) {
return {
status: 'error',
error: {
code: RpcErrorCode.INTERNAL_ERROR,
message: error.message,
},
};
}
}

private async etchRunes(params: Params<'runes_etch'>): Promise<RpcResult<'runes_etch'>> {
try {
const orderResponse = await new RunesApi(params.network).createEtchOrder(params);
if (orderResponse.data) {
const paymentResponse = await this.requestInternal('sendTransfer', {
recipients: [
{
address: orderResponse.data.fundAddress,
amount: orderResponse.data.fundAmount,
},
],
});
if (paymentResponse?.status === 'success') {
await new RunesApi(params.network).executeEtch(
orderResponse.data.orderId,
paymentResponse.result.txid
);
return {
status: 'success',
result: {
orderId: orderResponse.data.orderId,
fundTransactionId: paymentResponse.result.txid,
},
};
} else {
return {
status: 'error',
error: {
code: RpcErrorCode.USER_REJECTION,
message: 'User rejected the payment request',
},
};
}
} else {
return {
status: 'error',
error: {
code: RpcErrorCode.INTERNAL_ERROR,
message: orderResponse.error,
},
};
}
} catch (error) {
return {
status: 'error',
error: {
code: RpcErrorCode.INTERNAL_ERROR,
message: error.message,
},
};
}
}

async request<Method extends keyof Requests>(
method: Method,
params: Params<Method>
): Promise<RpcResult<Method> | undefined> {
switch (method) {
case 'runes_mint':
return this.mintRunes(params as Params<'runes_mint'>) as Promise<RpcResult<Method>>;
case 'runes_etch':
return this.etchRunes(params as Params<'runes_etch'>) as Promise<RpcResult<Method>>;
case 'runes_estimateMint':
return new RunesApi((params as EstimateRunesMintParams).network).estimateMintCost(
params as EstimateRunesMintParams
) as Promise<RpcResult<Method>>;
case 'runes_estimateEtch':
return new RunesApi((params as EstimateRunesMintParams).network).estimateEtchCost(
params as EstimateRunesEtchParams
) as Promise<RpcResult<Method>>;
default:
return this.requestInternal(method, params);
}
}

protected abstract requestInternal<Method extends keyof Requests>(
method: Method,
params: Params<Method>
): Promise<RpcResult<Method> | undefined>;
Expand Down
28 changes: 10 additions & 18 deletions src/adapters/unisat.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import { Buffer } from 'buffer';
import {
GetAccountsParams,
Params,
Requests,
Return,
SendTransferParams,
SignMessageParams,
SignPsbtParams,
} from '../request';
import { Params, Requests, Return } from '../request';
import { SatsConnectAdapter } from './satsConnectAdapter';
import { RpcErrorCode, RpcResult } from '../types';
import { AddressType, getAddressInfo } from 'bitcoin-address-validation';
Expand Down Expand Up @@ -69,7 +61,7 @@ function convertSignInputsToInputType(
class UnisatAdapter extends SatsConnectAdapter {
id = DefaultAdaptersInfo.unisat.id;

private async getAccounts(params: GetAccountsParams): Promise<Return<'getAccounts'>> {
private async getAccounts(params: Params<'getAccounts'>): Promise<Return<'getAccounts'>> {
const { purposes } = params;
if (!purposes.includes(AddressPurpose.Stacks)) {
throw new Error('Only bitcoin addresses are supported');
Expand Down Expand Up @@ -103,7 +95,7 @@ class UnisatAdapter extends SatsConnectAdapter {
return response;
}

private async signMessage(params: SignMessageParams): Promise<Return<'signMessage'>> {
private async signMessage(params: Params<'signMessage'>): Promise<Return<'signMessage'>> {
const { message, address } = params;
const addressType = getAddressInfo(address).type;
const Bip322supportedTypes = [AddressType.p2wpkh, AddressType.p2tr];
Expand All @@ -123,7 +115,7 @@ class UnisatAdapter extends SatsConnectAdapter {
};
}

private async sendTransfer(params: SendTransferParams): Promise<Return<'sendTransfer'>> {
private async sendTransfer(params: Params<'sendTransfer'>): Promise<Return<'sendTransfer'>> {
const { recipients } = params;
if (recipients.length > 1) {
throw new Error('Only one recipient is supported by this wallet provider');
Expand All @@ -135,7 +127,7 @@ class UnisatAdapter extends SatsConnectAdapter {
};
}

private async signPsbt(params: SignPsbtParams): Promise<Return<'signPsbt'>> {
private async signPsbt(params: Params<'signPsbt'>): Promise<Return<'signPsbt'>> {
const { psbt, signInputs, allowedSignHash, broadcast } = params;
const psbtHex = Buffer.from(psbt, 'base64').toString('hex');
const signedPsbt = await window.unisat.signPsbt(psbtHex, {
Expand All @@ -154,37 +146,37 @@ class UnisatAdapter extends SatsConnectAdapter {
};
}

request = async <Method extends keyof Requests>(
requestInternal = async <Method extends keyof Requests>(
method: Method,
params: Params<Method>
): Promise<RpcResult<Method> | undefined> => {
try {
switch (method) {
case 'getAccounts': {
const response: Return<'getAccounts'> = await this.getAccounts(
params as GetAccountsParams
params as Params<'getAccounts'>
);
return {
status: 'success',
result: response as Return<Method>,
};
}
case 'sendTransfer': {
const response = await this.sendTransfer(params as SendTransferParams);
const response = await this.sendTransfer(params as Params<'sendTransfer'>);
return {
status: 'success',
result: response as Return<Method>,
};
}
case 'signMessage': {
const response = await this.signMessage(params as SignMessageParams);
const response = await this.signMessage(params as Params<'signMessage'>);
return {
status: 'success',
result: response as Return<Method>,
};
}
case 'signPsbt': {
const response = await this.signPsbt(params as SignPsbtParams);
const response = await this.signPsbt(params as Params<'signPsbt'>);
return {
status: 'success',
result: response as Return<Method>,
Expand Down
3 changes: 2 additions & 1 deletion src/adapters/xverse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { DefaultAdaptersInfo } from '.';
class XverseAdapter extends SatsConnectAdapter {
id = DefaultAdaptersInfo.xverse.id;

request = async <Method extends keyof Requests>(
requestInternal = async <Method extends keyof Requests>(
method: Method,
params: Params<Method>
): Promise<RpcResult<Method> | undefined> => {
console.log('XverseAdapter requestInternal', method, params);
return request(method, params, this.id);
};
}
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export * from './inscriptions';
export * from './messages';
export * from './provider';
export * from './request';
export * from './runes';
export * from './transactions';
export * from './types';
export * from './ui';
12 changes: 11 additions & 1 deletion src/request/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SignMessage,
SignPsbt,
} from './btcMethods';
import { EstimateRunesEtch, EstimateRunesMint, EtchRunes, MintRunes } from './runesMethods';
import {
StxCallContract,
StxDeployContract,
Expand Down Expand Up @@ -41,7 +42,16 @@ export interface BtcRequests {

export type BtcRequestMethod = keyof BtcRequests;

export type Requests = BtcRequests & StxRequests;
export interface RunesRequests {
runes_estimateMint: EstimateRunesMint;
runes_mint: MintRunes;
runes_estimateEtch: EstimateRunesEtch;
runes_etch: EtchRunes;
}

export type RunesRequestMethod = keyof RunesRequests;

export type Requests = BtcRequests & StxRequests & RunesRequests;

export type Return<Method> = Method extends keyof Requests ? Requests[Method]['result'] : never;
export type Params<Method> = Method extends keyof Requests ? Requests[Method]['params'] : never;
Expand Down
52 changes: 52 additions & 0 deletions src/request/types/runesMethods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
CreateEtchOrderRequest,
CreateMintOrderRequest,
EstimateEtchOrderRequest,
EstimateMintOrderRequest,
EstimateOrderResponse,
} from '../../runes/types';
import { BitcoinNetworkType, MethodParamsAndResult } from '../../types';

export interface EstimateRunesMintParams extends EstimateMintOrderRequest {
network?: BitcoinNetworkType;
}

export type EstimateRunesMintResult = EstimateOrderResponse;

export type EstimateRunesMint = MethodParamsAndResult<
EstimateRunesMintParams,
EstimateRunesMintResult
>;

export interface MintRunesParams extends CreateMintOrderRequest {
network?: BitcoinNetworkType;
}

export type MintRunesResult = {
orderId: string;
fundTransactionId: string;
};

export type MintRunes = MethodParamsAndResult<MintRunesParams, MintRunesResult>;

export interface EstimateRunesEtchParams extends EstimateEtchOrderRequest {
network?: BitcoinNetworkType;
}

export type EstimateRunesEtchResult = EstimateOrderResponse;

export type EstimateRunesEtch = MethodParamsAndResult<
EstimateRunesEtchParams,
EstimateRunesEtchResult
>;

export interface EtchRunesParams extends CreateEtchOrderRequest {
network?: BitcoinNetworkType;
}

export type EtchRunesResult = {
orderId: string;
fundTransactionId: string;
};

export type EtchRunes = MethodParamsAndResult<EtchRunesParams, EtchRunesResult>;
Loading

0 comments on commit 990e23e

Please sign in to comment.