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
25 changes: 24 additions & 1 deletion crates/sage/src/endpoints/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,30 @@ impl Sage {
return Err(Error::InvalidKey);
}
} else {
let mnemonic = Mnemonic::from_str(&req.key)?;
let words: Vec<&str> = req.key.split_whitespace().collect();
let word_count = words.len();

if word_count != 12 && word_count != 24 {
return Err(Error::InvalidMnemonic(format!(
"Expected 12 or 24 words, but got {word_count}."
)));
}

let mnemonic = Mnemonic::from_str(&req.key).map_err(|e| match e {
bip39::Error::BadWordCount(count) => Error::InvalidMnemonic(format!(
"Expected 12 or 24 words, but got {count}."
)),
bip39::Error::UnknownWord(idx) => Error::InvalidMnemonic(format!(
"Word #{} ({}) is not a valid BIP39 word.",
idx + 1,
words.get(idx).copied().unwrap_or("unknown"),
)),
bip39::Error::InvalidChecksum => Error::InvalidMnemonic(
"Invalid checksum. Please verify all words are correct and in the right order."
.to_string(),
),
_ => Error::InvalidMnemonic(format!("Invalid mnemonic: {e}")),
})?;
let master_sk = SecretKey::from_seed(&mnemonic.to_seed(""));
let master_pk = master_sk.public_key();
let fingerprint = if req.save_secrets {
Expand Down
11 changes: 11 additions & 0 deletions crates/sage/src/endpoints/offers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ impl Sage {
}
}

let has_offered_assets = offered.xch > 0
|| !offered.cats.is_empty()
|| !offered.nfts.is_empty()
|| !offered.options.is_empty();

if !has_offered_assets && offered.fee == 0 {
return Err(Error::InvalidAmount(
"A request-only offer requires a network fee.".to_string(),
));
}

let mut requested = Requested::default();
let mut peer = None;

Expand Down
4 changes: 4 additions & 0 deletions crates/sage/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ pub enum Error {
#[error("Invalid key")]
InvalidKey,

#[error("{0}")]
InvalidMnemonic(String),

#[error("Wrong address prefix: {0}")]
AddressPrefix(String),

Expand Down Expand Up @@ -279,6 +282,7 @@ impl Error {
Self::Bls(..)
| Self::Hex(..)
| Self::InvalidKey
| Self::InvalidMnemonic(..)
| Self::TryFromSlice(..)
| Self::TryFromInt(..)
| Self::ParseInt(..)
Expand Down
4 changes: 2 additions & 2 deletions src/components/NftCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,12 @@ export function NftCard({ nft, updateNfts, selectionState }: NftCardProps) {
<TooltipContent>
<p>
{nftName}
{nft.edition_total != null && nft.edition_total > 1 && (
{nft.edition_total != null && (nft.edition_total === 0 || nft.edition_total > 1) && (
<span>
{' '}
(
<Trans>
{nft.edition_number} of {nft.edition_total}
{nft.edition_number} of {nft.edition_total === 0 ? '∞' : nft.edition_total}
</Trans>
)
</span>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full md:max-w-lg max-w-sm translate-x-[-50%] translate-y-[-50%] gap-4 border bg-card p-6 text-card-foreground shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg',
'fixed left-[50%] top-[50%] z-50 grid w-full md:max-w-lg max-w-sm translate-x-[-50%] translate-y-[-50%] gap-4 border bg-card p-6 text-card-foreground shadow-lg duration-200 max-h-[85vh] overflow-y-auto data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg',
className,
)}
style={mergedStyles}
Expand Down
7 changes: 4 additions & 3 deletions src/pages/ImportWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ export default function ImportWallet() {
</FormControl>
<FormDescription>
<Trans>
Enter your mnemonic, private key, or public key above.
If it&apos;s a public key, it will be imported as a
read-only cold wallet.
Enter your 12 or 24-word mnemonic seed phrase, private
key, or public key. Words should be separated by
spaces. If it&apos;s a public key, it will be imported
as a read-only cold wallet.
</Trans>
</FormDescription>
<FormMessage />
Expand Down
14 changes: 14 additions & 0 deletions src/pages/MakeOffer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ export function MakeOffer() {
});
return;
}

if (
!hasOfferedTokens &&
!hasOfferedNfts &&
!hasOfferedOptions &&
parseFloat(state.fee || '0') === 0
) {
addError({
kind: 'invalid',
reason: t`A request-only offer requires a network fee.`,
});
return;
}

setIsConfirmDialogOpen(true);
};

Expand Down
4 changes: 2 additions & 2 deletions src/pages/Nft.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,10 @@ export default function Nft() {
address={nft?.launcher_id ?? ''}
/>

{nft?.edition_total != null && nft?.edition_total > 1 && (
{nft?.edition_total != null && (nft?.edition_total === 0 || nft?.edition_total > 1) && (
<LabeledItem
label={t`Edition`}
content={`${nft.edition_number} of ${nft.edition_total}`}
content={`${nft.edition_number} of ${nft.edition_total === 0 ? '∞' : nft.edition_total}`}
/>
)}

Expand Down