Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,35 @@ donationRate = 10
```
<!-- tabs:end -->

## token-id

> **The ‘tokenId’ parameter defines the eToken that will be used in the button or widget.**

?> The token ID parameter is optional. It accepts a string containing the token ID. Default value is null.


**Example:**
<!-- tabs:start -->

#### **HTML**

```html
token-id="c67bf5c2b6d91cfb46a5c1772582eff80d88686887be10aa63b0945479cf4ed4"
```

#### **JavaScript**

```javascript
tokenId: "c67bf5c2b6d91cfb46a5c1772582eff80d88686887be10aa63b0945479cf4ed4"
```

#### **React**

```react
tokenId = "c67bf5c2b6d91cfb46a5c1772582eff80d88686887be10aa63b0945479cf4ed4"
```
<!-- tabs:end -->

# Contribute

PayButton is a community-driven open-source initiative. Contributions from the community are _crucial_ to the success of the project.
Expand Down
2 changes: 2 additions & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
- [disable-sound](/?id=disable-sound)
- [size](/?id=size)
- [donation-rate](/?id=donation-rate)
- [token-id](/?id=token-id)


- [Contribute](/?id=contribute)
- [Developer Quick Start](/?id=developer-quick-start)
Expand Down
30 changes: 30 additions & 0 deletions docs/zh-cn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,36 @@ donationRate = 10
```
<!-- tabs:end -->

## token-id

> **「tokenId」 参数用于定义在按钮或小组件中使用的 eToken。**

?> tokenId 参数是可选的。它接受一个包含 token ID 的字符串。默认值为 null。


**Example:**
<!-- tabs:start -->

#### **HTML**

```html
token-id="c67bf5c2b6d91cfb46a5c1772582eff80d88686887be10aa63b0945479cf4ed4"
```

#### **JavaScript**

```javascript
tokenId: "c67bf5c2b6d91cfb46a5c1772582eff80d88686887be10aa63b0945479cf4ed4"
```

#### **React**

```react
tokenId = "c67bf5c2b6d91cfb46a5c1772582eff80d88686887be10aa63b0945479cf4ed4"
```
<!-- tabs:end -->



# 贡献

Expand Down
2 changes: 2 additions & 0 deletions docs/zh-cn/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
- [disable-sound](/zh-cn/?id=disable-sound)
- [size](/zh-cn/?id=size)
- [donation-rate](/zh-cn/?id=donation-rate)
- [token-id](/zh-cn/?id=token-id)

- [贡献](/zh-cn/?id=贡献)
- [开发人员快速入门](/zh-cn/?id=开发人员快速入门)
- [入门](/zh-cn/?id=入门)
Expand Down
31 changes: 31 additions & 0 deletions docs/zh-tw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,37 @@ donationRate = 10
```
<!-- tabs:end -->

## token-id

> **「tokenId」 參數用於定義在按鈕或小工具中使用的 eToken。**

?> tokenId 參數為選填。它接受一個包含 token ID 的字串。預設值為 null。


**Example:**
<!-- tabs:start -->

#### **HTML**

```html
token-id="c67bf5c2b6d91cfb46a5c1772582eff80d88686887be10aa63b0945479cf4ed4"
```

#### **JavaScript**

```javascript
tokenId: "c67bf5c2b6d91cfb46a5c1772582eff80d88686887be10aa63b0945479cf4ed4"
```

#### **React**

```react
tokenId = "c67bf5c2b6d91cfb46a5c1772582eff80d88686887be10aa63b0945479cf4ed4"
```
<!-- tabs:end -->




# 貢獻

Expand Down
3 changes: 3 additions & 0 deletions docs/zh-tw/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
- [auto-close](/zh-tw/?id=auto-close)
- [disable-sound](/zh-tw/?id=disable-sound)
- [size](/zh-tw/?id=size)
- [donation-rate](/zh-tw/?id=donation-rate)
- [token-id](/zh-tw/?id=token-id)

- [貢獻](/zh-tw/?id=貢獻)
- [開發人員快速入門](/zh-tw/?id=開發人員快速入門)
- [入門](/zh-tw/?id=入門)
Expand Down
1 change: 1 addition & 0 deletions paybutton/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const allowedProps = [
'transactionText',
'size',
'donationRate',
'tokenId'
];

const requiredProps = [
Expand Down
8 changes: 6 additions & 2 deletions react/lib/components/PayButton/PayButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface PayButtonProps extends ButtonProps {
sizeScaleAlreadyApplied?: boolean;
donationAddress?: string;
donationRate?: number;
tokenId?: string;
}

export const PayButton = ({
Expand Down Expand Up @@ -92,7 +93,8 @@ export const PayButton = ({
size = 'md',
sizeScaleAlreadyApplied = false,
donationRate = DEFAULT_DONATION_RATE,
donationAddress = config.donationAddress
donationAddress = config.donationAddress,
tokenId,
}: PayButtonProps): React.ReactElement => {
const [dialogOpen, setDialogOpen] = useState(false);
const [disabled, setDisabled] = useState(false);
Expand Down Expand Up @@ -310,7 +312,8 @@ export const PayButton = ({
expectedPaymentId: paymentId,
currencyObj,
donationRate
}
},
tokenId,
})
}
if (altpaymentSocket === undefined && useAltpayment) {
Expand Down Expand Up @@ -466,6 +469,7 @@ export const PayButton = ({
donationRate={donationRate}
convertedCurrencyObj={convertedCurrencyObj}
setConvertedCurrencyObj={setConvertedCurrencyObj}
tokenId={tokenId}
/>
{errorMsg && (
<p
Expand Down
5 changes: 4 additions & 1 deletion react/lib/components/PaymentDialog/PaymentDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export interface PaymentDialogProps extends ButtonProps {
transactionText?: string
convertedCurrencyObj?: CurrencyObject;
setConvertedCurrencyObj?: Function;
tokenId?: string;
}

export const PaymentDialog = ({
Expand Down Expand Up @@ -129,7 +130,8 @@ export const PaymentDialog = ({
setConvertedCurrencyObj,
theme: themeProp,
donationAddress,
donationRate
donationRate,
tokenId,
}: PaymentDialogProps): React.ReactElement => {
const [success, setSuccess] = useState(false);
const [internalDisabled, setInternalDisabled] = useState(false);
Expand Down Expand Up @@ -259,6 +261,7 @@ export const PaymentDialog = ({
donationRate={donationRate}
convertedCurrencyObj={convertedCurrencyObj}
setConvertedCurrencyObj={setConvertedCurrencyObj}
tokenId={tokenId}
foot={success && (
<ButtonComponent
onClick={handleWidgetClose}
Expand Down
68 changes: 55 additions & 13 deletions react/lib/components/Widget/Widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
MINIMUM_ALTPAYMENT_CAD_AMOUNT,
} from '../../altpayment'

import { getTokenInfo } from '../../util/chronik'

export interface WidgetProps {
to: string
Expand Down Expand Up @@ -116,6 +117,7 @@ export interface WidgetProps {
convertedCurrencyObj?: CurrencyObject;
setConvertedCurrencyObj?: Function;
setPaymentId?: Function;
tokenId?: string;
}

interface StyleProps {
Expand Down Expand Up @@ -176,6 +178,7 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
donationRate = DEFAULT_DONATION_RATE,
setConvertedCurrencyObj = () => {},
setPaymentId,
tokenId,
} = props;
const [loading, setLoading] = useState(true);
const [draftAmount, setDraftAmount] = useState<string>("")
Expand Down Expand Up @@ -331,6 +334,7 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {

const [thisAmount, setThisAmount] = useState(props.amount)
const [thisCurrencyObject, setThisCurrencyObject] = useState(props.currencyObject)
const [tokenName, setTokenName] = useState<string | null>(null)

const blurCSS = isPropsTrue(disabled) ? { filter: 'blur(5px)' } : {}
// inject keyframes once (replacement for @global in makeStyles)
Expand Down Expand Up @@ -500,6 +504,10 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
)}' stroke='%23fff' stroke-width='.6'/%3E%3Cpath d='m7.2979 14.697-2.6964-2.6966 0.89292-0.8934c0.49111-0.49137 0.90364-0.88958 0.91675-0.88491 0.013104 0.0047 0.71923 0.69866 1.5692 1.5422 0.84994 0.84354 1.6548 1.6397 1.7886 1.7692l0.24322 0.23547 7.5834-7.5832 1.8033 1.8033-9.4045 9.4045z' fill='%23fff' stroke-width='.033708'/%3E%3C/svg%3E%0A`
}, [theme])

const getTokenIconUrl = useCallback((tokenId: string): string => {
return `https://icons.etokens.cash/128/${tokenId}.png`
}, [])

useEffect(() => {
if (thisCurrencyObject?.string !== undefined) {
const raw = stripFormatting(thisCurrencyObject.string);
Expand Down Expand Up @@ -541,6 +549,7 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
wsBaseUrl,
setTxsSocket: setThisTxsSocket,
setNewTxs: setThisNewTxs,
tokenId,
})
if (thisUseAltpayment) {
await setupAltpaymentSocket({
Expand Down Expand Up @@ -589,6 +598,26 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
})()
}, [thisNewTxs, to, apiBaseUrl])

useEffect(() => {
;(async (): Promise<void> => {
if (tokenId && tokenId !== '') {
try {
const tokenInfo = await getTokenInfo(tokenId, to)
const name = tokenInfo.genesisInfo.tokenTicker ?? null
setTokenName(name)
} catch (err) {
console.error('Failed to fetch token info:', err)
setTokenName(null)
setErrorMsg('Unable to load token information')
} finally {
setLoading(false)
}
return
}
setLoading(false)
})()
}, [tokenId, to])

useEffect(() => {
if (
isChild ||
Expand Down Expand Up @@ -805,7 +834,7 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
setText(
`Send ${amountToDisplay} ${thisCurrencyObject.currency} = ${convertedAmountToDisplay} ${thisAddressType}`,
)
const url = resolveUrl(thisAddressType, convertedObj.float)
const url = resolveUrl(thisAddressType, convertedObj.float, tokenId)
setUrl(url ?? "")
}
} else {
Expand All @@ -830,16 +859,16 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
amountToDisplay = amountWithDonationObj.string
}

setText(`Send ${amountToDisplay} ${cur}`)
setText(`Send ${amountToDisplay} ${tokenId ? tokenName : cur}`)
// Pass base amount (without donation) to resolveUrl
nextUrl = resolveUrl(cur, baseAmount)
nextUrl = resolveUrl(cur, baseAmount, tokenId)
} else {
setText(`Send any amount of ${thisAddressType}`)
nextUrl = resolveUrl(thisAddressType)
setText(`Send any amount of ${tokenId ? tokenName : thisAddressType}`)
nextUrl = resolveUrl(thisAddressType, undefined, tokenId)
}
setUrl(nextUrl ?? '')
}
}, [to, thisCurrencyObject, price, thisAmount, opReturn, hasPrice, isCashtabAvailable, userDonationRate, donationEnabled, disabled, donationAddress, currency, randomSatoshis, thisAddressType, shouldApplyDonation])
}, [to, thisCurrencyObject, price, thisAmount, opReturn, hasPrice, isCashtabAvailable, userDonationRate, donationEnabled, disabled, donationAddress, currency, randomSatoshis, thisAddressType, shouldApplyDonation, tokenName, tokenId])

useEffect(() => {
try {
Expand Down Expand Up @@ -961,7 +990,7 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
setRecentlyCopied(true)
}, [disabled, to, url, setCopied, setRecentlyCopied, qrLoading])

const resolveUrl = useCallback((currency: string, amount?: number) => {
const resolveUrl = useCallback((currency: string, amount?: number, tokenId?: string) => {
if (disabled || !to) return;

const prefix = CURRENCY_PREFIXES_MAP[currency.toLowerCase() as typeof CRYPTO_CURRENCIES[number]];
Expand All @@ -978,11 +1007,18 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
const donationPercent = userDonationRate / 100
// Calculate donation amount from base amount
const thisDonationAmount = amount * donationPercent

thisUrl += `?amount=${amount}`
thisUrl += `&addr=${donationAddress}&amount=${thisDonationAmount.toFixed(decimals)}`;
if (tokenId) {
thisUrl += `?token_decimalized_qty=${amount}`
} else {
thisUrl += `?amount=${amount}`
thisUrl += `&addr=${donationAddress}&amount=${thisDonationAmount.toFixed(decimals)}`;
}
} else {
thisUrl += `?amount=${amount}`
if (tokenId) {
thisUrl += `?token_decimalized_qty=${amount}`
} else {
thisUrl += `?amount=${amount}`
}
}
}

Expand All @@ -991,9 +1027,14 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
thisUrl += `${separator}op_return_raw=${opReturn}`;
}

if (tokenId) {
const separator = thisUrl.includes('?') ? '&' : '?';
thisUrl += `${separator}token_id=${tokenId}`;
}

return thisUrl;
},
[disabled, to, opReturn, userDonationRate, donationAddress, donationEnabled, shouldApplyDonation]
[disabled, to, opReturn, userDonationRate, donationAddress, donationEnabled, shouldApplyDonation, tokenId]
)
const stripFormatting = (s: string) => {
return s.replace(/,/g, '').replace(/(\.\d*?[1-9])0+$/, '$1').replace(/\.0+$/, '');
Expand Down Expand Up @@ -1033,6 +1074,7 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
}
}


const qrCode = (
<Box sx={classes.qrAnimations}>
<QRCodeSVG
Expand All @@ -1043,7 +1085,7 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
bgColor={isDarkMode ? '#1a1a1a' : '#ffffff'}
fgColor={theme.palette.tertiary as unknown as string}
imageSettings={{
src: success ? checkSvg : isValidCashAddress(to) ? bchSvg : xecSvg,
src: success ? checkSvg : tokenId ? getTokenIconUrl(tokenId) : isValidCashAddress(to) ? bchSvg : xecSvg,
excavate: false,
height: 112,
width: 112,
Expand Down
Loading