Skip to content

Commit

Permalink
feat: support customize output value
Browse files Browse the repository at this point in the history
  • Loading branch information
slient-coder committed Mar 18, 2023
1 parent 1e09039 commit 5ed9e75
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 22 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "unisat-extension",
"version": "1.1.9",
"version": "1.1.10",
"private": true,
"homepage": "https://github.com/unisat-wallet/extension#readme",
"bugs": {
Expand Down Expand Up @@ -51,7 +51,7 @@
"@types/react-dom": "^18.0.4",
"@unisat/bitcoin-hd-keyring": "0.2.0",
"@unisat/bitcoinjs-wallet": "^0.1.0",
"@unisat/ord-utils": "0.1.18",
"@unisat/ord-utils": "0.2.0",
"antd": "^4.20.4",
"antd-dayjs-webpack-plugin": "1.0.6",
"assert": "^2.0.0",
Expand Down
8 changes: 5 additions & 3 deletions src/background/controller/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,12 +625,14 @@ export class WalletController extends BaseController {
to,
inscriptionId,
utxos,
feeRate
feeRate,
outputValue
}: {
to: string;
inscriptionId: string;
utxos: UTXO[];
feeRate: number;
outputValue: number;
}) => {
const account = await preferenceService.getCurrentAccount();
if (!account) throw new Error('no current account');
Expand All @@ -656,9 +658,9 @@ export class WalletController extends BaseController {
network: psbtNetwork,
changeAddress: account.address,
pubkey: account.pubkey,
feeRate
feeRate,
outputValue
});

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
psbt.__CACHE.__UNSAFE_SIGN_NONSEGWIT = false;
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/AddressInputBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const AddressInputBar = ({
onChange: (params: { address: string; domain: string }) => void;
}) => {
const [validAddress, setValidAddress] = useState(defaultInfo.address);
const [parseAddress, setParseAddress] = useState(defaultInfo.address);
const [parseAddress, setParseAddress] = useState(defaultInfo.domain ? defaultInfo.address : '');
const [parseError, setParseError] = useState('');
const [formatError, setFormatError] = useState('');

Expand Down
78 changes: 78 additions & 0 deletions src/ui/components/OutputValueBar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Input } from 'antd';
import { useEffect, useState } from 'react';

enum FeeRateType {
CURRENT,
CUSTOM
}

export function OutputValueBar({ defaultValue, onChange }: { defaultValue: number; onChange: (val: number) => void }) {
const options = [
{
title: 'Current',
value: defaultValue
},
{
title: 'Custom'
}
];
const [optionIndex, setOptionIndex] = useState(FeeRateType.CURRENT);
const [inputVal, setInputVal] = useState('');

useEffect(() => {
let val: any = defaultValue;
if (optionIndex === FeeRateType.CUSTOM) {
val = parseInt(inputVal);
} else if (options.length > 0) {
val = options[optionIndex].value;
}
onChange(val);
}, [optionIndex, inputVal]);

return (
<div>
<div className="flex items-center !h-24 mt-2 justify-center">
{options.map((v, index) => (
<div
key={v.title}
onClick={() => {
setOptionIndex(index);
}}
className={
'text-center !h-24 w-40 px-2 py-2 rounded-md mx-2 flex flex-col justify-center cursor-pointer' +
(index === optionIndex ? ' bg-yellow-300 text-black' : '')
}
style={{ borderWidth: 1, borderColor: 'rgba(255,255,255,0.3)' }}>
<div>{v.title}</div>
{v.value && <div className="text-sm mt-1">{v.value} sats</div>}
</div>
))}
</div>
{optionIndex === FeeRateType.CUSTOM && (
<Input
className="font-semibold text-white h-15_5 box default hover !mt-5"
placeholder={'sats'}
defaultValue={inputVal}
value={inputVal}
onChange={async (e) => {
const val = e.target.value + '';
setInputVal(val);
}}
onBlur={() => {
if (inputVal) {
const val = parseInt(inputVal || '0') + '';
setInputVal(val);
}
}}
onPressEnter={(e) => {
if (inputVal) {
const val = parseInt(inputVal || '0') + '';
setInputVal(val);
}
}}
autoFocus={true}
/>
)}
</div>
);
}
67 changes: 60 additions & 7 deletions src/ui/pages/Wallet/OrdinalsTxCreateScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, Layout } from 'antd';
import { Content } from 'antd/lib/layout/layout';
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';

Expand All @@ -9,7 +9,13 @@ import { AddressInputBar } from '@/ui/components/AddressInputBar';
import CHeader from '@/ui/components/CHeader';
import { FeeRateBar } from '@/ui/components/FeeRateBar';
import InscriptionPreview from '@/ui/components/InscriptionPreview';
import { useCreateOrdinalsTxCallback, useFetchUtxosCallback, useOrdinalsTx } from '@/ui/state/transactions/hooks';
import { OutputValueBar } from '@/ui/components/OutputValueBar';
import {
useCreateOrdinalsTxCallback,
useFetchUtxosCallback,
useOrdinalsTx,
useUtxos
} from '@/ui/state/transactions/hooks';
import '@/ui/styles/domain.less';
import { isValidAddress } from '@/ui/utils';

Expand Down Expand Up @@ -39,13 +45,31 @@ export default function OrdinalsTxCreateScreen() {
fetchUtxos();
}, []);

const utxos = useUtxos();

const hasMultiInscriptions = useMemo(() => {
for (let i = 0; i < utxos.length; i++) {
const utxo = utxos[i];
if (utxo.inscriptions.find((v) => v.id === inscription.id)) {
if (utxo.inscriptions.length > 1) {
return true;
}
}
}
return false;
}, [utxos]);

const [feeRate, setFeeRate] = useState(5);
const defaultOutputValue = inscription.detail ? parseInt(inscription.detail.output_value) : 10000;

const minOutputValue = Math.max(parseInt(inscription.detail?.offset || '0'), 546);
const [outputValue, setOutputValue] = useState(defaultOutputValue);
useEffect(() => {
setDisabled(true);
setError('');

if (!isValidAddress(toInfo.address)) {
if (hasMultiInscriptions) {
setError('Multiple inscriptions are mixed together. Please split them first.');
return;
}

Expand All @@ -54,21 +78,38 @@ export default function OrdinalsTxCreateScreen() {
return;
}

if (toInfo.address == ordinalsTx.toAddress && feeRate == ordinalsTx.feeRate) {
if (outputValue < minOutputValue) {
setError(`OutputValue must be at least ${minOutputValue}`);
return;
}

if (!outputValue) {
return;
}

if (!isValidAddress(toInfo.address)) {
return;
}

if (
toInfo.address == ordinalsTx.toAddress &&
feeRate == ordinalsTx.feeRate &&
outputValue == ordinalsTx.outputValue
) {
//Prevent repeated triggering caused by setAmount
setDisabled(false);
return;
}

createOrdinalsTx(toInfo, inscription, feeRate)
createOrdinalsTx(toInfo, inscription, feeRate, outputValue)
.then(() => {
setDisabled(false);
})
.catch((e) => {
console.log(e);
setError(e.message);
});
}, [toInfo, feeRate]);
}, [toInfo, feeRate, outputValue]);

return (
<Layout className="h-full">
Expand All @@ -85,6 +126,9 @@ export default function OrdinalsTxCreateScreen() {
{inscription && <InscriptionPreview data={inscription} size="small" />}
</div>

<div className="flex justify-between w-full mt-5 box text-soft-white">
<span>{t('Recipient')}</span>
</div>
<AddressInputBar
defaultInfo={toInfo}
onChange={(val) => {
Expand All @@ -93,9 +137,18 @@ export default function OrdinalsTxCreateScreen() {
/>

<div className="flex justify-between w-full box text-soft-white">
<span>{t('Fee')}</span>
<span>{t('OutputValue')}</span>
</div>
<OutputValueBar
defaultValue={defaultOutputValue}
onChange={(val) => {
setOutputValue(val);
}}
/>

<div className="flex justify-between w-full box text-soft-white">
<span>{t('Fee')}</span>
</div>
<FeeRateBar
onChange={(val) => {
setFeeRate(val);
Expand Down
13 changes: 10 additions & 3 deletions src/ui/state/transactions/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,12 @@ export function useCreateOrdinalsTxCallback() {
const utxos = useUtxos();
const fetchUtxos = useFetchUtxosCallback();
return useCallback(
async (toInfo: { address: string; domain: string }, inscription: Inscription, feeRate: number) => {
async (
toInfo: { address: string; domain: string },
inscription: Inscription,
feeRate: number,
outputValue: number
) => {
let _utxos = utxos;
if (_utxos.length === 0) {
_utxos = await fetchUtxos();
Expand All @@ -116,7 +121,8 @@ export function useCreateOrdinalsTxCallback() {
to: toInfo.address,
inscriptionId: inscription.id,
utxos: _utxos,
feeRate
feeRate,
outputValue
});
const psbt = Psbt.fromHex(psbtHex);
const rawtx = psbt.extractTransaction().toHex();
Expand All @@ -128,7 +134,8 @@ export function useCreateOrdinalsTxCallback() {
toAddress: toInfo.address,
toDomain: toInfo.domain,
inscription,
feeRate
feeRate,
outputValue
})
);
return psbtHex;
Expand Down
5 changes: 4 additions & 1 deletion src/ui/state/transactions/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface OrdinalsTx {
psbtHex: string;
feeRate: number;
toDomain: string;
outputValue: number;
}

export interface TransactionsState {
Expand Down Expand Up @@ -71,7 +72,8 @@ export const initialState: TransactionsState = {
sending: false,
psbtHex: '',
feeRate: 5,
toDomain: ''
toDomain: '',
outputValue: 10000
},
utxos: []
};
Expand Down Expand Up @@ -119,6 +121,7 @@ const slice = createSlice({
psbtHex?: string;
feeRate?: number;
toDomain?: string;
outputValue?: number;
};
}
) {
Expand Down
8 changes: 7 additions & 1 deletion src/ui/utils/WalletContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,13 @@ export interface WalletController {
signTransaction(psbt: bitcoin.Psbt, inputs: ToSignInput[]): Promise<bitcoin.Psbt>;

sendBTC(data: { to: string; amount: number; utxos: UTXO[]; autoAdjust: boolean; feeRate: number }): Promise<string>;
sendInscription(data: { to: string; inscriptionId: string; utxos: UTXO[]; feeRate: number }): Promise<string>;
sendInscription(data: {
to: string;
inscriptionId: string;
utxos: UTXO[];
feeRate: number;
outputValue: number;
}): Promise<string>;
pushTx(rawtx: string): Promise<string>;

queryDomainInfo(domain: string): Promise<string>;
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2676,10 +2676,10 @@
tiny-secp256k1 "^2.2.1"
uuid "^9.0.0"

"@unisat/ord-utils@0.1.18":
version "0.1.18"
resolved "https://registry.yarnpkg.com/@unisat/ord-utils/-/ord-utils-0.1.18.tgz#adab2f05b0e545eb0fb65583b46b142ae68a6a37"
integrity sha512-LKJb2P/2MJ543nFaM/rL6/MFvRnDsIOOLYp8l5k+u53bypAAcSm4Rg4pUOJvdgu9c/KdfR7e0++zAZg9QANaDg==
"@unisat/ord-utils@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@unisat/ord-utils/-/ord-utils-0.2.0.tgz#461e640dd083f1fdb97474ef040802f72ccc4b87"
integrity sha512-YHDmLcOtIk9llJYE65ZrMW30CAD4BYaD6tMT5DMUu4qla+8yM/VOhPwPMoIY6hTBJNfkdRhKNLvpG83J+ThvDw==
dependencies:
bitcoinjs-lib "^6.1.0"
bs58check "^2.1.2"
Expand Down

0 comments on commit 5ed9e75

Please sign in to comment.