Skip to content

Commit

Permalink
feat(fractal): urls and chain (#222)
Browse files Browse the repository at this point in the history
* feat(fractal): urls and chain

* feat(addresses): add fractal support

---------

Co-authored-by: veralygit <[email protected]>
  • Loading branch information
Nanosync and veralygit authored Oct 30, 2024
1 parent 3c8a5ad commit d393021
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 48 deletions.
9 changes: 7 additions & 2 deletions apps/browser-tests/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ async function createAndPreparePsbt(psbtParams: PSBTBuilderOptions) {

function Transactions({
provider,
chain,
connectedAddresses,
}: {
provider: WalletProvider;
chain: Chain;
connectedAddresses: Address[];
}) {
const [inputAddressInfo, setInputAddress] = useState(connectedAddresses[0]);
Expand All @@ -53,7 +55,7 @@ function Transactions({
const [amount, setAmount] = useState(600);
const [error, setError] = useState<string | undefined>();

const psbtParams = useMemo(
const psbtParams: PSBTBuilderOptions = useMemo(
() => ({
address: inputAddressInfo.address,
feeRate,
Expand All @@ -64,10 +66,12 @@ function Transactions({
value: amount,
},
],
NETWORK,
network: NETWORK,
chain,
}),
[
amount,
chain,
feeRate,
inputAddressInfo.address,
inputAddressInfo.publicKey,
Expand Down Expand Up @@ -367,6 +371,7 @@ function App() {
</div>
<Transactions
provider={provider}
chain={chain}
connectedAddresses={connectedAddresses}
/>
</>
Expand Down
11 changes: 8 additions & 3 deletions packages/sdk/src/addresses/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,19 @@ export function getAddressesFromPublicKey(
publicKey: string | Buffer,
network: Network = "mainnet",
type: Exclude<AddressType, "p2wsh"> | "all" = "all",
chain: Chain = "bitcoin",
): Address[] {
const publicKeyBuffer = Buffer.isBuffer(publicKey)
? publicKey
: Buffer.from(publicKey, "hex");

// eslint-disable-next-line no-underscore-dangle
const _network = chain === "fractal-bitcoin" ? "mainnet" : network;

const { publicKey: bip32PublicKey } = BIP32.fromPublicKey(
publicKeyBuffer,
CHAIN_CODE,
getNetwork(network),
getNetwork(_network),
);

if (type === "all") {
Expand All @@ -136,11 +141,11 @@ export function getAddressesFromPublicKey(
Object.keys(ADDRESS_TYPE_TO_FORMAT) as AddressType[]
).filter((addressType) => addressType !== "p2wsh");
return addressTypes.map((addressType) =>
getAddressFromBip32PublicKey(bip32PublicKey, network, addressType),
getAddressFromBip32PublicKey(bip32PublicKey, _network, addressType),
);
}

return [getAddressFromBip32PublicKey(bip32PublicKey, network, type)];
return [getAddressFromBip32PublicKey(bip32PublicKey, _network, type)];
}

export function getNetworkByAddress(address: string): Network {
Expand Down
20 changes: 15 additions & 5 deletions packages/sdk/src/api/jsonrpc.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fetch from "cross-fetch";

import { API_CONFIG } from "../config";
import { Chain, Network } from "../config/types";
import { OrditSDKError } from "../errors";

type JsonRpcId = string | number | null;
Expand Down Expand Up @@ -109,12 +110,21 @@ class JsonRpc {
}
}

export const rpc = {
export const rpc: { id: number } & Record<Chain, Record<Network, JsonRpc>> = {
get id() {
return Math.floor(Math.random() * 100000);
},
mainnet: new JsonRpc(getRpcUrl(API_CONFIG.apis.mainnet.batter)),
testnet: new JsonRpc(getRpcUrl(API_CONFIG.apis.testnet.batter)),
signet: new JsonRpc(getRpcUrl(API_CONFIG.apis.signet.batter)),
regtest: new JsonRpc(getRpcUrl(API_CONFIG.apis.regtest.batter)),
bitcoin: {
mainnet: new JsonRpc(getRpcUrl(API_CONFIG.apis.bitcoin.mainnet)),
testnet: new JsonRpc(getRpcUrl(API_CONFIG.apis.bitcoin.testnet)),
signet: new JsonRpc(getRpcUrl(API_CONFIG.apis.bitcoin.signet)),
regtest: new JsonRpc(getRpcUrl(API_CONFIG.apis.bitcoin.regtest)),
},
"fractal-bitcoin": {
mainnet: new JsonRpc(getRpcUrl(API_CONFIG.apis["fractal-bitcoin"].mainnet)),
testnet: new JsonRpc(getRpcUrl(API_CONFIG.apis["fractal-bitcoin"].testnet)),
// unused
signet: new JsonRpc(getRpcUrl(API_CONFIG.apis["fractal-bitcoin"].testnet)),
regtest: new JsonRpc(getRpcUrl(API_CONFIG.apis["fractal-bitcoin"].testnet)),
},
} as const;
20 changes: 10 additions & 10 deletions packages/sdk/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
export const API_CONFIG = {
version: "0.0.0.10",
apis: {
mainnet: {
batter: "https://mainnet.ordit.io/",
bitcoin: {
mainnet: "https://mainnet.ordit.io/",
regtest: "https://regtest.ordit.io/",
testnet: "https://testnet.ordit.io/",
signet: "https://signet.ordit.io/",
},
regtest: {
batter: "https://regtest.ordit.io/",
},
testnet: {
batter: "https://testnet.ordit.io/",
},
signet: {
batter: "https://signet.ordit.io/",
"fractal-bitcoin": {
mainnet: "https://fractal.ordit.io/",
regtest: "https://fractal-regtest.ordit.io/", // unused
testnet: "https://fractal-testnet.ordit.io/",
signet: "https://fractal-signet.ordit.io/", // unused
},
},
};
21 changes: 18 additions & 3 deletions packages/sdk/src/fee/FeeEstimator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Psbt } from "bitcoinjs-lib";

import type { Network } from "../config/types";
import type { Chain, Network } from "../config/types";
import { MAXIMUM_FEE } from "../constants";
import { OrditSDKError } from "../errors";
import { getNetwork, getScriptType } from "../utils";
Expand All @@ -18,6 +18,8 @@ class FeeEstimator {
*/
protected feeRate: number;

protected chain: Chain;

protected network: Network;

protected psbt: Psbt;
Expand All @@ -28,16 +30,29 @@ class FeeEstimator {

protected weight: number = 0;

constructor({ feeRate, network, psbt, witness }: FeeEstimatorOptions) {
constructor({
feeRate,
chain = "bitcoin",
network,
psbt,
witness,
}: FeeEstimatorOptions) {
// feeRate can be 0 because a seller does not pay for network fees
if (feeRate < 0 || !Number.isSafeInteger(feeRate)) {
throw new OrditSDKError("Invalid feeRate");
}

this.feeRate = feeRate;
this.chain = chain;
this.network = network;
this.witness = witness || [];
this.psbt = psbt || new Psbt({ network: getNetwork(this.network) });
this.psbt =
psbt ||
new Psbt({
network: getNetwork(
chain === "fractal-bitcoin" ? "mainnet" : this.network,
),
});
}

get data() {
Expand Down
7 changes: 6 additions & 1 deletion packages/sdk/src/fee/types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import type { Psbt } from "bitcoinjs-lib";
import type { Buffer } from "buffer";

import type { Network } from "../config/types";
import type { Chain, Network } from "../config/types";

export interface FeeEstimatorOptions {
/**
* Fee rate in Satoshi per byte (sats/vB). Can only be a whole number.
*/
feeRate: number;

/**
* Chain to be selected
*/
chain?: Chain;

/**
* Network to be selected
*/
Expand Down
8 changes: 6 additions & 2 deletions packages/sdk/src/modules/BaseDatasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ import type {
GetUnspentsResponse,
RelayOptions,
} from "../api/types";
import type { Network } from "../config/types";
import type { Chain, Network } from "../config/types";
import type { Inscription } from "../inscription/types";
import type { UTXO, UTXOLimited } from "../transactions/types";

export interface BaseDatasourceOptions {
chain?: Chain;
network: Network;
}

export abstract class BaseDatasource {
protected readonly chain: Chain;

protected readonly network: Network;

constructor({ network }: BaseDatasourceOptions) {
constructor({ chain = "bitcoin", network }: BaseDatasourceOptions) {
this.chain = chain;
this.network = network;
}

Expand Down
23 changes: 12 additions & 11 deletions packages/sdk/src/modules/JsonRpcDatasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
GetUnspentsResponse,
RelayOptions,
} from "../api/types";
import type { Network } from "../config/types";
import type { Chain, Network } from "../config/types";
import { OrditSDKError } from "../errors";
import type { Inscription } from "../inscription/types";
import type { Transaction, UTXO, UTXOLimited } from "../transactions/types";
Expand All @@ -25,20 +25,21 @@ import type {
} from "./types";

export interface JsonRpcDatasourceOptions {
chain?: Chain;
network: Network;
}

export class JsonRpcDatasource extends BaseDatasource {
constructor({ network }: JsonRpcDatasourceOptions) {
super({ network });
constructor({ chain = "bitcoin", network }: JsonRpcDatasourceOptions) {
super({ chain, network });
}

async getBalance({ address }: GetBalanceOptions) {
if (!address) {
throw new OrditSDKError("Invalid request");
}

return rpc[this.network].call<number>(
return rpc[this.chain][this.network].call<number>(
"Address.GetBalance",
{ address },
rpc.id,
Expand All @@ -55,7 +56,7 @@ export class JsonRpcDatasource extends BaseDatasource {

const id = outpointToIdFormat(_id);

const inscription = await rpc[this.network].call<Inscription>(
const inscription = await rpc[this.chain][this.network].call<Inscription>(
"Ordinals.GetInscription",
{ id },
rpc.id,
Expand All @@ -71,7 +72,7 @@ export class JsonRpcDatasource extends BaseDatasource {

const id = outpointToIdFormat(_id);

return rpc[this.network].call<UTXO>(
return rpc[this.chain][this.network].call<UTXO>(
"Ordinals.GetInscriptionUtxo",
{ id },
rpc.id,
Expand All @@ -92,7 +93,7 @@ export class JsonRpcDatasource extends BaseDatasource {
let inscriptions: Inscription[] = [];
let next = _next;
do {
const { inscriptions: _inscriptions, pagination } = await rpc[
const { inscriptions: _inscriptions, pagination } = await rpc[this.chain][
this.network
].call<OrdinalsGetInscriptionsJsonRpcResponse>(
"Ordinals.GetInscriptions",
Expand Down Expand Up @@ -123,7 +124,7 @@ export class JsonRpcDatasource extends BaseDatasource {
throw new OrditSDKError("Invalid request");
}

return rpc[this.network].call<UTXOLimited[]>(
return rpc[this.chain][this.network].call<UTXOLimited[]>(
"Address.GetSpendables",
{
address,
Expand All @@ -148,7 +149,7 @@ export class JsonRpcDatasource extends BaseDatasource {
throw new OrditSDKError("Invalid request");
}

const tx = await rpc[this.network].call<Transaction>(
const tx = await rpc[this.chain][this.network].call<Transaction>(
"Transactions.GetTransaction",
{
txid: txId,
Expand Down Expand Up @@ -189,7 +190,7 @@ export class JsonRpcDatasource extends BaseDatasource {
let utxos: UTXO[] = [];
let next = _next;
do {
const { unspents, pagination } = await rpc[
const { unspents, pagination } = await rpc[this.chain][
this.network
].call<AddressGetUnspentsJsonRpcResponse>(
"Address.GetUnspents",
Expand Down Expand Up @@ -224,7 +225,7 @@ export class JsonRpcDatasource extends BaseDatasource {
throw new OrditSDKError("Invalid max fee rate");
}

return rpc[this.network].call<string>(
return rpc[this.chain][this.network].call<string>(
"Transactions.Relay",
{ hex, maxFeeRate, validate },
rpc.id,
Expand Down
18 changes: 14 additions & 4 deletions packages/sdk/src/transactions/PSBTBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Psbt, Transaction } from "bitcoinjs-lib";
import reverseBuffer from "buffer-reverse";

import type { Network } from "../config/types";
import type { Chain, Network } from "../config/types";
import {
INSTANT_BUY_SELLER_INPUT_INDEX,
MINIMUM_AMOUNT_IN_SATS,
Expand All @@ -23,6 +23,7 @@ export interface PSBTBuilderOptions {
address: string;
changeAddress?: string;
feeRate: number;
chain?: Chain;
network?: Network;
outputs: Output[];
publicKey: string;
Expand Down Expand Up @@ -121,6 +122,7 @@ export class PSBTBuilder extends FeeEstimator {
changeAddress,
datasource,
feeRate,
chain = "bitcoin",
network = "mainnet",
publicKey,
outputs,
Expand All @@ -129,19 +131,23 @@ export class PSBTBuilder extends FeeEstimator {
}: PSBTBuilderOptions) {
super({
feeRate,
chain,
network,
});
this.address = address;
this.changeAddress = changeAddress;
this.datasource =
datasource || new JsonRpcDatasource({ network: this.network });
datasource ||
new JsonRpcDatasource({ chain: this.chain, network: this.network });
this.outputs = outputs;
this.publicKey = publicKey;

this.autoAdjustment = autoAdjustment;
this.instantTradeMode = instantTradeMode;

this.psbt = new Psbt({ network: getNetwork(network) });
this.psbt = new Psbt({
network: getNetwork(chain === "fractal-bitcoin" ? "mainnet" : network),
});
}

toPSBT() {
Expand Down Expand Up @@ -205,7 +211,11 @@ export class PSBTBuilder extends FeeEstimator {
}

protected initPSBT() {
this.psbt = new Psbt({ network: getNetwork(this.network) }); // create new PSBT
this.psbt = new Psbt({
network: getNetwork(
this.chain === "fractal-bitcoin" ? "mainnet" : this.network,
),
}); // create new PSBT
this.psbt.setMaximumFeeRate(this.feeRate);
}

Expand Down
Loading

0 comments on commit d393021

Please sign in to comment.