Skip to content

Commit

Permalink
feat: Use a deterministic ECDSA signature algorithm and support inscr…
Browse files Browse the repository at this point in the history
…ibing BRC-20 TRANSFER with decimal places.
  • Loading branch information
slient-coder committed Feb 13, 2024
1 parent 2efa31f commit dc79dc7
Show file tree
Hide file tree
Showing 15 changed files with 298 additions and 73 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@types/react": "^18.0.9",
"@types/react-cache": "^2.0.1",
"@types/react-dom": "^18.0.4",
"@unisat/wallet-sdk": "1.1.2",
"@unisat/wallet-sdk": "1.2.1",
"antd": "^4.20.4",
"antd-dayjs-webpack-plugin": "1.0.6",
"assert": "^2.0.0",
Expand Down
21 changes: 16 additions & 5 deletions src/background/controller/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { publicKeyToAddress, scriptPkToAddress } from '@unisat/wallet-sdk/lib/ad
import { ECPair, bitcoin } from '@unisat/wallet-sdk/lib/bitcoin-core';
import { signMessageOfBIP322Simple } from '@unisat/wallet-sdk/lib/message';
import { toPsbtNetwork } from '@unisat/wallet-sdk/lib/network';
import { getAddressUtxoDust } from '@unisat/wallet-sdk/lib/transaction';
import { toXOnly } from '@unisat/wallet-sdk/lib/utils';

import { ContactBookItem } from '../service/contactBook';
Expand Down Expand Up @@ -861,6 +862,14 @@ export class WalletController extends BaseController {
return Object.assign(v, { pubkey: account.pubkey });
});

const toDust = getAddressUtxoDust(to);

assetUtxos.forEach((v) => {
if (v.satoshis < toDust) {
throw new Error('Unable to send inscriptions to this address in batches, please send them one by one.');
}
});

if (!btcUtxos) {
btcUtxos = await this.getBTCUtxos();
}
Expand Down Expand Up @@ -1223,8 +1232,8 @@ export class WalletController extends BaseController {
return openapiService.getFeeSummary();
};

inscribeBRC20Transfer = (address: string, tick: string, amount: string, feeRate: number) => {
return openapiService.inscribeBRC20Transfer(address, tick, amount, feeRate);
inscribeBRC20Transfer = (address: string, tick: string, amount: string, feeRate: number, outputValue: number) => {
return openapiService.inscribeBRC20Transfer(address, tick, amount, feeRate, outputValue);
};

getInscribeResult = (orderId: string) => {
Expand Down Expand Up @@ -1484,6 +1493,8 @@ export class WalletController extends BaseController {
btcUtxos = await this.getBTCUtxos();
}

const changeDust = getAddressUtxoDust(account.address);

const _assetUtxos: UnspentOutput[] = [];
let total = 0;
let change = 0;
Expand All @@ -1493,13 +1504,13 @@ export class WalletController extends BaseController {
_assetUtxos.push(v);
if (total >= amount) {
change = total - amount;
if (change == 0 || change > 546) {
if (change == 0 || change >= changeDust) {
break;
}
}
}
if (change != 0 && change < 546) {
throw new Error('Can not construct change greater than 546.');
if (change != 0 && change < changeDust) {
throw new Error('The amount for change is too low, please adjust the sending amount.');
}
assetUtxos = _assetUtxos;

Expand Down
10 changes: 8 additions & 2 deletions src/background/service/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,14 @@ export class OpenApiService {
return this.httpGet('/address/search', { domain });
}

async inscribeBRC20Transfer(address: string, tick: string, amount: string, feeRate: number): Promise<InscribeOrder> {
return this.httpPost('/brc20/inscribe-transfer', { address, tick, amount, feeRate });
async inscribeBRC20Transfer(
address: string,
tick: string,
amount: string,
feeRate: number,
outputValue: number
): Promise<InscribeOrder> {
return this.httpPost('/brc20/inscribe-transfer', { address, tick, amount, feeRate, outputValue });
}

async getInscribeResult(orderId: string): Promise<TokenTransfer> {
Expand Down
1 change: 1 addition & 0 deletions src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ export interface Arc20Balance {
export interface TokenInfo {
totalSupply: string;
totalMinted: string;
decimal: number;
}

export enum TokenInscriptionType {
Expand Down
12 changes: 11 additions & 1 deletion src/ui/components/BRC20Preview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ export default function BRC20Preview({
if (!balance) {
balance = 'deploy';
}
let balanceSize = 'xxl';
if (balance.length < 7) {
balanceSize = 'xxl';
} else if (balance.length < 14) {
balanceSize = 'xl';
} else if (balance.length < 21) {
balanceSize = 'md';
} else {
balanceSize = 'sm';
}
return (
<Column
style={{ backgroundColor: colors.bg4, width: 100, height: 130, minWidth: 100, minHeight: 130, borderRadius: 5 }}
Expand All @@ -43,7 +53,7 @@ export default function BRC20Preview({
<Text text={tick} color="white_muted" size="lg" />
</Row>

<Text text={balance} size="xxl" textCenter />
<Text text={balance} size={balanceSize as any} textCenter wrap />
</Column>

<Column px="sm" pb="sm" gap="sm">
Expand Down
24 changes: 20 additions & 4 deletions src/ui/components/Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface InputProps {
onAmountInputChange?: (amount: string) => void;
disabled?: boolean;
disableDecimal?: boolean;
enableBrc20Decimal?: boolean;
}

type Presets = keyof typeof $inputPresets;
Expand Down Expand Up @@ -89,7 +90,15 @@ function PasswordInput(props: InputProps) {
}

function AmountInput(props: InputProps) {
const { placeholder, onAmountInputChange, disabled, style: $inputStyleOverride, disableDecimal, ...rest } = props;
const {
placeholder,
onAmountInputChange,
disabled,
style: $inputStyleOverride,
disableDecimal,
enableBrc20Decimal,
...rest
} = props;
const $style = Object.assign({}, $baseInputStyle, $inputStyleOverride, disabled ? { color: colors.textDim } : {});

if (!onAmountInputChange) {
Expand All @@ -109,9 +118,16 @@ function AmountInput(props: InputProps) {
setInputValue(value);
}
} else {
if (/^\d*\.?\d{0,8}$/.test(value) || value === '') {
setValidAmount(value);
setInputValue(value);
if (enableBrc20Decimal) {
if (/^\d*\.?\d{0,18}$/.test(value) || value === '') {
setValidAmount(value);
setInputValue(value);
}
} else {
if (/^\d*\.?\d{0,8}$/.test(value) || value === '') {
setValidAmount(value);
setInputValue(value);
}
}
}
};
Expand Down
34 changes: 32 additions & 2 deletions src/ui/components/OutputValueBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CSSProperties, useEffect, useState } from 'react';

import { colors } from '@/ui/theme/colors';

import { useTools } from '../ActionComponent';
import { Column } from '../Column';
import { Input } from '../Input';
import { Row } from '../Row';
Expand All @@ -12,7 +13,15 @@ enum FeeRateType {
CUSTOM
}

export function OutputValueBar({ defaultValue, onChange }: { defaultValue: number; onChange: (val: number) => void }) {
export function OutputValueBar({
defaultValue,
minValue,
onChange
}: {
defaultValue: number;
minValue: number;
onChange: (val: number) => void;
}) {
const options = [
{
title: 'Current',
Expand All @@ -24,17 +33,35 @@ export function OutputValueBar({ defaultValue, onChange }: { defaultValue: numbe
];
const [optionIndex, setOptionIndex] = useState(FeeRateType.CURRENT);
const [inputVal, setInputVal] = useState('');
const [currentValue, setCurrentValue] = useState(defaultValue);

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

useEffect(() => {
if (minValue && currentValue < minValue) {
// setOptionIndex(FeeRateType.CUSTOM);
// setInputVal(minValue + '');
}
}, [minValue, currentValue]);

const tools = useTools();
return (
<Column>
<Row justifyCenter>
Expand All @@ -44,6 +71,10 @@ export function OutputValueBar({ defaultValue, onChange }: { defaultValue: numbe
<div
key={v.title}
onClick={() => {
if (defaultValue < minValue && index === 0) {
tools.showTip('Can not change to a lower value');
return;
}
setOptionIndex(index);
}}
style={Object.assign(
Expand Down Expand Up @@ -74,7 +105,6 @@ export function OutputValueBar({ defaultValue, onChange }: { defaultValue: numbe
preset="amount"
disableDecimal
placeholder={'sats'}
defaultValue={inputVal}
value={inputVal}
onAmountInputChange={(val) => {
setInputVal(val);
Expand Down
Loading

0 comments on commit dc79dc7

Please sign in to comment.