Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

D robert sol integration #1450

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
1431e38
feat: wip sol connect
911-Benedek-RobertGeorge Sep 3, 2024
b0ea008
feat: wip display nfts from sol blockchain
911-Benedek-RobertGeorge Sep 3, 2024
512f9c8
feat: wip fetch ithem balance from sol
911-Benedek-RobertGeorge Sep 3, 2024
d59aa66
feat: wip login with solana wallet
911-Benedek-RobertGeorge Sep 4, 2024
218b746
feat: login/logout with solana wallet
911-Benedek-RobertGeorge Sep 4, 2024
569e923
feat: fetch and show solana nfts in the wallet tab
911-Benedek-RobertGeorge Sep 5, 2024
dd75f4d
feat: hide unnecessary pages when connected to solana
911-Benedek-RobertGeorge Sep 6, 2024
94ef94c
refactor: delete console logs
911-Benedek-RobertGeorge Sep 6, 2024
f446204
ref: typo
911-Benedek-RobertGeorge Sep 9, 2024
6d9db5e
feat: remove wallet tabs for sol connection
911-Benedek-RobertGeorge Sep 9, 2024
cc7e7d5
feat: wip mint NFT ME ID
911-Benedek-RobertGeorge Sep 9, 2024
5e004fb
refactor: routes & rename components
911-Benedek-RobertGeorge Sep 9, 2024
b1634a3
refactor: compress big size images and add tailwind css to project
911-Benedek-RobertGeorge Sep 12, 2024
e181b8f
feat: add MX Bitz Mini Game modal into datadex
911-Benedek-RobertGeorge Sep 12, 2024
f805ff8
refactor: add a nft store and replace the calls with the stored nfts
911-Benedek-RobertGeorge Sep 12, 2024
30a06af
feat: WIP add sol Mini Game and fetch sol bitz balance
911-Benedek-RobertGeorge Sep 13, 2024
8120e32
Merge branch 'test' into d-robert-sol-integration
911-Benedek-RobertGeorge Sep 13, 2024
c8d6cca
feat: change network testnet into devnet and add new contract addres…
911-Benedek-RobertGeorge Sep 13, 2024
b214520
feat: mint nftIdMe on Sol
911-Benedek-RobertGeorge Sep 13, 2024
141d537
Update TradeForm.tsx
911-Benedek-RobertGeorge Sep 17, 2024
fd0b23d
Merge branch 'test-sol' into d-robert-sol-integration
911-Benedek-RobertGeorge Sep 17, 2024
2a54e12
feat: bonding and set as primary vault nftMeID
911-Benedek-RobertGeorge Sep 25, 2024
fb49104
feat: vault view & renew & topUp functionalities
911-Benedek-RobertGeorge Sep 26, 2024
a91c3d6
feat: wip bond list
911-Benedek-RobertGeorge Sep 26, 2024
84d59d4
feat: liveliness functionalities and rewards function
911-Benedek-RobertGeorge Sep 27, 2024
29aa12b
feat: error handling on transactions and toasts show
911-Benedek-RobertGeorge Sep 30, 2024
a11b040
feat: initialize address when minting for the first time , disable in…
911-Benedek-RobertGeorge Oct 1, 2024
f1959bf
feat: rewards computations
911-Benedek-RobertGeorge Oct 2, 2024
05e1d5f
fix: add addressClaimable amount
911-Benedek-RobertGeorge Oct 3, 2024
56899b6
fix: renewal resets the address claimable
911-Benedek-RobertGeorge Oct 3, 2024
ca4d8cd
fix: rewards computations to match SM
911-Benedek-RobertGeorge Oct 3, 2024
4bb5093
feat: balances update on tx, promise toast
911-Benedek-RobertGeorge Oct 4, 2024
cdbdc3b
chore: version bump to trigger build and new env vars
newbreedofgeek Oct 7, 2024
a676908
fix: Bond tx too large
911-Benedek-RobertGeorge Oct 8, 2024
77e2c72
fix: show right annual rewards estimation
911-Benedek-RobertGeorge Oct 8, 2024
e09dabc
feat: add promise toast to bond tx too
911-Benedek-RobertGeorge Oct 9, 2024
44cfa7f
fix: make buttons disabled when bonds inactive
911-Benedek-RobertGeorge Oct 9, 2024
defb45b
feat: inform user about 2% royalty tax
911-Benedek-RobertGeorge Oct 9, 2024
5100002
refactor: remove comments and TODO
911-Benedek-RobertGeorge Oct 10, 2024
3ff34af
feat: small modification
911-Benedek-RobertGeorge Oct 10, 2024
a8a6634
feat: add signing the message before mint for whitelist
911-Benedek-RobertGeorge Oct 10, 2024
e1e3843
chore: update sdk for the whitelisting
911-Benedek-RobertGeorge Oct 11, 2024
5339b6f
D robert sol integration (#1461)
newbreedofgeek Oct 11, 2024
49f53aa
chore: feature bump
newbreedofgeek Oct 11, 2024
e0cb1cb
fix: issues found
911-Benedek-RobertGeorge Oct 12, 2024
bed3562
feat: enable the normal minting process and remove donation, royalties
911-Benedek-RobertGeorge Oct 12, 2024
e66be01
fix: liveliness page load a default state when there are no bonds
911-Benedek-RobertGeorge Oct 12, 2024
46c5cae
fix: remove await sleep time when minting
911-Benedek-RobertGeorge Oct 12, 2024
6d33e5f
feat: consider last active bond as Vault add a reinvest message
911-Benedek-RobertGeorge Oct 12, 2024
272b007
refactor: clear code and analyze TODOs
911-Benedek-RobertGeorge Oct 12, 2024
1048c33
Merge branch 'test-sol-support' into d-robert-sol-integration
911-Benedek-RobertGeorge Oct 12, 2024
c99438e
fix: after merge bugs
911-Benedek-RobertGeorge Oct 12, 2024
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
Prev Previous commit
Next Next commit
feat: vault view & renew & topUp functionalities
  • Loading branch information
911-Benedek-RobertGeorge committed Sep 26, 2024
commit fb491040ecb46c701408e36b5f164c3f3e57f7ec
251 changes: 180 additions & 71 deletions src/components/Liveliness/LivelinessStakingSol.tsx

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/libs/Solana/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export enum SolEnvEnum {
}

export const BONDING_PROGRAM_ID = IS_DEVNET ? "4zAKaiW68x31n7mRbYQBUgTC9BWL3q4uATjuBc5txYSN" : "///todo ADD MAINNET ID";

export const BOND_CONFIG_INDEX = 1;
15 changes: 14 additions & 1 deletion src/libs/Solana/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BN } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";

export interface BondConfig {
bump: number;
index: number;
Expand All @@ -11,6 +11,19 @@ export interface BondConfig {
padding: Uint8Array;
}

export interface Bond {
bump: number;
bondId: number;
state: number;
isVault: boolean;
bondTimestamp: number;
unbondTimestamp: number;
bondAmount: BN;
assetId: PublicKey;
owner: PublicKey;
padding: number[];
}

const bondConfig: BondConfig = {
bump: 1,
index: 0,
Expand Down
184 changes: 106 additions & 78 deletions src/libs/Solana/utils.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { connect } from "http2";
import { BN, Program, web3 } from "@coral-xyz/anchor";
import { BN, Program } from "@coral-xyz/anchor";
import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";
import { CNftSolPostMintMetaType } from "@itheum/sdk-mx-data-nft/out";
import { getChainID } from "@multiversx/sdk-dapp/utils";
import { SPL_ACCOUNT_COMPRESSION_PROGRAM_ID } from "@solana/spl-account-compression";
import { ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddress, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { Wallet } from "@solana/wallet-adapter-react";
import { AccountMeta, Connection, PublicKey, Transaction } from "@solana/web3.js";
import BigNumber from "bignumber.js";
import { contractsForChain, IS_DEVNET } from "libs/config";
import { getApiDataDex } from "libs/utils";

import { BONDING_PROGRAM_ID, SolEnvEnum } from "./config";
import { BOND_CONFIG_INDEX, BONDING_PROGRAM_ID, SolEnvEnum } from "./config";
import { CoreSolBondStakeSc, IDL } from "./CoreSolBondStakeSc";
import { Bond } from "./types";

const MAX_PERCENT = 100;
const SLOTS_IN_YEAR = 78840000; // solana slots in a year
const ITHEUM_TOKEN = contractsForChain(IS_DEVNET ? SolEnvEnum.devnet : SolEnvEnum.mainnet).itheumToken;
export const MAX_PERCENT = 100;
export const SLOTS_IN_YEAR = 78840000; // solana slots in a year
export const ITHEUM_TOKEN_ADDRESS = contractsForChain(IS_DEVNET ? SolEnvEnum.devnet : SolEnvEnum.mainnet).itheumToken;

export async function fetchSolNfts(solAddress: string | undefined) {
if (!solAddress) {
Expand All @@ -30,29 +29,26 @@ export async function fetchSolNfts(solAddress: string | undefined) {
}

// BONDING

function bufferToArray(buffer: Buffer): number[] {
const nums: number[] = [];
for (let i = 0; i < buffer.length; i++) {
nums.push(buffer[i]);
}
return nums;
}

function decode(stuff: string) {
return bufferToArray(bs58.decode(stuff));
}

export async function fetchBondingConfig(programSol: any) {
/// bondAmount, bondState, lockPeriod, withdrawPenalty, merkleTree

try {
const bondConfigPda = await PublicKey.findProgramAddressSync([Buffer.from("bond_config"), Buffer.from([1])], programSol.programId)[0];

const res = await programSol?.account.bondConfig.fetch(bondConfigPda); //.then((data: any) => {
// // console.log("bondConfigggggg", data);

// const lockPeriod = data.lockPeriod.toNumber();
// const bondAmount = new BigNumber(data.bondAmount).dividedBy(10 ** 9);
// const withdrawPenalty = data.withdrawPenalty.toNumber().dividedBy(10 ** 2); // Convert to decimal % value
// console.log("Values", lockPeriod, bondAmount, withdrawPenalty);
// // console.log("lockPeriod", lockPeriod);
// // console.log("bondAmount", bondAmount);
// // console.log("withdrawPenalty", withdrawPenalty);
// res = { bondConfigPda: bondConfigPda, lockPeriod: lockPeriod, bondAmount: bondAmount, withdrawPenalty: withdrawPenalty };
// });
// console.log("res", res);
// const lockPeriod = res.lockPeriod.toNumber();
// const bondAmount = new BigNumber(res.bondAmount).dividedBy(10 ** 9);
// const withdrawPenalty = res.withdrawPenalty.toNumber() / 100; // Convert to decimal % value

const res = await programSol?.account.bondConfig.fetch(bondConfigPda);
// console.log("bondConfigggggg", data);
return {
bondConfigPda: bondConfigPda,
lockPeriod: res.lockPeriod.toNumber(),
Expand All @@ -68,7 +64,6 @@ export async function fetchBondingConfig(programSol: any) {

export async function fetchRewardsConfig(programSol: any) {
// rewardsPerShare, accumulatedRewards, lastRewardSlot, rewardsPerSlot, rewardsReserve, maxApr, rewardsState

try {
const _rewardsConfigPda = PublicKey.findProgramAddressSync([Buffer.from("rewards_config")], programSol.programId)[0];

Expand All @@ -89,15 +84,14 @@ export async function fetchRewardsConfig(programSol: any) {
}
}

function computeCurrentLivelinessScore(lastUpdateTimestamp: number, lockPeriod: number, weightedLivelinessScore: number) {
// din config lockPeriod
export function computeCurrentLivelinessScore(lastUpdateTimestamp: number, lockPeriod: number, weightedLivelinessScore: number): number {
const currentTimestamp = Math.round(Date.now() / 1000);
const decay = (currentTimestamp - lastUpdateTimestamp) / lockPeriod;
const livelinessScore = weightedLivelinessScore * (1 - decay);
return livelinessScore; // daca apare 9823 inseamna ca e 98.23% impart la 100
}

function computeClaimableAmount(
export function computeClaimableAmount(
addressRewardsPerShare: number,
addressTotalBondAmount: number,
currentWeightedLivelinessScore: number,
Expand All @@ -108,16 +102,7 @@ enum RewardsState {
Inactive = 0,
Active = 1,
}
function bufferToArray(buffer: Buffer): number[] {
const nums: number[] = [];
for (let i = 0; i < buffer.length; i++) {
nums.push(buffer[i]);
}
return nums;
}
function decode(stuff: string) {
return bufferToArray(bs58.decode(stuff));
}

// calculate_rewards_since_last_allocation function
async function calculateRewardsSinceLastAllocation(connection: Connection, rewardsConfig: any): Promise<BigNumber> {
const currentSlot = new BigNumber(await connection.getSlot());
Expand Down Expand Up @@ -176,13 +161,7 @@ const mapProof = (proof: string[]): AccountMeta[] => {
}));
};

// fetch addressBondsRewardsData from addressBondsRewardsData
/// bondId = currentIndex + 1 ( din addressBondsRewardsData)
// amont = bondConfig
//foloseste bigNumber // fetch addressBondsRewardsData from addressBondsRewardsData

export async function createBondTransaction(
isVault: boolean,
mintMeta: CNftSolPostMintMetaType,
userPublicKey: PublicKey,
connection: Connection
Expand Down Expand Up @@ -214,14 +193,15 @@ export async function createBondTransaction(
const _bondId = data.currentIndex + 1;
return _bondId;
});
console.log("bondId", typeof bondId);
//todo check if this is correct number

const { nftMeIdVault } = await retrieveBondsAndNftMeIdVault(userPublicKey, bondId - 1, program);
const isVault = nftMeIdVault ? false : true;

const bondPda = PublicKey.findProgramAddressSync([Buffer.from("bond"), userPublicKey.toBuffer(), Buffer.from([bondId])], program.programId)[0];
const assetUsagePda = PublicKey.findProgramAddressSync([new PublicKey(assetId).toBuffer()], program.programId)[0];
const vaultConfigPda = PublicKey.findProgramAddressSync([Buffer.from("vault_config")], programId)[0];
const vaultAta = await getAssociatedTokenAddress(new PublicKey(ITHEUM_TOKEN), vaultConfigPda, true);
const userItheumAta = await getAssociatedTokenAddress(new PublicKey(ITHEUM_TOKEN), userPublicKey, true);
const vaultAta = await getAssociatedTokenAddress(new PublicKey(ITHEUM_TOKEN_ADDRESS), vaultConfigPda, true);
const userItheumAta = await getAssociatedTokenAddress(new PublicKey(ITHEUM_TOKEN_ADDRESS), userPublicKey, true);
const rewardsConfigPda = PublicKey.findProgramAddressSync([Buffer.from("rewards_config")], program.programId)[0];

const bondConfigPda = await PublicKey.findProgramAddressSync([Buffer.from("bond_config"), Buffer.from([1])], program.programId)[0];
Expand All @@ -231,12 +211,9 @@ export async function createBondTransaction(
return { amount: bondAmount, merkleTree: merkleTree };
});

console.log("bondConfigData", bondConfigData);
console.log("add", addressBondsRewardsPda);

// Create the transaction using bond method from program
const transaction = await program.methods
.bond(1, bondId, bondConfigData.amount, new PublicKey(assetId), isVault, proofRoot, _dataHash, _creatorHash, new BN(nonce), index)
.bond(BOND_CONFIG_INDEX, bondId, bondConfigData.amount, new PublicKey(assetId), isVault, proofRoot, _dataHash, _creatorHash, new BN(nonce), index)
.accounts({
addressBondsRewards: addressBondsRewardsPda,
assetUsage: assetUsagePda,
Expand All @@ -245,7 +222,7 @@ export async function createBondTransaction(
rewardsConfig: rewardsConfigPda,
vaultConfig: vaultConfigPda,
vault: vaultAta,
mintOfTokenSent: new PublicKey(ITHEUM_TOKEN),
mintOfTokenSent: new PublicKey(ITHEUM_TOKEN_ADDRESS),
authority: userPublicKey,
merkleTree: bondConfigData.merkleTree,
authorityTokenAccount: userItheumAta,
Expand All @@ -268,31 +245,82 @@ export async function createBondTransaction(
}
}

async function initializeAddress(programSol: Program, userPublicKey: PublicKey, connection: Connection) {
// export async function sendTransaction(transaction: Transaction, connection: Connection): Promise<string> {
// try {
// const signedTransaction = await connection.sendTransaction(transaction, [], {
// skipPreflight: false,
// preflightCommitment: "singleGossip",
// });

// return signedTransaction;
// } catch (error) {
// console.error("Transaction failed:", error);
// return "";
// }
// }

// export async function initializeAddress(programSol: Program, userPublicKey: PublicKey, connection: Connection) {
// try {
// // console.log("initializeAddress", programSol, userPublicKey, rewardsConfigPda, addressBondsRewardsPda);

// if (!programSol || !userPublicKey) return;

// // Build the transaction
// const transaction = await programSol.methods
// .initializeAddress()
// .accounts({
// addressBondsRewards: addressBondsRewardsPda,
// rewardsConfig: rewardsConfigPda,
// authority: userPublicKey,
// })
// .transaction();

// // Step 4: Get the latest blockhash to include in the transaction
// const latestBlockhash = await connection.getLatestBlockhash();
// transaction.recentBlockhash = latestBlockhash.blockhash;
// transaction.feePayer = userPublicKey;

// const signedTransaction = await sendTransaction(transaction, connection);

// console.log("Transaction sent with signature:", signedTransaction);
// } catch (error) {
// console.error("Transaction failed:", error);
// }
// }

export async function retrieveBondsAndNftMeIdVault(
userPublicKey: PublicKey,
lastIndex: number,
program?: Program<CoreSolBondStakeSc>,
connection?: Connection
): Promise<{ bonds: Bond[]; nftMeIdVault: Bond | undefined }> {
try {
// console.log("initializeAddress", programSol, userPublicKey, rewardsConfigPda, addressBondsRewardsPda);

if (!programSol || !userPublicKey) return;

// Build the transaction
const transaction = await programSol.methods
.initializeAddress()
.accounts({
addressBondsRewards: addressBondsRewardsPda,
rewardsConfig: rewardsConfigPda,
authority: userPublicKey,
})
.transaction();

// Step 4: Get the latest blockhash to include in the transaction
const latestBlockhash = await connection.getLatestBlockhash();
transaction.recentBlockhash = latestBlockhash.blockhash;
transaction.feePayer = userPublicKey;

const signedTransaction = await sendTransaction(transaction, connection);

console.log("Transaction sent with signature:", signedTransaction);
if (program === undefined) {
const programId = new PublicKey(BONDING_PROGRAM_ID);
if (connection) {
program = new Program<CoreSolBondStakeSc>(IDL, programId, {
connection: connection,
});
} else {
throw new Error("Connection is required to retrieve bonds");
}
}
const bonds: Bond[] = [];
let nftMeIdVault: Bond | undefined;
for (let i = 1; i <= lastIndex; i++) {
const bondPda = PublicKey.findProgramAddressSync([Buffer.from("bond"), userPublicKey.toBuffer(), Buffer.from([i])], program.programId)[0];
const bond = await program.account.bond.fetch(bondPda);
const bondUpgraded = { ...bond, bondId: i, unbondTimestamp: bond.unbondTimestamp.toNumber(), bondTimestamp: bond.bondTimestamp.toNumber() };
if (bond.isVault) {
nftMeIdVault = bondUpgraded;
}
bonds.push(bondUpgraded);
}

return { bonds: bonds, nftMeIdVault: nftMeIdVault };
} catch (error) {
console.error("Transaction failed:", error);
console.error("retrieveBondsError", error);

throw new Error("Retrieve Bonds Error: Not able to fetch the bonds from the blockchain");
}
}
4 changes: 3 additions & 1 deletion src/libs/utils/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,10 @@ export const getBondsForOffers = async (offers: Offer[]): Promise<ExtendedOffer[
};

export const settingLivelinessScore = async (tokenIdentifier?: string, unbondTimestamp?: number, lockPeriod?: number): Promise<number | undefined> => {
const bondingContract = new BondContract(IS_DEVNET ? "devnet" : "mainnet");
try {
if (tokenIdentifier) {
// multiversX only
const bondingContract = new BondContract(IS_DEVNET ? "devnet" : "mainnet");
const periodOfBond = await bondingContract.viewBonds([tokenIdentifier]);
const newDate = new Date();
const currentTimestamp = Math.floor(newDate.getTime() / 1000);
Expand All @@ -371,6 +372,7 @@ export const settingLivelinessScore = async (tokenIdentifier?: string, unbondTim
const newDate = new Date();
const currentTimestamp = Math.floor(newDate.getTime() / 1000);
const difDays = currentTimestamp - unbondTimestamp;
console.log("difDays", difDays, "lockPeriod", lockPeriod, "unbondTimestamp", unbondTimestamp, "Curr", currentTimestamp);
return difDays > 0 ? 0 : unbondTimestamp === 0 ? -1 : Number(Math.abs(getLivelinessScore(difDays, lockPeriod)).toFixed(2));
}
} catch (error) {
Expand Down
13 changes: 7 additions & 6 deletions src/pages/AdvertiseData/components/TradeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import { UserDataType } from "libs/MultiversX/types";
import { getApiDataMarshal, isValidNumericCharacter, sleep, timeUntil } from "libs/utils";
import { useAccountStore, useMintStore } from "store";
import { MintingModal } from "./MintingModal";
import { createBondTransaction, fetchBondingConfig, fetchRewardsConfig } from "libs/Solana/utils";
import { createBondTransaction, fetchBondingConfig, fetchRewardsConfig, retrieveBondsAndNftMeIdVault } from "libs/Solana/utils";
import { PublicKey } from "@solana/web3.js";
import { BONDING_PROGRAM_ID, SolEnvEnum } from "libs/Solana/config";
import { CoreSolBondStakeSc, IDL } from "libs/Solana/CoreSolBondStakeSc";
Expand Down Expand Up @@ -664,7 +664,7 @@ export const TradeForm: React.FC<TradeFormProps> = (props) => {

// The actual data nft mint TX we will execute once we confirm the IPFS metadata has loaded
// setMintTx(dataNFTMintTX);
// console.log("mintMeta", _imageUrl, _metadataUrl, mintMeta);

if (!_imageUrl || _imageUrl.trim() === "" || !_metadataUrl || _metadataUrl.trim() === "") {
setErrDataNFTStreamGeneric(new Error(labels.ERR_IPFS_ASSET_SAVE_FAILED));
} else if (!mintMeta || mintMeta?.error || Object.keys(mintMeta).length === 0) {
Expand All @@ -680,16 +680,17 @@ export const TradeForm: React.FC<TradeFormProps> = (props) => {
setMintingSuccessful(true);
setSaveProgress((prevSaveProgress) => ({ ...prevSaveProgress, s4: 1 }));

const isVault = !bondVaultNonce ? true : false;
const bondTransaction = await createBondTransaction(isVault, mintMeta, publicKey, connection);
const bondTransaction = await createBondTransaction(mintMeta, publicKey, connection);

if (bondTransaction) {
try {
const txId = await sendTransaction(bondTransaction, connection, {
skipPreflight: true, // Skips preflight check
preflightCommitment: "confirmed", // Adjust the preflightCommitment if needed
skipPreflight: true,
preflightCommitment: "confirmed",
});

console.log("bondTransaction txId", txId);
setMintTx(txId);
setMakePrimaryNFMeIdSuccessful(true);
} catch (error) {
setErrDataNFTStreamGeneric(new Error(labels.ERR_SUCCESS_MINT_BUT_BONDING_TRANSACTION_FAILED));
Expand Down