many fixes
This commit is contained in:
parent
7286741c41
commit
a4d04a60ae
|
|
@ -551,6 +551,7 @@ dependencies = [
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"skein",
|
"skein",
|
||||||
"sled",
|
"sled",
|
||||||
|
"socket2 0.5.10",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
"windows-service",
|
"windows-service",
|
||||||
|
|
@ -2077,7 +2078,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2 0.6.4",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
|
@ -3528,7 +3529,7 @@ dependencies = [
|
||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2",
|
"socket2 0.6.4",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
@ -3565,7 +3566,7 @@ dependencies = [
|
||||||
"cfg_aliases 0.2.1",
|
"cfg_aliases 0.2.1",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2",
|
"socket2 0.6.4",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
@ -4456,6 +4457,16 @@ version = "1.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
|
|
@ -5080,7 +5091,7 @@ dependencies = [
|
||||||
"parking_lot 0.12.5",
|
"parking_lot 0.12.5",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2 0.6.4",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
@ -5126,7 +5137,7 @@ dependencies = [
|
||||||
"postgres-protocol",
|
"postgres-protocol",
|
||||||
"postgres-types",
|
"postgres-types",
|
||||||
"rand 0.10.1",
|
"rand 0.10.1",
|
||||||
"socket2",
|
"socket2 0.6.4",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"whoami",
|
"whoami",
|
||||||
|
|
|
||||||
|
|
@ -391,8 +391,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions" data-transfer-actions>
|
<div class="form-actions" data-transfer-actions>
|
||||||
<button class="secondary-button" type="button" data-transfer-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-transfer-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-transfer-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-transfer-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message" data-transfer-message></div>
|
<div class="startup-message" data-transfer-message></div>
|
||||||
|
|
||||||
|
|
@ -420,8 +420,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions hidden" data-create-token-actions>
|
<div class="form-actions hidden" data-create-token-actions>
|
||||||
<button class="secondary-button" type="button" data-create-token-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-create-token-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-create-token-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-create-token-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message hidden" data-create-token-message></div>
|
<div class="startup-message hidden" data-create-token-message></div>
|
||||||
|
|
||||||
|
|
@ -447,8 +447,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions hidden" data-issue-token-actions>
|
<div class="form-actions hidden" data-issue-token-actions>
|
||||||
<button class="secondary-button" type="button" data-issue-token-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-issue-token-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-issue-token-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-issue-token-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message hidden" data-issue-token-message></div>
|
<div class="startup-message hidden" data-issue-token-message></div>
|
||||||
|
|
||||||
|
|
@ -467,6 +467,13 @@
|
||||||
<span>NFT Name</span>
|
<span>NFT Name</span>
|
||||||
<input data-create-nft-name maxlength="15" placeholder="3 to 15 characters">
|
<input data-create-nft-name maxlength="15" placeholder="3 to 15 characters">
|
||||||
</label>
|
</label>
|
||||||
|
<label>
|
||||||
|
<span>Ownership</span>
|
||||||
|
<select data-create-nft-ownership>
|
||||||
|
<option value="0">Indivisible</option>
|
||||||
|
<option value="1">Fractional</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>IPFS CID</span>
|
<span>IPFS CID</span>
|
||||||
<input data-create-nft-ipfs maxlength="100" placeholder="CIDv0 or CIDv1">
|
<input data-create-nft-ipfs maxlength="100" placeholder="CIDv0 or CIDv1">
|
||||||
|
|
@ -491,8 +498,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions hidden" data-create-nft-actions>
|
<div class="form-actions hidden" data-create-nft-actions>
|
||||||
<button class="secondary-button" type="button" data-create-nft-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-create-nft-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-create-nft-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-create-nft-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message hidden" data-create-nft-message></div>
|
<div class="startup-message hidden" data-create-nft-message></div>
|
||||||
|
|
||||||
|
|
@ -675,8 +682,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions hidden" data-loan-payment-actions>
|
<div class="form-actions hidden" data-loan-payment-actions>
|
||||||
<button class="secondary-button" type="button" data-loan-payment-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-loan-payment-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-loan-payment-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-loan-payment-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message hidden" data-loan-payment-message></div>
|
<div class="startup-message hidden" data-loan-payment-message></div>
|
||||||
|
|
||||||
|
|
@ -703,8 +710,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions hidden" data-collateral-claim-actions>
|
<div class="form-actions hidden" data-collateral-claim-actions>
|
||||||
<button class="secondary-button" type="button" data-collateral-claim-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-collateral-claim-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-collateral-claim-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-collateral-claim-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message hidden" data-collateral-claim-message></div>
|
<div class="startup-message hidden" data-collateral-claim-message></div>
|
||||||
|
|
||||||
|
|
@ -729,8 +736,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions hidden" data-burn-actions>
|
<div class="form-actions hidden" data-burn-actions>
|
||||||
<button class="secondary-button" type="button" data-burn-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-burn-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-burn-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-burn-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message hidden" data-burn-message></div>
|
<div class="startup-message hidden" data-burn-message></div>
|
||||||
|
|
||||||
|
|
@ -781,8 +788,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions hidden" data-marketing-actions>
|
<div class="form-actions hidden" data-marketing-actions>
|
||||||
<button class="secondary-button" type="button" data-marketing-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-marketing-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-marketing-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-marketing-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message hidden" data-marketing-message></div>
|
<div class="startup-message hidden" data-marketing-message></div>
|
||||||
|
|
||||||
|
|
@ -833,8 +840,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions hidden" data-data-storage-actions>
|
<div class="form-actions hidden" data-data-storage-actions>
|
||||||
<button class="secondary-button" type="button" data-data-storage-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-data-storage-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-data-storage-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-data-storage-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message hidden" data-data-storage-message></div>
|
<div class="startup-message hidden" data-data-storage-message></div>
|
||||||
|
|
||||||
|
|
@ -857,8 +864,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions hidden" data-vanity-actions>
|
<div class="form-actions hidden" data-vanity-actions>
|
||||||
<button class="secondary-button" type="button" data-vanity-save>Save Transaction</button>
|
<button class="secondary-button" type="button" data-vanity-save>Sign and Save</button>
|
||||||
<button class="primary-button" type="button" data-vanity-broadcast>Broadcast Transaction</button>
|
<button class="primary-button" type="button" data-vanity-broadcast>Sign and Broadcast</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="startup-message hidden" data-vanity-message></div>
|
<div class="startup-message hidden" data-vanity-message></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ const issueTokenMessage = document.querySelector("[data-issue-token-message]");
|
||||||
const createNftForm = document.querySelector("[data-create-nft-form]");
|
const createNftForm = document.querySelector("[data-create-nft-form]");
|
||||||
const createNftActions = document.querySelector("[data-create-nft-actions]");
|
const createNftActions = document.querySelector("[data-create-nft-actions]");
|
||||||
const createNftSeriesSelect = document.querySelector("[data-create-nft-series]");
|
const createNftSeriesSelect = document.querySelector("[data-create-nft-series]");
|
||||||
|
const createNftOwnershipSelect = document.querySelector("[data-create-nft-ownership]");
|
||||||
const createNftNameInput = document.querySelector("[data-create-nft-name]");
|
const createNftNameInput = document.querySelector("[data-create-nft-name]");
|
||||||
const createNftIpfsInput = document.querySelector("[data-create-nft-ipfs]");
|
const createNftIpfsInput = document.querySelector("[data-create-nft-ipfs]");
|
||||||
const createNftCountInput = document.querySelector("[data-create-nft-count]");
|
const createNftCountInput = document.querySelector("[data-create-nft-count]");
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ function selectedBurnAsset() {
|
||||||
return {
|
return {
|
||||||
asset: option.dataset.asset || "",
|
asset: option.dataset.asset || "",
|
||||||
nftSeries: Number(option.dataset.nftSeries || 0),
|
nftSeries: Number(option.dataset.nftSeries || 0),
|
||||||
|
ownershipType: Number(option.dataset.ownershipType || -1),
|
||||||
|
isNft: option.dataset.isNft === "true",
|
||||||
balance: BigInt(option.dataset.balance || "0")
|
balance: BigInt(option.dataset.balance || "0")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +57,9 @@ function populateBurnAssets() {
|
||||||
option.dataset.asset = balance.asset || "";
|
option.dataset.asset = balance.asset || "";
|
||||||
option.dataset.nftSeries = String(balance.nft_series || 0);
|
option.dataset.nftSeries = String(balance.nft_series || 0);
|
||||||
option.dataset.balance = String(balance.balance || "0");
|
option.dataset.balance = String(balance.balance || "0");
|
||||||
|
const metadata = nftOwnershipMetadata(balance.asset, balance.nft_series);
|
||||||
|
option.dataset.isNft = String(Boolean(metadata));
|
||||||
|
option.dataset.ownershipType = String(metadata?.ownershipType ?? -1);
|
||||||
option.textContent = burnAssetOptionLabel(balance);
|
option.textContent = burnAssetOptionLabel(balance);
|
||||||
burnAssetSelect.appendChild(option);
|
burnAssetSelect.appendChild(option);
|
||||||
});
|
});
|
||||||
|
|
@ -70,7 +75,7 @@ function populateBurnAssets() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureBurnBalances() {
|
async function ensureBurnBalances() {
|
||||||
if (activeWalletBalances.length || !walletLoaded || !registrationRegistered) {
|
if (!walletLoaded || !registrationRegistered) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const invoke = window.__TAURI__?.core?.invoke;
|
const invoke = window.__TAURI__?.core?.invoke;
|
||||||
|
|
@ -78,8 +83,13 @@ async function ensureBurnBalances() {
|
||||||
if (!invoke || !broadcastNode) {
|
if (!invoke || !broadcastNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
burnBalancesRefreshPromise ||= invoke("get_active_wallet_balances", { broadcastNode })
|
burnBalancesRefreshPromise ||= Promise.all([
|
||||||
.then((balances) => {
|
activeWalletBalances.length
|
||||||
|
? Promise.resolve(activeWalletBalances)
|
||||||
|
: invoke("get_active_wallet_balances", { broadcastNode }),
|
||||||
|
ensureNftOwnershipRegistry()
|
||||||
|
])
|
||||||
|
.then(([balances]) => {
|
||||||
activeWalletBalances = Array.isArray(balances) ? balances : [];
|
activeWalletBalances = Array.isArray(balances) ? balances : [];
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
|
@ -113,16 +123,19 @@ function updateBurnDerivedFields() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isNft = selected.nftSeries > 0;
|
const isNft = selected.isNft;
|
||||||
|
const isIndivisible = isNft && selected.ownershipType === 0;
|
||||||
if (burnAvailable) {
|
if (burnAvailable) {
|
||||||
burnAvailable.textContent = `${atomicToDecimal(selected.balance)} ${selected.asset}`;
|
burnAvailable.textContent = `${atomicToDecimal(selected.balance)} ${selected.asset}`;
|
||||||
}
|
}
|
||||||
if (burnType) {
|
if (burnType) {
|
||||||
burnType.textContent = isNft ? `NFT #${selected.nftSeries}` : "Token";
|
burnType.textContent = isNft
|
||||||
|
? (selected.nftSeries > 0 ? `NFT #${selected.nftSeries}` : "NFT / RWA")
|
||||||
|
: "Token";
|
||||||
}
|
}
|
||||||
if (burnAmountInput) {
|
if (burnAmountInput) {
|
||||||
burnAmountInput.disabled = isNft;
|
burnAmountInput.disabled = isIndivisible;
|
||||||
if (isNft) {
|
if (isIndivisible) {
|
||||||
burnAmountInput.value = "1.00000000";
|
burnAmountInput.value = "1.00000000";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +156,7 @@ function validateBurnForm() {
|
||||||
if (amount === null || amount <= 0n) {
|
if (amount === null || amount <= 0n) {
|
||||||
return "Enter a valid burn amount.";
|
return "Enter a valid burn amount.";
|
||||||
}
|
}
|
||||||
if (selected.nftSeries > 0 && amount !== ATOMIC_UNITS_PER_COIN) {
|
if (selected.isNft && selected.ownershipType === 0 && amount !== ATOMIC_UNITS_PER_COIN) {
|
||||||
return "NFT burns must destroy exactly 1.00000000 units.";
|
return "NFT burns must destroy exactly 1.00000000 units.";
|
||||||
}
|
}
|
||||||
if (amount > selected.balance) {
|
if (amount > selected.balance) {
|
||||||
|
|
@ -209,7 +222,7 @@ async function handleBurnAction(action) {
|
||||||
setBurnButtonsDisabled(true);
|
setBurnButtonsDisabled(true);
|
||||||
try {
|
try {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
setBurnMessage("Saving burn transaction...");
|
setBurnMessage("Signing and saving burn transaction...");
|
||||||
const review = await invoke("save_burn_transaction", {
|
const review = await invoke("save_burn_transaction", {
|
||||||
...burnPayload(),
|
...burnPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
@ -224,7 +237,7 @@ async function handleBurnAction(action) {
|
||||||
if (!broadcastNode) {
|
if (!broadcastNode) {
|
||||||
throw new Error("Select a broadcast node before broadcasting.");
|
throw new Error("Select a broadcast node before broadcasting.");
|
||||||
}
|
}
|
||||||
setBurnMessage("Broadcasting burn transaction...");
|
setBurnMessage("Signing and broadcasting burn transaction...");
|
||||||
const result = await invoke("broadcast_burn_transaction", {
|
const result = await invoke("broadcast_burn_transaction", {
|
||||||
...burnPayload(),
|
...burnPayload(),
|
||||||
broadcastNode,
|
broadcastNode,
|
||||||
|
|
@ -233,6 +246,12 @@ async function handleBurnAction(action) {
|
||||||
if (typeof invalidateAssetsCatalog === "function") {
|
if (typeof invalidateAssetsCatalog === "function") {
|
||||||
invalidateAssetsCatalog();
|
invalidateAssetsCatalog();
|
||||||
}
|
}
|
||||||
|
if (typeof invalidateDashboardNftBalanceKeys === "function") {
|
||||||
|
invalidateDashboardNftBalanceKeys();
|
||||||
|
}
|
||||||
|
if (typeof resetNftsPage === "function") {
|
||||||
|
resetNftsPage();
|
||||||
|
}
|
||||||
activeWalletBalances = [];
|
activeWalletBalances = [];
|
||||||
clearBurnForm();
|
clearBurnForm();
|
||||||
setBurnMessage(result.response || "Burn transaction broadcast.", "success");
|
setBurnMessage(result.response || "Burn transaction broadcast.", "success");
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ async function handleCollateralClaimAction(action) {
|
||||||
setCollateralClaimButtonsDisabled(true);
|
setCollateralClaimButtonsDisabled(true);
|
||||||
try {
|
try {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
setCollateralClaimMessage("Saving collateral claim transaction...");
|
setCollateralClaimMessage("Signing and saving collateral claim transaction...");
|
||||||
const review = await invoke("save_collateral_claim_transaction", {
|
const review = await invoke("save_collateral_claim_transaction", {
|
||||||
...collateralClaimPayload(),
|
...collateralClaimPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
@ -178,7 +178,7 @@ async function handleCollateralClaimAction(action) {
|
||||||
refreshUnbroadcastTransactions();
|
refreshUnbroadcastTransactions();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setCollateralClaimMessage("Broadcasting collateral claim...");
|
setCollateralClaimMessage("Signing and broadcasting collateral claim...");
|
||||||
const result = await invoke("broadcast_collateral_claim_transaction", {
|
const result = await invoke("broadcast_collateral_claim_transaction", {
|
||||||
...collateralClaimPayload(),
|
...collateralClaimPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ function createNftSeriesValue() {
|
||||||
return Number(createNftSeriesSelect?.value || 0) === 1 ? 1 : 0;
|
return Number(createNftSeriesSelect?.value || 0) === 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNftOwnershipValue() {
|
||||||
|
return Number(createNftOwnershipSelect?.value || 0) === 1 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeCreateNftCountInput() {
|
function normalizeCreateNftCountInput() {
|
||||||
const isCollection = createNftSeriesValue() === 1;
|
const isCollection = createNftSeriesValue() === 1;
|
||||||
if (createNftCountInput) {
|
if (createNftCountInput) {
|
||||||
|
|
@ -20,6 +24,12 @@ function normalizeCreateNftCountInput() {
|
||||||
createNftCountInput.value = "2";
|
createNftCountInput.value = "2";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (createNftOwnershipSelect) {
|
||||||
|
createNftOwnershipSelect.disabled = isCollection;
|
||||||
|
if (isCollection) {
|
||||||
|
createNftOwnershipSelect.value = "0";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCreateNftDerivedFields({ clampCount = false } = {}) {
|
function updateCreateNftDerivedFields({ clampCount = false } = {}) {
|
||||||
|
|
@ -108,6 +118,7 @@ function validateCreateNftForm() {
|
||||||
function createNftPayload() {
|
function createNftPayload() {
|
||||||
return {
|
return {
|
||||||
series: createNftSeriesValue(),
|
series: createNftSeriesValue(),
|
||||||
|
ownershipType: createNftOwnershipValue(),
|
||||||
nftName: String(createNftNameInput?.value || "").trim(),
|
nftName: String(createNftNameInput?.value || "").trim(),
|
||||||
itemIpfs: String(createNftIpfsInput?.value || "").trim(),
|
itemIpfs: String(createNftIpfsInput?.value || "").trim(),
|
||||||
count: Number.parseInt(createNftCountInput?.value || "1", 10),
|
count: Number.parseInt(createNftCountInput?.value || "1", 10),
|
||||||
|
|
@ -120,6 +131,9 @@ function clearCreateNftForm() {
|
||||||
if (createNftSeriesSelect) {
|
if (createNftSeriesSelect) {
|
||||||
createNftSeriesSelect.value = "0";
|
createNftSeriesSelect.value = "0";
|
||||||
}
|
}
|
||||||
|
if (createNftOwnershipSelect) {
|
||||||
|
createNftOwnershipSelect.value = "0";
|
||||||
|
}
|
||||||
if (createNftNameInput) {
|
if (createNftNameInput) {
|
||||||
createNftNameInput.value = "";
|
createNftNameInput.value = "";
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +195,7 @@ async function handleCreateNftAction(action) {
|
||||||
setCreateNftButtonsDisabled(true);
|
setCreateNftButtonsDisabled(true);
|
||||||
try {
|
try {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
setCreateNftMessage("Saving NFT transaction...");
|
setCreateNftMessage("Signing and saving NFT transaction...");
|
||||||
const review = await invoke("save_create_nft_transaction", {
|
const review = await invoke("save_create_nft_transaction", {
|
||||||
...createNftPayload(),
|
...createNftPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
@ -196,7 +210,7 @@ async function handleCreateNftAction(action) {
|
||||||
if (!broadcastNode) {
|
if (!broadcastNode) {
|
||||||
throw new Error("Select a broadcast node before broadcasting.");
|
throw new Error("Select a broadcast node before broadcasting.");
|
||||||
}
|
}
|
||||||
setCreateNftMessage("Broadcasting NFT transaction...");
|
setCreateNftMessage("Signing and broadcasting NFT transaction...");
|
||||||
const result = await invoke("broadcast_create_nft_transaction", {
|
const result = await invoke("broadcast_create_nft_transaction", {
|
||||||
...createNftPayload(),
|
...createNftPayload(),
|
||||||
broadcastNode,
|
broadcastNode,
|
||||||
|
|
@ -230,6 +244,7 @@ createNftSeriesSelect?.addEventListener("change", () => {
|
||||||
updateCreateNftDerivedFields({ clampCount: true });
|
updateCreateNftDerivedFields({ clampCount: true });
|
||||||
setCreateNftMessage("");
|
setCreateNftMessage("");
|
||||||
});
|
});
|
||||||
|
createNftOwnershipSelect?.addEventListener("change", () => setCreateNftMessage(""));
|
||||||
createNftCountInput?.addEventListener("input", () => {
|
createNftCountInput?.addEventListener("input", () => {
|
||||||
updateCreateNftDerivedFields();
|
updateCreateNftDerivedFields();
|
||||||
setCreateNftMessage("");
|
setCreateNftMessage("");
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ async function handleCreateTokenAction(action) {
|
||||||
setCreateTokenButtonsDisabled(true);
|
setCreateTokenButtonsDisabled(true);
|
||||||
try {
|
try {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
setCreateTokenMessage("Saving token transaction...");
|
setCreateTokenMessage("Signing and saving token transaction...");
|
||||||
const review = await invoke("save_create_token_transaction", {
|
const review = await invoke("save_create_token_transaction", {
|
||||||
...createTokenPayload(),
|
...createTokenPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
@ -133,7 +133,7 @@ async function handleCreateTokenAction(action) {
|
||||||
if (!broadcastNode) {
|
if (!broadcastNode) {
|
||||||
throw new Error("Select a broadcast node before broadcasting.");
|
throw new Error("Select a broadcast node before broadcasting.");
|
||||||
}
|
}
|
||||||
setCreateTokenMessage("Broadcasting token transaction...");
|
setCreateTokenMessage("Signing and broadcasting token transaction...");
|
||||||
const result = await invoke("broadcast_create_token_transaction", {
|
const result = await invoke("broadcast_create_token_transaction", {
|
||||||
...createTokenPayload(),
|
...createTokenPayload(),
|
||||||
broadcastNode,
|
broadcastNode,
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,7 @@ async function handleDataStorageAction(action) {
|
||||||
setDataStorageButtonsDisabled(true);
|
setDataStorageButtonsDisabled(true);
|
||||||
try {
|
try {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
setDataStorageMessage("Saving data storage transaction...");
|
setDataStorageMessage("Signing and saving data storage transaction...");
|
||||||
const review = await invoke("save_data_storage_transaction", {
|
const review = await invoke("save_data_storage_transaction", {
|
||||||
...dataStoragePayload(),
|
...dataStoragePayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
@ -386,7 +386,7 @@ async function handleDataStorageAction(action) {
|
||||||
if (!broadcastNode) {
|
if (!broadcastNode) {
|
||||||
throw new Error("Select a broadcast node before broadcasting.");
|
throw new Error("Select a broadcast node before broadcasting.");
|
||||||
}
|
}
|
||||||
setDataStorageMessage("Broadcasting data storage transaction...");
|
setDataStorageMessage("Signing and broadcasting data storage transaction...");
|
||||||
const result = await invoke("broadcast_data_storage_transaction", {
|
const result = await invoke("broadcast_data_storage_transaction", {
|
||||||
...dataStoragePayload(),
|
...dataStoragePayload(),
|
||||||
broadcastNode,
|
broadcastNode,
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ async function handleIssueTokenAction(action) {
|
||||||
setIssueTokenButtonsDisabled(true);
|
setIssueTokenButtonsDisabled(true);
|
||||||
try {
|
try {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
setIssueTokenMessage("Saving issue-token transaction...");
|
setIssueTokenMessage("Signing and saving issue-token transaction...");
|
||||||
const review = await invoke("save_issue_token_transaction", {
|
const review = await invoke("save_issue_token_transaction", {
|
||||||
...issueTokenPayload(),
|
...issueTokenPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
@ -233,7 +233,7 @@ async function handleIssueTokenAction(action) {
|
||||||
refreshUnbroadcastTransactions();
|
refreshUnbroadcastTransactions();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setIssueTokenMessage("Broadcasting issue-token transaction...");
|
setIssueTokenMessage("Signing and broadcasting issue-token transaction...");
|
||||||
const result = await invoke("broadcast_issue_token_transaction", {
|
const result = await invoke("broadcast_issue_token_transaction", {
|
||||||
...issueTokenPayload(),
|
...issueTokenPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ function selectedLoanCollateral() {
|
||||||
asset: option.dataset.asset || "",
|
asset: option.dataset.asset || "",
|
||||||
kind: option.dataset.kind || "token",
|
kind: option.dataset.kind || "token",
|
||||||
series: Number(option.dataset.series || 0),
|
series: Number(option.dataset.series || 0),
|
||||||
|
ownershipType: Number(option.dataset.ownershipType || -1),
|
||||||
wireName: option.dataset.wireName || option.dataset.asset || ""
|
wireName: option.dataset.wireName || option.dataset.asset || ""
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -120,6 +121,7 @@ function populateLoanCollateralSelect() {
|
||||||
option.dataset.asset = asset.asset;
|
option.dataset.asset = asset.asset;
|
||||||
option.dataset.kind = asset.kind;
|
option.dataset.kind = asset.kind;
|
||||||
option.dataset.series = String(asset.series || 0);
|
option.dataset.series = String(asset.series || 0);
|
||||||
|
option.dataset.ownershipType = String(asset.ownershipType ?? -1);
|
||||||
option.dataset.wireName = asset.wireName;
|
option.dataset.wireName = asset.wireName;
|
||||||
loanCollateralSelect.appendChild(option);
|
loanCollateralSelect.appendChild(option);
|
||||||
});
|
});
|
||||||
|
|
@ -167,6 +169,7 @@ async function loadLoanOfferAssets() {
|
||||||
const tokenRows = Array.isArray(tokens) ? tokens : [];
|
const tokenRows = Array.isArray(tokens) ? tokens : [];
|
||||||
const nftRows = Array.isArray(nfts) ? nfts : [];
|
const nftRows = Array.isArray(nfts) ? nfts : [];
|
||||||
loanNftKeys = new Set(nftRows.map((nft) => loanNftKey(nft.nft_name, nft.series)));
|
loanNftKeys = new Set(nftRows.map((nft) => loanNftKey(nft.nft_name, nft.series)));
|
||||||
|
updateNftOwnershipRegistry(nftRows, key);
|
||||||
|
|
||||||
loanCollateralAssets = [
|
loanCollateralAssets = [
|
||||||
{
|
{
|
||||||
|
|
@ -193,7 +196,10 @@ async function loadLoanOfferAssets() {
|
||||||
wireName: series > 0 ? `${asset}_${series}` : asset,
|
wireName: series > 0 ? `${asset}_${series}` : asset,
|
||||||
series,
|
series,
|
||||||
kind: "nft",
|
kind: "nft",
|
||||||
label: series > 0 ? `${asset} #${series} (NFT)` : `${asset} (NFT)`
|
ownershipType: Number(nft.ownership_type),
|
||||||
|
label: series > 0
|
||||||
|
? `${asset} #${series} (NFT)`
|
||||||
|
: `${asset} (${Number(nft.ownership_type) === 1 ? "Fractional NFT / RWA" : "NFT"})`
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
].filter((asset) => {
|
].filter((asset) => {
|
||||||
|
|
@ -229,7 +235,7 @@ function updateLoanOfferDerivedFields() {
|
||||||
if (loanFeeInput) {
|
if (loanFeeInput) {
|
||||||
loanFeeInput.value = atomicToDecimal(LOAN_FEE_ATOMIC);
|
loanFeeInput.value = atomicToDecimal(LOAN_FEE_ATOMIC);
|
||||||
}
|
}
|
||||||
if (collateral?.kind === "nft") {
|
if (collateral?.kind === "nft" && collateral.ownershipType === 0) {
|
||||||
loanCollateralAmountInput.value = "1";
|
loanCollateralAmountInput.value = "1";
|
||||||
loanCollateralAmountInput.disabled = true;
|
loanCollateralAmountInput.disabled = true;
|
||||||
} else if (loanCollateralAmountInput) {
|
} else if (loanCollateralAmountInput) {
|
||||||
|
|
@ -273,9 +279,7 @@ function validateLoanOfferForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const loanAmount = decimalToAtomic(loanAmountInput?.value);
|
const loanAmount = decimalToAtomic(loanAmountInput?.value);
|
||||||
const collateralAmount = collateral.kind === "nft"
|
const collateralAmount = decimalToAtomic(loanCollateralAmountInput?.value);
|
||||||
? ATOMIC_UNITS_PER_COIN
|
|
||||||
: decimalToAtomic(loanCollateralAmountInput?.value);
|
|
||||||
const paymentAmount = decimalToAtomic(loanPaymentAmountInput?.value);
|
const paymentAmount = decimalToAtomic(loanPaymentAmountInput?.value);
|
||||||
const maxLateValue = decimalToAtomic(loanMaxLateValueInput?.value);
|
const maxLateValue = decimalToAtomic(loanMaxLateValueInput?.value);
|
||||||
if (loanAmount === null || loanAmount <= 0n) {
|
if (loanAmount === null || loanAmount <= 0n) {
|
||||||
|
|
@ -284,6 +288,13 @@ function validateLoanOfferForm() {
|
||||||
if (collateralAmount === null || collateralAmount <= 0n) {
|
if (collateralAmount === null || collateralAmount <= 0n) {
|
||||||
return "Enter a valid collateral amount.";
|
return "Enter a valid collateral amount.";
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
collateral.kind === "nft"
|
||||||
|
&& collateral.ownershipType === 0
|
||||||
|
&& collateralAmount !== ATOMIC_UNITS_PER_COIN
|
||||||
|
) {
|
||||||
|
return "Indivisible NFT collateral must be exactly 1.";
|
||||||
|
}
|
||||||
if (paymentAmount === null || paymentAmount <= 0n) {
|
if (paymentAmount === null || paymentAmount <= 0n) {
|
||||||
return "Enter a valid payment amount.";
|
return "Enter a valid payment amount.";
|
||||||
}
|
}
|
||||||
|
|
@ -330,9 +341,7 @@ function validateLoanOfferForm() {
|
||||||
function loanOfferPayload() {
|
function loanOfferPayload() {
|
||||||
const loanAsset = selectedLoanAsset();
|
const loanAsset = selectedLoanAsset();
|
||||||
const collateral = selectedLoanCollateral();
|
const collateral = selectedLoanCollateral();
|
||||||
const collateralAmount = collateral.kind === "nft"
|
const collateralAmount = decimalToAtomic(loanCollateralAmountInput.value);
|
||||||
? ATOMIC_UNITS_PER_COIN
|
|
||||||
: decimalToAtomic(loanCollateralAmountInput.value);
|
|
||||||
return {
|
return {
|
||||||
broadcastNode: activeBroadcastNode(),
|
broadcastNode: activeBroadcastNode(),
|
||||||
loanCoin: loanAsset.asset,
|
loanCoin: loanAsset.asset,
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ async function handleLoanPaymentAction(action) {
|
||||||
setLoanPaymentButtonsDisabled(true);
|
setLoanPaymentButtonsDisabled(true);
|
||||||
try {
|
try {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
setLoanPaymentMessage("Saving loan payment transaction...");
|
setLoanPaymentMessage("Signing and saving loan payment transaction...");
|
||||||
const review = await invoke("save_loan_payment_transaction", {
|
const review = await invoke("save_loan_payment_transaction", {
|
||||||
...loanPaymentPayload(),
|
...loanPaymentPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
@ -238,7 +238,7 @@ async function handleLoanPaymentAction(action) {
|
||||||
refreshUnbroadcastTransactions();
|
refreshUnbroadcastTransactions();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setLoanPaymentMessage("Broadcasting loan payment...");
|
setLoanPaymentMessage("Signing and broadcasting loan payment...");
|
||||||
const result = await invoke("broadcast_loan_payment_transaction", {
|
const result = await invoke("broadcast_loan_payment_transaction", {
|
||||||
...loanPaymentPayload(),
|
...loanPaymentPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ async function handleMarketingAction(action) {
|
||||||
setMarketingButtonsDisabled(true);
|
setMarketingButtonsDisabled(true);
|
||||||
try {
|
try {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
setMarketingMessage("Saving marketing transaction...");
|
setMarketingMessage("Signing and saving marketing transaction...");
|
||||||
const review = await invoke("save_marketing_transaction", {
|
const review = await invoke("save_marketing_transaction", {
|
||||||
...marketingPayload(),
|
...marketingPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
@ -141,7 +141,7 @@ async function handleMarketingAction(action) {
|
||||||
if (!broadcastNode) {
|
if (!broadcastNode) {
|
||||||
throw new Error("Select a broadcast node before broadcasting.");
|
throw new Error("Select a broadcast node before broadcasting.");
|
||||||
}
|
}
|
||||||
setMarketingMessage("Broadcasting marketing transaction...");
|
setMarketingMessage("Signing and broadcasting marketing transaction...");
|
||||||
const result = await invoke("broadcast_marketing_transaction", {
|
const result = await invoke("broadcast_marketing_transaction", {
|
||||||
...marketingPayload(),
|
...marketingPayload(),
|
||||||
broadcastNode,
|
broadcastNode,
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,9 @@ function ownedNftRowsFromBalances(balances, registry) {
|
||||||
nft_name: nftName,
|
nft_name: nftName,
|
||||||
series,
|
series,
|
||||||
balance: String(balance.balance || "0"),
|
balance: String(balance.balance || "0"),
|
||||||
origin_txid: nft.origin_txid || ""
|
origin_txid: nft.origin_txid || "",
|
||||||
|
ownership_type: Number(nft.ownership_type || 0),
|
||||||
|
remaining_supply: String(nft.remaining_supply || "0")
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|
@ -197,12 +199,29 @@ function metadataDescription(metadata) {
|
||||||
return String(metadata?.description || metadata?.summary || "").trim();
|
return String(metadata?.description || metadata?.summary || "").trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nftOwnershipPercent(balance, remainingSupply) {
|
||||||
|
const atomic = BigInt(String(balance || "0"));
|
||||||
|
const supply = BigInt(String(remainingSupply || "0"));
|
||||||
|
if (supply <= 0n) {
|
||||||
|
return "0%";
|
||||||
|
}
|
||||||
|
const scaledPercent = atomic * 100000000n / supply;
|
||||||
|
const whole = scaledPercent / 1000000n;
|
||||||
|
const fraction = String(scaledPercent % 1000000n).padStart(6, "0").replace(/0+$/, "");
|
||||||
|
return fraction ? `${whole}.${fraction}%` : `${whole}%`;
|
||||||
|
}
|
||||||
|
|
||||||
function renderNftCard(nft) {
|
function renderNftCard(nft) {
|
||||||
const key = nftKey(nft);
|
const key = nftKey(nft);
|
||||||
const name = nftDisplayName(nft);
|
const name = nftDisplayName(nft);
|
||||||
const image = nftImageUri(nft);
|
const image = nftImageUri(nft);
|
||||||
const type = nftTypeLabel(nft);
|
const type = nftTypeLabel(nft);
|
||||||
const active = key === selectedNftKey ? " active" : "";
|
const active = key === selectedNftKey ? " active" : "";
|
||||||
|
const fractional = Number(nft.ownership_type || 0) === 1;
|
||||||
|
const ownershipAtomic = BigInt(String(nft.balance || "0"));
|
||||||
|
const ownershipLabel = fractional
|
||||||
|
? `${nftOwnershipPercent(ownershipAtomic, nft.remaining_supply)} ownership`
|
||||||
|
: "Complete ownership";
|
||||||
const preview = image && nft.gateway
|
const preview = image && nft.gateway
|
||||||
? `<img src="${escapeNftText(ipfsToGatewayUrl(image, nft.gateway))}" alt="">`
|
? `<img src="${escapeNftText(ipfsToGatewayUrl(image, nft.gateway))}" alt="">`
|
||||||
: `<div class="asset-mark art">${escapeNftText(assetInitial(name))}</div>`;
|
: `<div class="asset-mark art">${escapeNftText(assetInitial(name))}</div>`;
|
||||||
|
|
@ -214,6 +233,7 @@ function renderNftCard(nft) {
|
||||||
<div>
|
<div>
|
||||||
<strong>${escapeNftText(name)}</strong>
|
<strong>${escapeNftText(name)}</strong>
|
||||||
<span>${escapeNftText(type)} - ${escapeNftText(nft.asset_name || `${nft.nft_name}_${nft.series}`)}</span>
|
<span>${escapeNftText(type)} - ${escapeNftText(nft.asset_name || `${nft.nft_name}_${nft.series}`)}</span>
|
||||||
|
<span>${escapeNftText(ownershipLabel)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="nft-link-row">
|
<div class="nft-link-row">
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const SWAP_FEE_ATOMIC = 100000000n;
|
const SWAP_FEE_ATOMIC = 100000000n;
|
||||||
let swapOfferLoadPromise = null;
|
let swapOfferLoadPromise = null;
|
||||||
let swapNetworkAssets = [];
|
let swapNetworkAssets = [];
|
||||||
let swapNftKeys = new Set();
|
let swapNftMetadata = new Map();
|
||||||
let swapOfferAssetsKey = "";
|
let swapOfferAssetsKey = "";
|
||||||
|
|
||||||
function setSwapOfferMessage(message, type = "") {
|
function setSwapOfferMessage(message, type = "") {
|
||||||
|
|
@ -18,10 +18,14 @@ function swapAssetLabel(asset, includeBalance = false) {
|
||||||
? `${asset.asset} #${series}`
|
? `${asset.asset} #${series}`
|
||||||
: asset.asset;
|
: asset.asset;
|
||||||
if (!includeBalance) {
|
if (!includeBalance) {
|
||||||
return asset.kind === "nft" ? `${name} (NFT)` : name;
|
return asset.kind === "nft"
|
||||||
|
? `${name} (${asset.ownershipType === 1 ? "Fractional NFT / RWA" : "NFT"})`
|
||||||
|
: name;
|
||||||
}
|
}
|
||||||
if (asset.kind === "nft") {
|
if (asset.kind === "nft") {
|
||||||
return `${name} (1 item)`;
|
return asset.ownershipType === 1
|
||||||
|
? `${name} (${formatAtomicBalance(String(asset.balance || "0"))})`
|
||||||
|
: `${name} (1 item)`;
|
||||||
}
|
}
|
||||||
return `${name} (${formatAtomicBalance(String(asset.balance || "0"))})`;
|
return `${name} (${formatAtomicBalance(String(asset.balance || "0"))})`;
|
||||||
}
|
}
|
||||||
|
|
@ -35,6 +39,7 @@ function selectedSwapAsset(select) {
|
||||||
asset: option.dataset.asset || "",
|
asset: option.dataset.asset || "",
|
||||||
nftSeries: Number(option.dataset.nftSeries || 0),
|
nftSeries: Number(option.dataset.nftSeries || 0),
|
||||||
kind: option.dataset.kind || "token",
|
kind: option.dataset.kind || "token",
|
||||||
|
ownershipType: Number(option.dataset.ownershipType || -1),
|
||||||
balance: BigInt(option.dataset.balance || "0")
|
balance: BigInt(option.dataset.balance || "0")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -59,6 +64,7 @@ function populateSwapAssetSelect(select, assets, includeBalance) {
|
||||||
option.dataset.asset = asset.asset;
|
option.dataset.asset = asset.asset;
|
||||||
option.dataset.nftSeries = String(Number(asset.nftSeries || 0));
|
option.dataset.nftSeries = String(Number(asset.nftSeries || 0));
|
||||||
option.dataset.kind = asset.kind;
|
option.dataset.kind = asset.kind;
|
||||||
|
option.dataset.ownershipType = String(asset.ownershipType ?? -1);
|
||||||
option.dataset.balance = String(asset.balance || "0");
|
option.dataset.balance = String(asset.balance || "0");
|
||||||
select.appendChild(option);
|
select.appendChild(option);
|
||||||
});
|
});
|
||||||
|
|
@ -73,9 +79,10 @@ function populateSwapAssetSelect(select, assets, includeBalance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function swapRegistryNftKeys(nfts) {
|
function swapRegistryNftKeys(nfts) {
|
||||||
return new Set(nfts.map((nft) => {
|
return new Map(nfts.map((nft) => [
|
||||||
return `${String(nft.nft_name || "").trim().toLowerCase()}|${Number(nft.series || 0)}`;
|
nftRegistryKey(nft.nft_name, nft.series),
|
||||||
}));
|
Number(nft.ownership_type)
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshSwapOfferSelects() {
|
function refreshSwapOfferSelects() {
|
||||||
|
|
@ -84,11 +91,13 @@ function refreshSwapOfferSelects() {
|
||||||
.map((balance) => {
|
.map((balance) => {
|
||||||
const asset = String(balance.asset || "").trim();
|
const asset = String(balance.asset || "").trim();
|
||||||
const nftSeries = Number(balance.nft_series || 0);
|
const nftSeries = Number(balance.nft_series || 0);
|
||||||
const isNft = swapNftKeys.has(`${asset.toLowerCase()}|${nftSeries}`);
|
const ownershipType = swapNftMetadata.get(nftRegistryKey(asset, nftSeries));
|
||||||
|
const isNft = ownershipType !== undefined;
|
||||||
return {
|
return {
|
||||||
asset,
|
asset,
|
||||||
nftSeries,
|
nftSeries,
|
||||||
kind: isNft ? "nft" : (isBaseTransferAsset(asset) ? "coin" : "token"),
|
kind: isNft ? "nft" : (isBaseTransferAsset(asset) ? "coin" : "token"),
|
||||||
|
ownershipType: isNft ? ownershipType : -1,
|
||||||
balance: String(balance.balance || "0")
|
balance: String(balance.balance || "0")
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -125,7 +134,8 @@ async function loadSwapOfferAssets() {
|
||||||
activeWalletBalances = Array.isArray(balances) ? balances : [];
|
activeWalletBalances = Array.isArray(balances) ? balances : [];
|
||||||
const tokenRows = Array.isArray(tokens) ? tokens : [];
|
const tokenRows = Array.isArray(tokens) ? tokens : [];
|
||||||
const nftRows = Array.isArray(nfts) ? nfts : [];
|
const nftRows = Array.isArray(nfts) ? nfts : [];
|
||||||
swapNftKeys = swapRegistryNftKeys(nftRows);
|
swapNftMetadata = swapRegistryNftKeys(nftRows);
|
||||||
|
updateNftOwnershipRegistry(nftRows, assetsKey);
|
||||||
|
|
||||||
swapNetworkAssets = [
|
swapNetworkAssets = [
|
||||||
{ asset: activeBaseCoin(), nftSeries: 0, kind: "coin", balance: "0" },
|
{ asset: activeBaseCoin(), nftSeries: 0, kind: "coin", balance: "0" },
|
||||||
|
|
@ -133,12 +143,14 @@ async function loadSwapOfferAssets() {
|
||||||
asset: String(token.ticker || "").trim(),
|
asset: String(token.ticker || "").trim(),
|
||||||
nftSeries: 0,
|
nftSeries: 0,
|
||||||
kind: "token",
|
kind: "token",
|
||||||
|
ownershipType: -1,
|
||||||
balance: "0"
|
balance: "0"
|
||||||
})),
|
})),
|
||||||
...nftRows.map((nft) => ({
|
...nftRows.map((nft) => ({
|
||||||
asset: String(nft.nft_name || "").trim(),
|
asset: String(nft.nft_name || "").trim(),
|
||||||
nftSeries: Number(nft.series || 0),
|
nftSeries: Number(nft.series || 0),
|
||||||
kind: "nft",
|
kind: "nft",
|
||||||
|
ownershipType: Number(nft.ownership_type),
|
||||||
balance: "0"
|
balance: "0"
|
||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
|
|
@ -164,7 +176,7 @@ function updateSwapSide(asset, amountInput, tipInput) {
|
||||||
tipInput.value = "";
|
tipInput.value = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (asset.kind === "nft") {
|
if (asset.kind === "nft" && asset.ownershipType === 0) {
|
||||||
amountInput.value = "1";
|
amountInput.value = "1";
|
||||||
amountInput.disabled = true;
|
amountInput.disabled = true;
|
||||||
tipInput.value = "0";
|
tipInput.value = "0";
|
||||||
|
|
@ -172,7 +184,10 @@ function updateSwapSide(asset, amountInput, tipInput) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
amountInput.disabled = false;
|
amountInput.disabled = false;
|
||||||
tipInput.disabled = false;
|
tipInput.disabled = asset.kind === "nft";
|
||||||
|
if (asset.kind === "nft") {
|
||||||
|
tipInput.value = "0";
|
||||||
|
}
|
||||||
const amount = decimalToAtomic(amountInput.value);
|
const amount = decimalToAtomic(amountInput.value);
|
||||||
if (!amount || amount <= 0n) {
|
if (!amount || amount <= 0n) {
|
||||||
if (!tipInput.value) {
|
if (!tipInput.value) {
|
||||||
|
|
@ -198,7 +213,9 @@ function updateSwapOfferDerivedFields() {
|
||||||
if (swapAvailable) {
|
if (swapAvailable) {
|
||||||
swapAvailable.textContent = offered
|
swapAvailable.textContent = offered
|
||||||
? (offered.kind === "nft"
|
? (offered.kind === "nft"
|
||||||
? "1 NFT"
|
? (offered.ownershipType === 1
|
||||||
|
? `${formatAtomicBalance(String(offered.balance))} available`
|
||||||
|
: "1 NFT")
|
||||||
: `${formatAtomicBalance(String(offered.balance))} ${offered.asset}`)
|
: `${formatAtomicBalance(String(offered.balance))} ${offered.asset}`)
|
||||||
: "--";
|
: "--";
|
||||||
}
|
}
|
||||||
|
|
@ -211,10 +228,13 @@ function validateSwapSide(asset, amountInput, tipInput, label) {
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
return `Select the asset ${label}.`;
|
return `Select the asset ${label}.`;
|
||||||
}
|
}
|
||||||
const amount = asset.kind === "nft" ? 1n : decimalToAtomic(amountInput.value);
|
const amount = decimalToAtomic(amountInput.value);
|
||||||
if (amount === null || amount <= 0n) {
|
if (amount === null || amount <= 0n) {
|
||||||
return `Enter a valid amount ${label}.`;
|
return `Enter a valid amount ${label}.`;
|
||||||
}
|
}
|
||||||
|
if (asset.kind === "nft" && asset.ownershipType === 0 && amount !== ATOMIC_UNITS_PER_COIN) {
|
||||||
|
return `Indivisible NFT amounts must be exactly 1 ${label}.`;
|
||||||
|
}
|
||||||
const tip = decimalToAtomic(tipInput.value || "0");
|
const tip = decimalToAtomic(tipInput.value || "0");
|
||||||
if (tip === null) {
|
if (tip === null) {
|
||||||
return `Enter a valid miner tip ${label}.`;
|
return `Enter a valid miner tip ${label}.`;
|
||||||
|
|
@ -260,7 +280,7 @@ function validateSwapOfferForm() {
|
||||||
return "Offer expiration must be between 1 and 720 hours.";
|
return "Offer expiration must be between 1 and 720 hours.";
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount = offered.kind === "nft" ? 1n : decimalToAtomic(swapOfferAmountInput.value);
|
const amount = decimalToAtomic(swapOfferAmountInput.value);
|
||||||
const tip = offered.kind === "nft" ? 0n : decimalToAtomic(swapOfferTipInput.value);
|
const tip = offered.kind === "nft" ? 0n : decimalToAtomic(swapOfferTipInput.value);
|
||||||
if (amount + tip > offered.balance) {
|
if (amount + tip > offered.balance) {
|
||||||
return "Offered amount plus miner tip exceeds the available asset balance.";
|
return "Offered amount plus miner tip exceeds the available asset balance.";
|
||||||
|
|
@ -279,8 +299,8 @@ function validateSwapOfferForm() {
|
||||||
function swapOfferPayload() {
|
function swapOfferPayload() {
|
||||||
const offered = selectedSwapAsset(swapOfferAssetSelect);
|
const offered = selectedSwapAsset(swapOfferAssetSelect);
|
||||||
const requested = selectedSwapAsset(swapRequestAssetSelect);
|
const requested = selectedSwapAsset(swapRequestAssetSelect);
|
||||||
const value1 = offered.kind === "nft" ? 1n : decimalToAtomic(swapOfferAmountInput.value);
|
const value1 = decimalToAtomic(swapOfferAmountInput.value);
|
||||||
const value2 = requested.kind === "nft" ? 1n : decimalToAtomic(swapRequestAmountInput.value);
|
const value2 = decimalToAtomic(swapRequestAmountInput.value);
|
||||||
const tip1 = offered.kind === "nft" ? 0n : decimalToAtomic(swapOfferTipInput.value);
|
const tip1 = offered.kind === "nft" ? 0n : decimalToAtomic(swapOfferTipInput.value);
|
||||||
const tip2 = requested.kind === "nft" ? 0n : decimalToAtomic(swapRequestTipInput.value);
|
const tip2 = requested.kind === "nft" ? 0n : decimalToAtomic(swapRequestTipInput.value);
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -360,9 +360,9 @@ function parseDashboardTransactionRows(
|
||||||
if (type === 4) {
|
if (type === 4) {
|
||||||
const creator = bytesToAddressHex(bytes.slice(5, 27));
|
const creator = bytesToAddressHex(bytes.slice(5, 27));
|
||||||
const series = Number(bytes[27] || 0);
|
const series = Number(bytes[27] || 0);
|
||||||
const nftName = bytesToText(bytes.slice(28, 43)) || "NFT";
|
const nftName = bytesToText(bytes.slice(29, 44)) || "NFT";
|
||||||
const count = readU32LE(bytes, 143);
|
const count = readU32LE(bytes, 144);
|
||||||
const fee = readU64LE(bytes, 247);
|
const fee = readU64LE(bytes, 248);
|
||||||
if (isActiveAddress(creator)) {
|
if (isActiveAddress(creator)) {
|
||||||
return [dashboardRow(base, "NFT Created", assetWithSeries(nftName, series), "Self", `+${count}`, signedBaseFee("-", fee))];
|
return [dashboardRow(base, "NFT Created", assetWithSeries(nftName, series), "Self", `+${count}`, signedBaseFee("-", fee))];
|
||||||
}
|
}
|
||||||
|
|
@ -556,11 +556,12 @@ function decodeLightweightTransaction(bytesLike) {
|
||||||
time: time(),
|
time: time(),
|
||||||
creator: bytesToAddressHex(bytes.slice(5, 27)),
|
creator: bytesToAddressHex(bytes.slice(5, 27)),
|
||||||
series: Number(bytes[27] || 0),
|
series: Number(bytes[27] || 0),
|
||||||
nft_name: bytesToText(bytes.slice(28, 43)),
|
ownership_type: Number(bytes[28] || 0),
|
||||||
item_ipfs: bytesToText(bytes.slice(43, 143)),
|
nft_name: bytesToText(bytes.slice(29, 44)),
|
||||||
count: readU32LE(bytes, 143),
|
item_ipfs: bytesToText(bytes.slice(44, 144)),
|
||||||
description: bytesToText(bytes.slice(147, 247)),
|
count: readU32LE(bytes, 144),
|
||||||
txfee: Number(readU64LE(bytes, 247))
|
description: bytesToText(bytes.slice(148, 248)),
|
||||||
|
txfee: Number(readU64LE(bytes, 248))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,40 @@ const transferBroadcastButton = document.querySelector("[data-transfer-broadcast
|
||||||
const ATOMIC_UNITS_PER_COIN = 100000000n;
|
const ATOMIC_UNITS_PER_COIN = 100000000n;
|
||||||
const NON_BASE_TRANSFER_MIN_FEE_ATOMIC = 100000000n;
|
const NON_BASE_TRANSFER_MIN_FEE_ATOMIC = 100000000n;
|
||||||
let transferBalancesRefreshPromise = null;
|
let transferBalancesRefreshPromise = null;
|
||||||
|
let nftOwnershipRegistry = new Map();
|
||||||
|
let nftOwnershipRegistryKey = "";
|
||||||
|
|
||||||
|
function nftRegistryKey(asset, series) {
|
||||||
|
return `${String(asset || "").trim().toLowerCase()}|${Number(series || 0)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNftOwnershipRegistry(nfts, registryKey = "") {
|
||||||
|
nftOwnershipRegistry = new Map(
|
||||||
|
(Array.isArray(nfts) ? nfts : []).map((nft) => [
|
||||||
|
nftRegistryKey(nft.nft_name, nft.series),
|
||||||
|
{
|
||||||
|
ownershipType: Number(nft.ownership_type),
|
||||||
|
remainingSupply: BigInt(nft.remaining_supply || "0")
|
||||||
|
}
|
||||||
|
])
|
||||||
|
);
|
||||||
|
nftOwnershipRegistryKey = registryKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nftOwnershipMetadata(asset, series) {
|
||||||
|
return nftOwnershipRegistry.get(nftRegistryKey(asset, series)) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureNftOwnershipRegistry() {
|
||||||
|
const invoke = window.__TAURI__?.core?.invoke;
|
||||||
|
const broadcastNode = activeBroadcastNode();
|
||||||
|
const registryKey = `${activeWalletAddressShort || activeWalletAddressHex}|${broadcastNode}`;
|
||||||
|
if (!invoke || !broadcastNode || nftOwnershipRegistryKey === registryKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nfts = await invoke("nft_list", { broadcastNode });
|
||||||
|
updateNftOwnershipRegistry(nfts, registryKey);
|
||||||
|
}
|
||||||
|
|
||||||
function setTransferMessage(message, type = "") {
|
function setTransferMessage(message, type = "") {
|
||||||
setScopedTransactionMessage("Transfer", transferMessage, message, type);
|
setScopedTransactionMessage("Transfer", transferMessage, message, type);
|
||||||
|
|
@ -79,6 +113,8 @@ function selectedTransferAsset() {
|
||||||
return {
|
return {
|
||||||
asset: option.dataset.asset || "",
|
asset: option.dataset.asset || "",
|
||||||
nftSeries: Number(option.dataset.nftSeries || 0),
|
nftSeries: Number(option.dataset.nftSeries || 0),
|
||||||
|
ownershipType: Number(option.dataset.ownershipType || -1),
|
||||||
|
isNft: option.dataset.isNft === "true",
|
||||||
balance: BigInt(option.dataset.balance || "0")
|
balance: BigInt(option.dataset.balance || "0")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +158,7 @@ function updateTransferDerivedFields() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transferAmountInput) {
|
if (transferAmountInput) {
|
||||||
if (selected.nftSeries > 0) {
|
if (selected.isNft && selected.ownershipType === 0) {
|
||||||
transferAmountInput.value = "1";
|
transferAmountInput.value = "1";
|
||||||
transferAmountInput.disabled = true;
|
transferAmountInput.disabled = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -144,7 +180,7 @@ function updateTransferFee() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount = selected.nftSeries > 0 ? 1n : decimalToAtomic(transferAmountInput?.value);
|
const amount = decimalToAtomic(transferAmountInput?.value);
|
||||||
if (amount === null || amount <= 0n) {
|
if (amount === null || amount <= 0n) {
|
||||||
transferFeeInput.value = "";
|
transferFeeInput.value = "";
|
||||||
return;
|
return;
|
||||||
|
|
@ -188,6 +224,9 @@ function refreshTransferAssetOptions() {
|
||||||
option.dataset.asset = balance.asset || "";
|
option.dataset.asset = balance.asset || "";
|
||||||
option.dataset.nftSeries = String(Number(balance.nft_series || 0));
|
option.dataset.nftSeries = String(Number(balance.nft_series || 0));
|
||||||
option.dataset.balance = String(balance.balance || "0");
|
option.dataset.balance = String(balance.balance || "0");
|
||||||
|
const metadata = nftOwnershipMetadata(balance.asset, balance.nft_series);
|
||||||
|
option.dataset.isNft = String(Boolean(metadata));
|
||||||
|
option.dataset.ownershipType = String(metadata?.ownershipType ?? -1);
|
||||||
transferAssetSelect.appendChild(option);
|
transferAssetSelect.appendChild(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -199,7 +238,7 @@ function refreshTransferAssetOptions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshTransferBalancesIfNeeded() {
|
async function refreshTransferBalancesIfNeeded() {
|
||||||
if (activeWalletBalances.length || !walletLoaded || !registrationRegistered) {
|
if (!walletLoaded || !registrationRegistered) {
|
||||||
refreshTransferAssetOptions();
|
refreshTransferAssetOptions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -218,7 +257,12 @@ async function refreshTransferBalancesIfNeeded() {
|
||||||
transferBalancesRefreshPromise = (async () => {
|
transferBalancesRefreshPromise = (async () => {
|
||||||
setTransferMessage("Loading spendable assets...");
|
setTransferMessage("Loading spendable assets...");
|
||||||
try {
|
try {
|
||||||
const balances = await invoke("get_active_wallet_balances", { broadcastNode });
|
const [balances] = await Promise.all([
|
||||||
|
activeWalletBalances.length
|
||||||
|
? Promise.resolve(activeWalletBalances)
|
||||||
|
: invoke("get_active_wallet_balances", { broadcastNode }),
|
||||||
|
ensureNftOwnershipRegistry()
|
||||||
|
]);
|
||||||
activeWalletBalances = Array.isArray(balances) ? balances : [];
|
activeWalletBalances = Array.isArray(balances) ? balances : [];
|
||||||
refreshTransferAssetOptions();
|
refreshTransferAssetOptions();
|
||||||
setTransferMessage("");
|
setTransferMessage("");
|
||||||
|
|
@ -326,11 +370,15 @@ function validateTransferForm() {
|
||||||
return "Receiver cannot be the active wallet address.";
|
return "Receiver cannot be the active wallet address.";
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount = selected.nftSeries > 0 ? 1n : decimalToAtomic(transferAmountInput?.value);
|
const amount = decimalToAtomic(transferAmountInput?.value);
|
||||||
if (amount === null || amount <= 0n) {
|
if (amount === null || amount <= 0n) {
|
||||||
return "Enter a valid amount with up to 8 decimal places.";
|
return "Enter a valid amount with up to 8 decimal places.";
|
||||||
}
|
}
|
||||||
if (selected.nftSeries > 0 && String(transferAmountInput?.value || "").trim() !== "1") {
|
if (
|
||||||
|
selected.isNft
|
||||||
|
&& selected.ownershipType === 0
|
||||||
|
&& amount !== ATOMIC_UNITS_PER_COIN
|
||||||
|
) {
|
||||||
return "NFT transfers must send exactly 1 item.";
|
return "NFT transfers must send exactly 1 item.";
|
||||||
}
|
}
|
||||||
if (amount > selected.balance) {
|
if (amount > selected.balance) {
|
||||||
|
|
@ -360,7 +408,7 @@ function validateTransferForm() {
|
||||||
|
|
||||||
function transferPayload() {
|
function transferPayload() {
|
||||||
const selected = selectedTransferAsset();
|
const selected = selectedTransferAsset();
|
||||||
const value = selected?.nftSeries > 0 ? 1n : decimalToAtomic(transferAmountInput?.value);
|
const value = decimalToAtomic(transferAmountInput?.value);
|
||||||
const fee = decimalToAtomic(transferFeeInput?.value || "0");
|
const fee = decimalToAtomic(transferFeeInput?.value || "0");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -420,7 +468,7 @@ async function handleTransferAction(action) {
|
||||||
if (!broadcastNode) {
|
if (!broadcastNode) {
|
||||||
throw new Error("Select a broadcast node before saving.");
|
throw new Error("Select a broadcast node before saving.");
|
||||||
}
|
}
|
||||||
setTransferMessage("Saving transfer...");
|
setTransferMessage("Signing and saving transfer...");
|
||||||
const review = await invoke("save_transfer_transaction", {
|
const review = await invoke("save_transfer_transaction", {
|
||||||
...transferPayload(),
|
...transferPayload(),
|
||||||
broadcastNode,
|
broadcastNode,
|
||||||
|
|
@ -436,7 +484,7 @@ async function handleTransferAction(action) {
|
||||||
if (!broadcastNode) {
|
if (!broadcastNode) {
|
||||||
throw new Error("Select a broadcast node before broadcasting.");
|
throw new Error("Select a broadcast node before broadcasting.");
|
||||||
}
|
}
|
||||||
setTransferMessage("Broadcasting transfer...");
|
setTransferMessage("Signing and broadcasting transfer...");
|
||||||
const result = await invoke("broadcast_transfer_transaction", {
|
const result = await invoke("broadcast_transfer_transaction", {
|
||||||
...transferPayload(),
|
...transferPayload(),
|
||||||
broadcastNode,
|
broadcastNode,
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ async function handleVanityAction(action) {
|
||||||
setVanityButtonsDisabled(true);
|
setVanityButtonsDisabled(true);
|
||||||
try {
|
try {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
setVanityMessage("Saving vanity address transaction...");
|
setVanityMessage("Signing and saving vanity address transaction...");
|
||||||
const review = await invoke("save_vanity_address_transaction", {
|
const review = await invoke("save_vanity_address_transaction", {
|
||||||
...vanityPayload(),
|
...vanityPayload(),
|
||||||
walletKey
|
walletKey
|
||||||
|
|
@ -196,7 +196,7 @@ async function handleVanityAction(action) {
|
||||||
if (!broadcastNode) {
|
if (!broadcastNode) {
|
||||||
throw new Error("Select a broadcast node before broadcasting.");
|
throw new Error("Select a broadcast node before broadcasting.");
|
||||||
}
|
}
|
||||||
setVanityMessage("Broadcasting vanity address transaction...");
|
setVanityMessage("Signing and broadcasting vanity address transaction...");
|
||||||
const result = await invoke("broadcast_vanity_address_transaction", {
|
const result = await invoke("broadcast_vanity_address_transaction", {
|
||||||
...vanityPayload(),
|
...vanityPayload(),
|
||||||
broadcastNode,
|
broadcastNode,
|
||||||
|
|
|
||||||
|
|
@ -474,7 +474,7 @@ async fn nft_list(
|
||||||
if let Some(error) = parse_error_response(&response) {
|
if let Some(error) = parse_error_response(&response) {
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
const NFT_LIST_ROW_BYTES: usize = 32 + 15 + 4;
|
const NFT_LIST_ROW_BYTES: usize = 32 + 15 + 4 + 1 + 8;
|
||||||
if response.len() % NFT_LIST_ROW_BYTES != 0 {
|
if response.len() % NFT_LIST_ROW_BYTES != 0 {
|
||||||
return Err("NFT list response had an unexpected byte length.".to_string());
|
return Err("NFT list response had an unexpected byte length.".to_string());
|
||||||
}
|
}
|
||||||
|
|
@ -488,6 +488,12 @@ async fn nft_list(
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| "Failed to parse NFT series.".to_string())?,
|
.map_err(|_| "Failed to parse NFT series.".to_string())?,
|
||||||
);
|
);
|
||||||
|
let ownership_type = chunk[51];
|
||||||
|
let remaining_supply = u64::from_le_bytes(
|
||||||
|
chunk[52..60]
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| "Failed to parse NFT remaining supply.".to_string())?,
|
||||||
|
);
|
||||||
if nft_name.is_empty() {
|
if nft_name.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -495,6 +501,8 @@ async fn nft_list(
|
||||||
nft_name,
|
nft_name,
|
||||||
series,
|
series,
|
||||||
origin_txid,
|
origin_txid,
|
||||||
|
ownership_type,
|
||||||
|
remaining_supply: remaining_supply.to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1152,15 +1160,11 @@ async fn build_transfer_json(
|
||||||
}
|
}
|
||||||
|
|
||||||
let coin = padded_coin_field(&coin)?;
|
let coin = padded_coin_field(&coin)?;
|
||||||
let mut value = parse_atomic_u64(&value, "Transfer value")?;
|
let value = parse_atomic_u64(&value, "Transfer value")?;
|
||||||
let txfee = parse_atomic_u64(&txfee, "Transfer fee")?;
|
let txfee = parse_atomic_u64(&txfee, "Transfer fee")?;
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
return Err("Transfer value must be greater than zero.".to_string());
|
return Err("Transfer value must be greater than zero.".to_string());
|
||||||
}
|
}
|
||||||
if nft_series > 0 {
|
|
||||||
value = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_base_coin = coin.trim().eq_ignore_ascii_case(BASECOIN.trim());
|
let is_base_coin = coin.trim().eq_ignore_ascii_case(BASECOIN.trim());
|
||||||
let minimum_fee = minimum_transfer_fee(value, is_base_coin);
|
let minimum_fee = minimum_transfer_fee(value, is_base_coin);
|
||||||
if txfee < minimum_fee {
|
if txfee < minimum_fee {
|
||||||
|
|
@ -1247,6 +1251,7 @@ async fn build_create_nft_json(
|
||||||
state: &State<'_, WalletState>,
|
state: &State<'_, WalletState>,
|
||||||
wallet_key: Option<String>,
|
wallet_key: Option<String>,
|
||||||
series: u8,
|
series: u8,
|
||||||
|
ownership_type: u8,
|
||||||
nft_name: String,
|
nft_name: String,
|
||||||
item_ipfs: String,
|
item_ipfs: String,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
|
@ -1263,6 +1268,12 @@ async fn build_create_nft_json(
|
||||||
if series == 1 && count < 2 {
|
if series == 1 && count < 2 {
|
||||||
return Err("An NFT collection must contain at least 2 items.".to_string());
|
return Err("An NFT collection must contain at least 2 items.".to_string());
|
||||||
}
|
}
|
||||||
|
if ownership_type > 1 {
|
||||||
|
return Err("Ownership type must be indivisible or fractional.".to_string());
|
||||||
|
}
|
||||||
|
if series == 1 && ownership_type != 0 {
|
||||||
|
return Err("NFT/RWA collections must use indivisible ownership.".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
let nft_name = padded_token_ticker(&nft_name)
|
let nft_name = padded_token_ticker(&nft_name)
|
||||||
.map_err(|_| "NFT name must normalize to 3-15 alphanumeric characters.".to_string())?;
|
.map_err(|_| "NFT name must normalize to 3-15 alphanumeric characters.".to_string())?;
|
||||||
|
|
@ -1290,6 +1301,7 @@ async fn build_create_nft_json(
|
||||||
timestamp,
|
timestamp,
|
||||||
&creator,
|
&creator,
|
||||||
series,
|
series,
|
||||||
|
ownership_type,
|
||||||
&nft_name,
|
&nft_name,
|
||||||
&item_ipfs,
|
&item_ipfs,
|
||||||
count,
|
count,
|
||||||
|
|
@ -1307,6 +1319,7 @@ async fn build_create_nft_json(
|
||||||
"timestamp": timestamp,
|
"timestamp": timestamp,
|
||||||
"creator": creator,
|
"creator": creator,
|
||||||
"series": series,
|
"series": series,
|
||||||
|
"ownership_type": ownership_type,
|
||||||
"nft_name": nft_name,
|
"nft_name": nft_name,
|
||||||
"item_ipfs": item_ipfs,
|
"item_ipfs": item_ipfs,
|
||||||
"count": count,
|
"count": count,
|
||||||
|
|
@ -2732,6 +2745,7 @@ async fn rebuild_create_nft(
|
||||||
json_u32(transaction, "timestamp", "time")?,
|
json_u32(transaction, "timestamp", "time")?,
|
||||||
&json_str(transaction, "creator")?,
|
&json_str(transaction, "creator")?,
|
||||||
json_u8(transaction, "series")?,
|
json_u8(transaction, "series")?,
|
||||||
|
json_u8(transaction, "ownership_type")?,
|
||||||
&json_str(transaction, "nft_name")?,
|
&json_str(transaction, "nft_name")?,
|
||||||
&json_str(transaction, "item_ipfs")?,
|
&json_str(transaction, "item_ipfs")?,
|
||||||
json_u64(transaction, "count")? as u32,
|
json_u64(transaction, "count")? as u32,
|
||||||
|
|
@ -3990,6 +4004,7 @@ async fn save_create_nft_transaction(
|
||||||
state: State<'_, WalletState>,
|
state: State<'_, WalletState>,
|
||||||
wallet_key: Option<String>,
|
wallet_key: Option<String>,
|
||||||
series: u8,
|
series: u8,
|
||||||
|
ownership_type: u8,
|
||||||
nft_name: String,
|
nft_name: String,
|
||||||
item_ipfs: String,
|
item_ipfs: String,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
|
@ -4001,6 +4016,7 @@ async fn save_create_nft_transaction(
|
||||||
&state,
|
&state,
|
||||||
wallet_key,
|
wallet_key,
|
||||||
series,
|
series,
|
||||||
|
ownership_type,
|
||||||
nft_name,
|
nft_name,
|
||||||
item_ipfs,
|
item_ipfs,
|
||||||
count,
|
count,
|
||||||
|
|
@ -4017,6 +4033,7 @@ async fn broadcast_create_nft_transaction(
|
||||||
wallet_key: Option<String>,
|
wallet_key: Option<String>,
|
||||||
broadcast_node: String,
|
broadcast_node: String,
|
||||||
series: u8,
|
series: u8,
|
||||||
|
ownership_type: u8,
|
||||||
nft_name: String,
|
nft_name: String,
|
||||||
item_ipfs: String,
|
item_ipfs: String,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
|
@ -4027,6 +4044,7 @@ async fn broadcast_create_nft_transaction(
|
||||||
&state,
|
&state,
|
||||||
wallet_key,
|
wallet_key,
|
||||||
series,
|
series,
|
||||||
|
ownership_type,
|
||||||
nft_name,
|
nft_name,
|
||||||
item_ipfs,
|
item_ipfs,
|
||||||
count,
|
count,
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,8 @@ struct NftListItemView {
|
||||||
nft_name: String,
|
nft_name: String,
|
||||||
series: u32,
|
series: u32,
|
||||||
origin_txid: String,
|
origin_txid: String,
|
||||||
|
ownership_type: u8,
|
||||||
|
remaining_supply: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue