diff --git a/djed-sdk/dist/esm/index.js b/djed-sdk/dist/esm/index.js index f030428..62691c7 100644 --- a/djed-sdk/dist/esm/index.js +++ b/djed-sdk/dist/esm/index.js @@ -158,6 +158,7 @@ const FEE_UI_UNSCALED = decimalUnscaling( (FEE_UI / 100).toString(), SCALING_DECIMALS ); + const tradeDataPriceCore = (djed, method, decimals, amountScaled) => { const amountUnscaled = decimalUnscaling(amountScaled, decimals); return scaledUnscaledPromise(web3Promise$1(djed, method, 0), BC_DECIMALS).then( @@ -183,29 +184,11 @@ const tradeDataPriceCore = (djed, method, decimals, amountScaled) => { ); }; -/** - * Function that converts coin amount to BC - * @param {*} amount unscaled coin amount to be converted to BC - * @param {*} price unscaled coin price - * @param {*} decimals coin decimals - * @returns unscaled BC amount - */ const convertToBC = (amount, price, decimals) => { const decimalScalingFactor = BigInt(Math.pow(10, decimals)); return (BigInt(amount) * BigInt(price)) / decimalScalingFactor; }; -/** - * Calculate if the transaction will reach the maximum reserve ratio - * @param scPrice - Unscaled stablecoin price - * @param reserveBc - Unscaled reserve of base coin with appended potential transaction amount. - * Example: If user wants to buy 1RC, the reserveBc param will be calculated as sum of current reserve of BC and desired RC amount converted in BC - * @param totalScSupply - Unscaled total stablecoin supply - * @param reserveRatioMax - Unscaled maximum reserve ratio - * @param scDecimalScalingFactor - If stablecoin has 6 decimals, scDecimalScalingFactor will be calculated as 10^6 - * @param thresholdSupplySC - Unscaled threshold SC supply - * @returns - */ const calculateIsRatioBelowMax = ({ scPrice, reserveBc, @@ -223,16 +206,6 @@ const calculateIsRatioBelowMax = ({ ); }; -/** - * Calculate if the transaction will reach the minimum reserve ratio - * @param scPrice - Unscaled stablecoin price - * @param reserveBc - Unscaled reserve of base coin with calculated potential transaction amount. - * Example: If user wants to buy 1SC, the reserveBc param will be calculated as sum of current reserve of BC and desired SC amount converted in BC - * @param totalScSupply - Unscaled total stablecoin supply - * @param reserveRatioMin - Unscaled minimum reserve ratio - * @param scDecimalScalingFactor - If stablecoin has 6 decimals, scDecimalScalingFactor will be calculated as 10^6 - * @returns - */ const calculateIsRatioAboveMin = ({ scPrice, reserveBc, @@ -248,15 +221,8 @@ const calculateIsRatioAboveMin = ({ ); }; -/** - * - * @param {*} amountUSD - * @param {*} totalSCSupply - * @param {*} thresholdSCSupply - * @returns - */ -const isTxLimitReached = (amountUSD, totalSCSupply, thresholdSCSupply) => - amountUSD > TRANSACTION_USD_LIMIT && +const isTxLimitReached = (amountUSD, totalSCSupply, thresholdSCSupply, txLimit) => + (BigInt(amountUSD) > BigInt(txLimit || TRANSACTION_USD_LIMIT)) && BigInt(totalSCSupply) >= BigInt(thresholdSCSupply); const promiseTx = (isWalletConnected, tx, signer) => { @@ -279,13 +245,6 @@ const verifyTx = (web3, hash) => { }); }; -/** - * Function that deducts all platform fees from the BC amount - * @param {*} value The amount of BC from which fees should be deducted - * @param {*} fee The platform fee - * @param {*} treasuryFee The treasury fee - * @returns BC value with all fees calculated - */ const calculateTxFees = (value, fee, treasuryFee, feeUI) => { const f = (BigInt(value) * BigInt(fee)) / BigInt(scalingFactor); const f_ui = @@ -295,26 +254,11 @@ const calculateTxFees = (value, fee, treasuryFee, feeUI) => { return { f, f_ui, f_t }; }; -/** - * Function that deducts all platform fees from the BC amount - * @param {*} value The amount of BC from which fees should be deducted - * @param {*} fee The platform fee - * @param {*} treasuryFee The treasury fee - * @returns BC value with all fees calculated - */ const deductFees = (value, fee, treasuryFee) => { const { f, f_ui, f_t } = calculateTxFees(value, fee, treasuryFee); return BigInt(value) - f - f_ui - f_t; }; -/** - * Function that appends all platform fees to the BC amount - * @param {*} amountBC The unscaled amount of BC (e.g. for 1BC, value should be 1 * 10^BC_DECIMALS) - * @param {*} treasuryFee Treasury fee unscaled (e.g. If the fee is 1%, than 1/100 * scalingFactor) - * @param {*} fee Fee unscaled (e.g. If the fee is 1%, than 1/100 * scalingFactor) - * @param {*} fee_UI UI fee unscaled (e.g. If the fee is 1%, than 1/100 * scalingFactor) - * @returns Unscaled BC amount with calculated fees - */ const appendFees = (amountBC, treasuryFee, fee, fee_UI) => { const totalFees = BigInt(treasuryFee) + BigInt(fee) + BigInt(fee_UI); const substractedFees = BigInt(scalingFactor) - totalFees; @@ -324,11 +268,6 @@ const appendFees = (amountBC, treasuryFee, fee, fee_UI) => { return appendedFeesAmount.toString(); }; -/** - * Function that returns treasury and platform fees - * @param {*} djed Djed contract - * @returns Treasury and platform fee - */ const getFees = async (djed) => { try { const [treasuryFee, fee] = await Promise.all([ @@ -344,6 +283,23 @@ const getFees = async (djed) => { } }; +/** + * Added getPriceMethod export to fix Rollup Error + */ +const getPriceMethod = async (djed, operation) => { + const isShu = await djed.methods.scMaxPrice(0).call().then(() => true).catch(() => false); + + if (!isShu) return "scPrice"; + + switch (operation) { + case 'buySC': return "scMaxPrice"; + case 'sellSC': return "scMinPrice"; + case 'buyRC': return "scMinPrice"; + case 'sellRC': return "scMaxPrice"; + default: return "scPrice"; + } +}; + /** * Function that calculates fees and how much BC (totalBCAmount) user should pay to receive desired amount of reserve coin * @param {*} djed DjedContract @@ -543,8 +499,139 @@ const calculateFutureScPrice = async ({ } }; -var contractName$2 = "Djed"; -var abi$2 = [ +/** + * Function that calculates fees and how much BC (totalBCAmount) user will receive if he sells desired amount of stable coin and reserve coin + * @param {*} djed DjedContract + * @param {*} scDecimals Stable coin decimals + * @param {*} rcDecimals Reserve coin decimals + * @param {*} amountScScaled Stable coin amount that user wants to sell + * @param {*} amountRcScaled Reserve coin amount that user wants to sell + * @returns + */ +const tradeDataPriceSellBoth = async ( + djed, + scDecimals, + rcDecimals, + amountScScaled, + amountRcScaled +) => { + try { + const scPriceMethod = await getPriceMethod(djed, 'sellSC'); + const [scPriceData, rcPriceData] = await Promise.all([ + scaledUnscaledPromise(web3Promise$1(djed, scPriceMethod, 0), BC_DECIMALS), + scaledUnscaledPromise(web3Promise$1(djed, "rcTargetPrice", 0), BC_DECIMALS), + ]); + + const amountScUnscaled = decimalUnscaling(amountScScaled, scDecimals); + const amountRcUnscaled = decimalUnscaling(amountRcScaled, rcDecimals); + + const scValueBC = convertToBC( + amountScUnscaled, + scPriceData[1], + scDecimals + ); + const rcValueBC = convertToBC( + amountRcUnscaled, + rcPriceData[1], + rcDecimals + ); + + const totalValueBC = (BigInt(scValueBC) + BigInt(rcValueBC)).toString(); + + const { treasuryFee, fee } = await getFees(djed); + const totalBCAmount = deductFees(totalValueBC, fee, treasuryFee); + + return { + scPriceScaled: scPriceData[0], + scPriceUnscaled: scPriceData[1], + rcPriceScaled: rcPriceData[0], + rcPriceUnscaled: rcPriceData[1], + amountScUnscaled, + amountRcUnscaled, + totalBCScaled: decimalScaling(totalBCAmount.toString(), BC_DECIMALS), + totalBCUnscaled: totalBCAmount.toString(), + }; + } catch (error) { + console.error("Error in tradeDataPriceSellBoth:", error); + throw error; + } +}; + +/** + * Function to sell both stablecoins and reservecoins + * @param {*} djed DjedContract + * @param {*} account User address + * @param {*} amountSC Unscaled amount of stablecoins to sell + * @param {*} amountRC Unscaled amount of reservecoins to sell + * @param {*} UI UI address for fee + * @param {*} DJED_ADDRESS Address of Djed contract + * @returns + */ +const sellBothTx = ( + djed, + account, + amountSC, + amountRC, + UI, + DJED_ADDRESS +) => { + const data = djed.methods + .sellBothCoins(amountSC, amountRC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); +}; + +// # ISIS / TEFNUT Transaction Functions (ERC20 Base Asset) + +/** + * Buy StableCoins (Isis/Tefnut Variant - ERC20 Base Asset) + * Note: Caller must APPROVE the Djed contract to spend `amountBC` of the Base Asset before calling this. + */ +const buyScIsisTx = (djed, payer, receiver, amountBC, UI, DJED_ADDRESS) => { + // Signature: buyStableCoins(uint256 amountBC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .buyStableCoins(amountBC, receiver, FEE_UI_UNSCALED, UI) + .encodeABI(); + + // Value is 0 because Base Asset is ERC20 transferFrom, not msg.value + return buildTx(payer, DJED_ADDRESS, 0, data); +}; + +const sellScIsisTx = (djed, account, amountSC, UI, DJED_ADDRESS) => { + // Signature: sellStableCoins(uint256 amountSC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .sellStableCoins(amountSC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); +}; + +const buyRcIsisTx = (djed, payer, receiver, amountBC, UI, DJED_ADDRESS) => { + // Signature: buyReserveCoins(uint256 amountBC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .buyReserveCoins(amountBC, receiver, FEE_UI_UNSCALED, UI) + .encodeABI(); + + return buildTx(payer, DJED_ADDRESS, 0, data); + }; + +const sellRcIsisTx = (djed, account, amountRC, UI, DJED_ADDRESS) => { + // Signature: sellReserveCoins(uint256 amountRC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .sellReserveCoins(amountRC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); +}; + +const sellBothIsisTx = (djed, account, amountSC, amountRC, UI, DJED_ADDRESS) => { + // Signature: sellBothCoins(uint256 amountSC, uint256 amountRC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .sellBothCoins(amountSC, amountRC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); +}; + +var contractName$4 = "Djed"; +var abi$7 = [ { inputs: [ { @@ -1270,26 +1357,71 @@ var abi$2 = [ } ]; var djedArtifact = { - contractName: contractName$2, - abi: abi$2 + contractName: contractName$4, + abi: abi$7 }; -var contractName$1 = "Coin"; -var abi$1 = [ +var contractName$3 = "Djed"; +var abi$6 = [ { inputs: [ { - internalType: "string", - name: "name", - type: "string" + internalType: "address", + name: "oracleAddress", + type: "address" }, { - internalType: "string", - name: "symbol", - type: "string" + internalType: "uint256", + name: "_scalingFactor", + type: "uint256" + }, + { + internalType: "address", + name: "_treasury", + type: "address" + }, + { + internalType: "uint256", + name: "_initialTreasuryFee", + type: "uint256" + }, + { + internalType: "uint256", + name: "_treasuryRevenueTarget", + type: "uint256" + }, + { + internalType: "uint256", + name: "_reserveRatioMin", + type: "uint256" + }, + { + internalType: "uint256", + name: "_reserveRatioMax", + type: "uint256" + }, + { + internalType: "uint256", + name: "_fee", + type: "uint256" + }, + { + internalType: "uint256", + name: "_thresholdSupplySC", + type: "uint256" + }, + { + internalType: "uint256", + name: "_rcMinPrice", + type: "uint256" + }, + { + internalType: "uint256", + name: "_txLimit", + type: "uint256" } ], - stateMutability: "nonpayable", + stateMutability: "payable", type: "constructor" }, { @@ -1298,23 +1430,29 @@ var abi$1 = [ { indexed: true, internalType: "address", - name: "owner", + name: "buyer", type: "address" }, { indexed: true, internalType: "address", - name: "spender", + name: "receiver", type: "address" }, { indexed: false, internalType: "uint256", - name: "value", + name: "amountRC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", type: "uint256" } ], - name: "Approval", + name: "BoughtReserveCoins", type: "event" }, { @@ -1323,97 +1461,1678 @@ var abi$1 = [ { indexed: true, internalType: "address", - name: "from", + name: "buyer", type: "address" }, { indexed: true, internalType: "address", - name: "to", + name: "receiver", type: "address" }, { indexed: false, internalType: "uint256", - name: "value", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", type: "uint256" } ], - name: "Transfer", + name: "BoughtStableCoins", type: "event" }, { + anonymous: false, inputs: [ { + indexed: true, internalType: "address", - name: "owner", + name: "seller", type: "address" }, { + indexed: true, internalType: "address", - name: "spender", + name: "receiver", type: "address" - } - ], - name: "allowance", - outputs: [ + }, { + indexed: false, internalType: "uint256", - name: "", + name: "amountSC", type: "uint256" - } - ], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { - internalType: "address", - name: "spender", - type: "address" }, { + indexed: false, internalType: "uint256", - name: "amount", + name: "amountRC", type: "uint256" - } - ], - name: "approve", - outputs: [ + }, { - internalType: "bool", - name: "", - type: "bool" + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" } ], - stateMutability: "nonpayable", - type: "function" + name: "SoldBothCoins", + type: "event" }, { + anonymous: false, inputs: [ { + indexed: true, internalType: "address", - name: "account", + name: "seller", type: "address" - } - ], - name: "balanceOf", - outputs: [ + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, { + indexed: false, internalType: "uint256", - name: "", + name: "amountBC", type: "uint256" } ], - stateMutability: "view", - type: "function" + name: "SoldReserveCoins", + type: "event" }, { + anonymous: false, inputs: [ { + indexed: true, internalType: "address", - name: "spender", + name: "seller", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "SoldStableCoins", + type: "event" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "E", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "L", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "R", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "buyReserveCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "feeUI", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "buyStableCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + ], + name: "fee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "initialTreasuryFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "oracle", + outputs: [ + { + internalType: "contract IFreeOracle", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratio", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "rcBuyingPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "rcDecimalScalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "rcMinPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "rcTargetPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveCoin", + outputs: [ + { + internalType: "contract Coin", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveRatioMax", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveRatioMin", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "scDecimalScalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "scalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellBothCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellReserveCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "feeUI", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellStableCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + ], + name: "stableCoin", + outputs: [ + { + internalType: "contract Coin", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "thresholdSupplySC", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasury", + outputs: [ + { + internalType: "address", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryRevenue", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryRevenueTarget", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "txLimit", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scMaxPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scMinPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratioMax", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratioMin", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "updateOracleValues", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + } +]; +var djedIsisArtifact = { + contractName: contractName$3, + abi: abi$6 +}; + +var contractName$2 = "Djed"; +var abi$5 = [ + { + inputs: [ + { + internalType: "address", + name: "oracleAddress", + type: "address" + }, + { + internalType: "uint256", + name: "_scalingFactor", + type: "uint256" + }, + { + internalType: "address", + name: "_treasury", + type: "address" + }, + { + internalType: "uint256", + name: "_initialTreasuryFee", + type: "uint256" + }, + { + internalType: "uint256", + name: "_treasuryRevenueTarget", + type: "uint256" + }, + { + internalType: "uint256", + name: "_reserveRatioMin", + type: "uint256" + }, + { + internalType: "uint256", + name: "_reserveRatioMax", + type: "uint256" + }, + { + internalType: "uint256", + name: "_fee", + type: "uint256" + }, + { + internalType: "uint256", + name: "_thresholdSupplySC", + type: "uint256" + }, + { + internalType: "uint256", + name: "_rcMinPrice", + type: "uint256" + }, + { + internalType: "uint256", + name: "_txLimit", + type: "uint256" + } + ], + stateMutability: "payable", + type: "constructor" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "buyer", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "BoughtReserveCoins", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "buyer", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "BoughtStableCoins", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "seller", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "SoldBothCoins", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "seller", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "SoldReserveCoins", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "seller", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "SoldStableCoins", + type: "event" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "E", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "L", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "R", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "buyReserveCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "feeUI", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "buyStableCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + ], + name: "fee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "initialTreasuryFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "oracle", + outputs: [ + { + internalType: "contract IFreeOracle", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratio", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "rcBuyingPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "rcDecimalScalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "rcMinPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "rcTargetPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveCoin", + outputs: [ + { + internalType: "contract Coin", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveRatioMax", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveRatioMin", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "scDecimalScalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "scalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellBothCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellReserveCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "feeUI", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellStableCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + ], + name: "stableCoin", + outputs: [ + { + internalType: "contract Coin", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "thresholdSupplySC", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasury", + outputs: [ + { + internalType: "address", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryRevenue", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryRevenueTarget", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "txLimit", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scMaxPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scMinPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratioMax", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratioMin", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "updateOracleValues", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + } +]; +var djedTefnutArtifact = { + contractName: contractName$2, + abi: abi$5 +}; + +var contractName$1 = "Coin"; +var abi$4 = [ + { + inputs: [ + { + internalType: "string", + name: "name", + type: "string" + }, + { + internalType: "string", + name: "symbol", + type: "string" + } + ], + stateMutability: "nonpayable", + type: "constructor" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "spender", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256" + } + ], + name: "Approval", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "from", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "to", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256" + } + ], + name: "Transfer", + type: "event" + }, + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address" + }, + { + internalType: "address", + name: "spender", + type: "address" + } + ], + name: "allowance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool" + } + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "spender", type: "address" }, { @@ -1607,15 +3326,27 @@ var abi$1 = [ ]; var coinArtifact = { contractName: contractName$1, - abi: abi$1 + abi: abi$4 }; -//setting up djed +// Standard Djed (Osiris / Native) const getDjedContract = (web3, DJED_ADDRESS) => { const djed = new web3.eth.Contract(djedArtifact.abi, DJED_ADDRESS); return djed; }; +// Djed Isis (ERC20 Backed) +const getDjedIsisContract = (web3, DJED_ADDRESS) => { + const djed = new web3.eth.Contract(djedIsisArtifact.abi, DJED_ADDRESS); + return djed; +}; + +// Djed Tefnut +const getDjedTefnutContract = (web3, DJED_ADDRESS) => { + const djed = new web3.eth.Contract(djedTefnutArtifact.abi, DJED_ADDRESS); + return djed; +}; + const getCoinContracts = async (djedContract, web3) => { const [stableCoinAddress, reserveCoinAddress] = await Promise.all([ web3Promise$1(djedContract, "stableCoin"), @@ -1628,6 +3359,7 @@ const getCoinContracts = async (djedContract, web3) => { ); return { stableCoin, reserveCoin }; }; + const getDecimals = async (stableCoin, reserveCoin) => { const [scDecimals, rcDecimals] = await Promise.all([ convertInt(web3Promise$1(stableCoin, "decimals")), @@ -1776,8 +3508,84 @@ const getAccountDetails = async ( }; }; +/** + * Utility to listen for Djed contract events + * @param {Object} djedContract - The Web3 contract instance + * @param {Object} callbacks - Object containing callback functions for different events + */ +const subscribeToDjedEvents = (djedContract, callbacks) => { + const events = [ + { name: "BoughtStableCoins", cb: callbacks.onBoughtStableCoins }, + { name: "SoldStableCoins", cb: callbacks.onSoldStableCoins }, + { name: "BoughtReserveCoins", cb: callbacks.onBoughtReserveCoins }, + { name: "SoldReserveCoins", cb: callbacks.onSoldReserveCoins }, + { name: "SoldBothCoins", cb: callbacks.onSoldBothCoins }, + ]; + + const subscriptions = []; + + events.forEach((event) => { + if (event.cb) { + const sub = djedContract.events[event.name]({ + fromBlock: "latest", + }) + .on("data", (data) => { + event.cb(data.returnValues); + }) + .on("error", (err) => { + if (callbacks.onError) callbacks.onError(err); + else console.error(`Error in ${event.name} subscription:`, err); + }); + subscriptions.push(sub); + } + }); + + return { + unsubscribe: () => { + subscriptions.forEach((sub) => { + if (sub.unsubscribe) sub.unsubscribe(); + }); + }, + }; +}; + +/** + * Utility to fetch past events from the Djed contract + * @param {Object} djedContract - The Web3 contract instance + * @param {string} eventName - Name of the event + * @param {Object} filter - Web3 filter object (e.g., { buyer: '0x...' }) + * @param {number|string} fromBlock - Starting block + * @returns {Promise} - Array of past events + */ +const getPastDjedEvents = async ( + djedContract, + eventName, + filter = {}, + fromBlock = 0 +) => { + try { + return await djedContract.getPastEvents(eventName, { + filter, + fromBlock, + toBlock: "latest", + }); + } catch (error) { + console.error(`Error fetching past events for ${eventName}:`, error); + throw error; + } +}; + +const approveTx = (tokenContract, owner, spender, amount) => { + const data = tokenContract.methods.approve(spender, amount).encodeABI(); + return buildTx(owner, tokenContract.options.address, 0, data); +}; + +const checkAllowance = async (tokenContract, owner, spender) => { + return await tokenContract.methods.allowance(owner, spender).call(); +}; + var contractName = "Oracle"; -var abi = [ +var abi$3 = [ { inputs: [ { @@ -2252,6 +4060,259 @@ var abi = [ ]; var oracleArtifact = { contractName: contractName, + abi: abi$3 +}; + +var abi$2 = [ + { + type: "constructor", + inputs: [ + { + name: "_ref", + type: "address", + internalType: "contract IStdReference" + }, + { + name: "_decimals", + type: "uint8", + internalType: "uint8" + }, + { + name: "_hebeSwapDecimals", + type: "uint8", + internalType: "uint8" + }, + { + name: "_baseToken", + type: "string", + internalType: "string" + }, + { + name: "_quoteToken", + type: "string", + internalType: "string" + } + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "acceptTermsOfService", + inputs: [ + ], + outputs: [ + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "baseToken", + inputs: [ + ], + outputs: [ + { + name: "", + type: "string", + internalType: "string" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "quoteToken", + inputs: [ + ], + outputs: [ + { + name: "", + type: "string", + internalType: "string" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "readData", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "ref", + inputs: [ + ], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IStdReference" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "scalingFactor", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + } +]; +var hebeSwapOracleArtifact = { + abi: abi$2 +}; + +var abi$1 = [ + { + type: "constructor", + inputs: [ + { + name: "_dataFeedAddress", + type: "address", + internalType: "address" + }, + { + name: "_decimals", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "acceptTermsOfService", + inputs: [ + ], + outputs: [ + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "readData", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "scalingFactor", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + } +]; +var chainlinkOracleArtifact = { + abi: abi$1 +}; + +var abi = [ + { + type: "constructor", + inputs: [ + { + name: "_proxyAddress", + type: "address", + internalType: "address" + }, + { + name: "_api3Decimals", + type: "uint256", + internalType: "uint256" + }, + { + name: "_decimals", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "acceptTermsOfService", + inputs: [ + ], + outputs: [ + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "proxyAddress", + inputs: [ + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "readData", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "scalingFactor", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + } +]; +var api3OracleArtifact = { abi: abi }; @@ -2266,4 +4327,34 @@ const getOracleContract = (web3, oracleAddress, msgSender) => { return oracle; }; -export { FEE_UI_UNSCALED, appendFees, buyRcTx, buyScTx, calculateBcUsdEquivalent, calculateFutureScPrice, calculateIsRatioAboveMin, calculateIsRatioBelowMax, calculateRcUsdEquivalent, calculateTxFees, convertToBC, deductFees, getAccountDetails, getBcUsdEquivalent, getCoinContracts, getCoinDetails, getDecimals, getDjedContract, getFees, getOracleAddress, getOracleContract, getRcUsdEquivalent, getScAdaEquivalent, getSystemParams, getWeb3, isTxLimitReached, promiseTx, scalingFactor, sellRcTx, sellScTx, tradeDataPriceBuyRc, tradeDataPriceBuySc, tradeDataPriceCore, tradeDataPriceSellRc, tradeDataPriceSellSc, verifyTx }; +/** + * Added export to resolve Rollup error + */ +const getHebeSwapOracleContract = (web3, oracleAddress, msgSender) => { + const oracle = new web3.eth.Contract(hebeSwapOracleArtifact.abi, oracleAddress, { + from: msgSender + }); + return oracle; +}; + +/** + * Added export to resolve Rollup error + */ +const getChainlinkOracleContract = (web3, oracleAddress, msgSender) => { + const oracle = new web3.eth.Contract(chainlinkOracleArtifact.abi, oracleAddress, { + from: msgSender + }); + return oracle; +}; + +/** + * Added export to resolve Rollup error + */ +const getAPI3OracleContract = (web3, oracleAddress, msgSender) => { + const oracle = new web3.eth.Contract(api3OracleArtifact.abi, oracleAddress, { + from: msgSender + }); + return oracle; +}; + +export { FEE_UI_UNSCALED, appendFees, approveTx, buyRcIsisTx, buyRcTx, buyScIsisTx, buyScTx, calculateBcUsdEquivalent, calculateFutureScPrice, calculateIsRatioAboveMin, calculateIsRatioBelowMax, calculateRcUsdEquivalent, calculateTxFees, checkAllowance, convertToBC, deductFees, getAPI3OracleContract, getAccountDetails, getBcUsdEquivalent, getChainlinkOracleContract, getCoinContracts, getCoinDetails, getDecimals, getDjedContract, getDjedIsisContract, getDjedTefnutContract, getFees, getHebeSwapOracleContract, getOracleAddress, getOracleContract, getPastDjedEvents, getRcUsdEquivalent, getScAdaEquivalent, getSystemParams, getWeb3, isTxLimitReached, promiseTx, scalingFactor, sellBothIsisTx, sellBothTx, sellRcIsisTx, sellRcTx, sellScIsisTx, sellScTx, subscribeToDjedEvents, tradeDataPriceBuyRc, tradeDataPriceBuySc, tradeDataPriceCore, tradeDataPriceSellBoth, tradeDataPriceSellRc, tradeDataPriceSellSc, verifyTx }; diff --git a/djed-sdk/dist/umd/index.js b/djed-sdk/dist/umd/index.js index 24bf322..f971a95 100644 --- a/djed-sdk/dist/umd/index.js +++ b/djed-sdk/dist/umd/index.js @@ -162,6 +162,7 @@ (FEE_UI / 100).toString(), SCALING_DECIMALS ); + const tradeDataPriceCore = (djed, method, decimals, amountScaled) => { const amountUnscaled = decimalUnscaling(amountScaled, decimals); return scaledUnscaledPromise(web3Promise$1(djed, method, 0), BC_DECIMALS).then( @@ -187,29 +188,11 @@ ); }; - /** - * Function that converts coin amount to BC - * @param {*} amount unscaled coin amount to be converted to BC - * @param {*} price unscaled coin price - * @param {*} decimals coin decimals - * @returns unscaled BC amount - */ const convertToBC = (amount, price, decimals) => { const decimalScalingFactor = BigInt(Math.pow(10, decimals)); return (BigInt(amount) * BigInt(price)) / decimalScalingFactor; }; - /** - * Calculate if the transaction will reach the maximum reserve ratio - * @param scPrice - Unscaled stablecoin price - * @param reserveBc - Unscaled reserve of base coin with appended potential transaction amount. - * Example: If user wants to buy 1RC, the reserveBc param will be calculated as sum of current reserve of BC and desired RC amount converted in BC - * @param totalScSupply - Unscaled total stablecoin supply - * @param reserveRatioMax - Unscaled maximum reserve ratio - * @param scDecimalScalingFactor - If stablecoin has 6 decimals, scDecimalScalingFactor will be calculated as 10^6 - * @param thresholdSupplySC - Unscaled threshold SC supply - * @returns - */ const calculateIsRatioBelowMax = ({ scPrice, reserveBc, @@ -227,16 +210,6 @@ ); }; - /** - * Calculate if the transaction will reach the minimum reserve ratio - * @param scPrice - Unscaled stablecoin price - * @param reserveBc - Unscaled reserve of base coin with calculated potential transaction amount. - * Example: If user wants to buy 1SC, the reserveBc param will be calculated as sum of current reserve of BC and desired SC amount converted in BC - * @param totalScSupply - Unscaled total stablecoin supply - * @param reserveRatioMin - Unscaled minimum reserve ratio - * @param scDecimalScalingFactor - If stablecoin has 6 decimals, scDecimalScalingFactor will be calculated as 10^6 - * @returns - */ const calculateIsRatioAboveMin = ({ scPrice, reserveBc, @@ -252,15 +225,8 @@ ); }; - /** - * - * @param {*} amountUSD - * @param {*} totalSCSupply - * @param {*} thresholdSCSupply - * @returns - */ - const isTxLimitReached = (amountUSD, totalSCSupply, thresholdSCSupply) => - amountUSD > TRANSACTION_USD_LIMIT && + const isTxLimitReached = (amountUSD, totalSCSupply, thresholdSCSupply, txLimit) => + (BigInt(amountUSD) > BigInt(txLimit || TRANSACTION_USD_LIMIT)) && BigInt(totalSCSupply) >= BigInt(thresholdSCSupply); const promiseTx = (isWalletConnected, tx, signer) => { @@ -283,13 +249,6 @@ }); }; - /** - * Function that deducts all platform fees from the BC amount - * @param {*} value The amount of BC from which fees should be deducted - * @param {*} fee The platform fee - * @param {*} treasuryFee The treasury fee - * @returns BC value with all fees calculated - */ const calculateTxFees = (value, fee, treasuryFee, feeUI) => { const f = (BigInt(value) * BigInt(fee)) / BigInt(scalingFactor); const f_ui = @@ -299,26 +258,11 @@ return { f, f_ui, f_t }; }; - /** - * Function that deducts all platform fees from the BC amount - * @param {*} value The amount of BC from which fees should be deducted - * @param {*} fee The platform fee - * @param {*} treasuryFee The treasury fee - * @returns BC value with all fees calculated - */ const deductFees = (value, fee, treasuryFee) => { const { f, f_ui, f_t } = calculateTxFees(value, fee, treasuryFee); return BigInt(value) - f - f_ui - f_t; }; - /** - * Function that appends all platform fees to the BC amount - * @param {*} amountBC The unscaled amount of BC (e.g. for 1BC, value should be 1 * 10^BC_DECIMALS) - * @param {*} treasuryFee Treasury fee unscaled (e.g. If the fee is 1%, than 1/100 * scalingFactor) - * @param {*} fee Fee unscaled (e.g. If the fee is 1%, than 1/100 * scalingFactor) - * @param {*} fee_UI UI fee unscaled (e.g. If the fee is 1%, than 1/100 * scalingFactor) - * @returns Unscaled BC amount with calculated fees - */ const appendFees = (amountBC, treasuryFee, fee, fee_UI) => { const totalFees = BigInt(treasuryFee) + BigInt(fee) + BigInt(fee_UI); const substractedFees = BigInt(scalingFactor) - totalFees; @@ -328,11 +272,6 @@ return appendedFeesAmount.toString(); }; - /** - * Function that returns treasury and platform fees - * @param {*} djed Djed contract - * @returns Treasury and platform fee - */ const getFees = async (djed) => { try { const [treasuryFee, fee] = await Promise.all([ @@ -348,6 +287,23 @@ } }; + /** + * Added getPriceMethod export to fix Rollup Error + */ + const getPriceMethod = async (djed, operation) => { + const isShu = await djed.methods.scMaxPrice(0).call().then(() => true).catch(() => false); + + if (!isShu) return "scPrice"; + + switch (operation) { + case 'buySC': return "scMaxPrice"; + case 'sellSC': return "scMinPrice"; + case 'buyRC': return "scMinPrice"; + case 'sellRC': return "scMaxPrice"; + default: return "scPrice"; + } + }; + /** * Function that calculates fees and how much BC (totalBCAmount) user should pay to receive desired amount of reserve coin * @param {*} djed DjedContract @@ -547,8 +503,139 @@ } }; - var contractName$2 = "Djed"; - var abi$2 = [ + /** + * Function that calculates fees and how much BC (totalBCAmount) user will receive if he sells desired amount of stable coin and reserve coin + * @param {*} djed DjedContract + * @param {*} scDecimals Stable coin decimals + * @param {*} rcDecimals Reserve coin decimals + * @param {*} amountScScaled Stable coin amount that user wants to sell + * @param {*} amountRcScaled Reserve coin amount that user wants to sell + * @returns + */ + const tradeDataPriceSellBoth = async ( + djed, + scDecimals, + rcDecimals, + amountScScaled, + amountRcScaled + ) => { + try { + const scPriceMethod = await getPriceMethod(djed, 'sellSC'); + const [scPriceData, rcPriceData] = await Promise.all([ + scaledUnscaledPromise(web3Promise$1(djed, scPriceMethod, 0), BC_DECIMALS), + scaledUnscaledPromise(web3Promise$1(djed, "rcTargetPrice", 0), BC_DECIMALS), + ]); + + const amountScUnscaled = decimalUnscaling(amountScScaled, scDecimals); + const amountRcUnscaled = decimalUnscaling(amountRcScaled, rcDecimals); + + const scValueBC = convertToBC( + amountScUnscaled, + scPriceData[1], + scDecimals + ); + const rcValueBC = convertToBC( + amountRcUnscaled, + rcPriceData[1], + rcDecimals + ); + + const totalValueBC = (BigInt(scValueBC) + BigInt(rcValueBC)).toString(); + + const { treasuryFee, fee } = await getFees(djed); + const totalBCAmount = deductFees(totalValueBC, fee, treasuryFee); + + return { + scPriceScaled: scPriceData[0], + scPriceUnscaled: scPriceData[1], + rcPriceScaled: rcPriceData[0], + rcPriceUnscaled: rcPriceData[1], + amountScUnscaled, + amountRcUnscaled, + totalBCScaled: decimalScaling(totalBCAmount.toString(), BC_DECIMALS), + totalBCUnscaled: totalBCAmount.toString(), + }; + } catch (error) { + console.error("Error in tradeDataPriceSellBoth:", error); + throw error; + } + }; + + /** + * Function to sell both stablecoins and reservecoins + * @param {*} djed DjedContract + * @param {*} account User address + * @param {*} amountSC Unscaled amount of stablecoins to sell + * @param {*} amountRC Unscaled amount of reservecoins to sell + * @param {*} UI UI address for fee + * @param {*} DJED_ADDRESS Address of Djed contract + * @returns + */ + const sellBothTx = ( + djed, + account, + amountSC, + amountRC, + UI, + DJED_ADDRESS + ) => { + const data = djed.methods + .sellBothCoins(amountSC, amountRC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); + }; + + // # ISIS / TEFNUT Transaction Functions (ERC20 Base Asset) + + /** + * Buy StableCoins (Isis/Tefnut Variant - ERC20 Base Asset) + * Note: Caller must APPROVE the Djed contract to spend `amountBC` of the Base Asset before calling this. + */ + const buyScIsisTx = (djed, payer, receiver, amountBC, UI, DJED_ADDRESS) => { + // Signature: buyStableCoins(uint256 amountBC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .buyStableCoins(amountBC, receiver, FEE_UI_UNSCALED, UI) + .encodeABI(); + + // Value is 0 because Base Asset is ERC20 transferFrom, not msg.value + return buildTx(payer, DJED_ADDRESS, 0, data); + }; + + const sellScIsisTx = (djed, account, amountSC, UI, DJED_ADDRESS) => { + // Signature: sellStableCoins(uint256 amountSC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .sellStableCoins(amountSC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); + }; + + const buyRcIsisTx = (djed, payer, receiver, amountBC, UI, DJED_ADDRESS) => { + // Signature: buyReserveCoins(uint256 amountBC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .buyReserveCoins(amountBC, receiver, FEE_UI_UNSCALED, UI) + .encodeABI(); + + return buildTx(payer, DJED_ADDRESS, 0, data); + }; + + const sellRcIsisTx = (djed, account, amountRC, UI, DJED_ADDRESS) => { + // Signature: sellReserveCoins(uint256 amountRC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .sellReserveCoins(amountRC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); + }; + + const sellBothIsisTx = (djed, account, amountSC, amountRC, UI, DJED_ADDRESS) => { + // Signature: sellBothCoins(uint256 amountSC, uint256 amountRC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .sellBothCoins(amountSC, amountRC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); + }; + + var contractName$4 = "Djed"; + var abi$7 = [ { inputs: [ { @@ -1274,26 +1361,71 @@ } ]; var djedArtifact = { - contractName: contractName$2, - abi: abi$2 + contractName: contractName$4, + abi: abi$7 }; - var contractName$1 = "Coin"; - var abi$1 = [ + var contractName$3 = "Djed"; + var abi$6 = [ { inputs: [ { - internalType: "string", - name: "name", - type: "string" + internalType: "address", + name: "oracleAddress", + type: "address" }, { - internalType: "string", - name: "symbol", - type: "string" + internalType: "uint256", + name: "_scalingFactor", + type: "uint256" + }, + { + internalType: "address", + name: "_treasury", + type: "address" + }, + { + internalType: "uint256", + name: "_initialTreasuryFee", + type: "uint256" + }, + { + internalType: "uint256", + name: "_treasuryRevenueTarget", + type: "uint256" + }, + { + internalType: "uint256", + name: "_reserveRatioMin", + type: "uint256" + }, + { + internalType: "uint256", + name: "_reserveRatioMax", + type: "uint256" + }, + { + internalType: "uint256", + name: "_fee", + type: "uint256" + }, + { + internalType: "uint256", + name: "_thresholdSupplySC", + type: "uint256" + }, + { + internalType: "uint256", + name: "_rcMinPrice", + type: "uint256" + }, + { + internalType: "uint256", + name: "_txLimit", + type: "uint256" } ], - stateMutability: "nonpayable", + stateMutability: "payable", type: "constructor" }, { @@ -1302,23 +1434,29 @@ { indexed: true, internalType: "address", - name: "owner", + name: "buyer", type: "address" }, { indexed: true, internalType: "address", - name: "spender", + name: "receiver", type: "address" }, { indexed: false, internalType: "uint256", - name: "value", + name: "amountRC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", type: "uint256" } ], - name: "Approval", + name: "BoughtReserveCoins", type: "event" }, { @@ -1327,97 +1465,1678 @@ { indexed: true, internalType: "address", - name: "from", + name: "buyer", type: "address" }, { indexed: true, internalType: "address", - name: "to", + name: "receiver", type: "address" }, { indexed: false, internalType: "uint256", - name: "value", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", type: "uint256" } ], - name: "Transfer", + name: "BoughtStableCoins", type: "event" }, { + anonymous: false, inputs: [ { + indexed: true, internalType: "address", - name: "owner", + name: "seller", type: "address" }, { + indexed: true, internalType: "address", - name: "spender", + name: "receiver", type: "address" - } - ], - name: "allowance", - outputs: [ + }, { + indexed: false, internalType: "uint256", - name: "", + name: "amountSC", type: "uint256" - } - ], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { - internalType: "address", - name: "spender", - type: "address" }, { + indexed: false, internalType: "uint256", - name: "amount", + name: "amountRC", type: "uint256" - } - ], - name: "approve", - outputs: [ + }, { - internalType: "bool", - name: "", - type: "bool" + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" } ], - stateMutability: "nonpayable", - type: "function" + name: "SoldBothCoins", + type: "event" }, { + anonymous: false, inputs: [ { + indexed: true, internalType: "address", - name: "account", + name: "seller", type: "address" - } - ], - name: "balanceOf", - outputs: [ + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, { + indexed: false, internalType: "uint256", - name: "", + name: "amountBC", type: "uint256" } ], - stateMutability: "view", - type: "function" + name: "SoldReserveCoins", + type: "event" }, { + anonymous: false, inputs: [ { + indexed: true, internalType: "address", - name: "spender", + name: "seller", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "SoldStableCoins", + type: "event" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "E", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "L", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "R", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "buyReserveCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "feeUI", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "buyStableCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + ], + name: "fee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "initialTreasuryFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "oracle", + outputs: [ + { + internalType: "contract IFreeOracle", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratio", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "rcBuyingPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "rcDecimalScalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "rcMinPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "rcTargetPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveCoin", + outputs: [ + { + internalType: "contract Coin", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveRatioMax", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveRatioMin", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "scDecimalScalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "scalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellBothCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellReserveCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "feeUI", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellStableCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + ], + name: "stableCoin", + outputs: [ + { + internalType: "contract Coin", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "thresholdSupplySC", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasury", + outputs: [ + { + internalType: "address", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryRevenue", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryRevenueTarget", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "txLimit", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scMaxPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scMinPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratioMax", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratioMin", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "updateOracleValues", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + } + ]; + var djedIsisArtifact = { + contractName: contractName$3, + abi: abi$6 + }; + + var contractName$2 = "Djed"; + var abi$5 = [ + { + inputs: [ + { + internalType: "address", + name: "oracleAddress", + type: "address" + }, + { + internalType: "uint256", + name: "_scalingFactor", + type: "uint256" + }, + { + internalType: "address", + name: "_treasury", + type: "address" + }, + { + internalType: "uint256", + name: "_initialTreasuryFee", + type: "uint256" + }, + { + internalType: "uint256", + name: "_treasuryRevenueTarget", + type: "uint256" + }, + { + internalType: "uint256", + name: "_reserveRatioMin", + type: "uint256" + }, + { + internalType: "uint256", + name: "_reserveRatioMax", + type: "uint256" + }, + { + internalType: "uint256", + name: "_fee", + type: "uint256" + }, + { + internalType: "uint256", + name: "_thresholdSupplySC", + type: "uint256" + }, + { + internalType: "uint256", + name: "_rcMinPrice", + type: "uint256" + }, + { + internalType: "uint256", + name: "_txLimit", + type: "uint256" + } + ], + stateMutability: "payable", + type: "constructor" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "buyer", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "BoughtReserveCoins", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "buyer", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "BoughtStableCoins", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "seller", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "SoldBothCoins", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "seller", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "SoldReserveCoins", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "seller", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + indexed: false, + internalType: "uint256", + name: "amountBC", + type: "uint256" + } + ], + name: "SoldStableCoins", + type: "event" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "E", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "L", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "R", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "buyReserveCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "feeUI", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "buyStableCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + ], + name: "fee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "initialTreasuryFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "oracle", + outputs: [ + { + internalType: "contract IFreeOracle", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratio", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "rcBuyingPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "rcDecimalScalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "rcMinPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "rcTargetPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveCoin", + outputs: [ + { + internalType: "contract Coin", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveRatioMax", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "reserveRatioMin", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "scDecimalScalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "scalingFactor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellBothCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountRC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "fee_ui", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellReserveCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "amountSC", + type: "uint256" + }, + { + internalType: "address", + name: "receiver", + type: "address" + }, + { + internalType: "uint256", + name: "feeUI", + type: "uint256" + }, + { + internalType: "address", + name: "ui", + type: "address" + } + ], + name: "sellStableCoins", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + ], + name: "stableCoin", + outputs: [ + { + internalType: "contract Coin", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "thresholdSupplySC", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasury", + outputs: [ + { + internalType: "address", + name: "", + type: "address" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryRevenue", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "treasuryRevenueTarget", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "txLimit", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scMaxPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "uint256", + name: "_currentPaymentAmount", + type: "uint256" + } + ], + name: "scMinPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratioMax", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "ratioMin", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + ], + name: "updateOracleValues", + outputs: [ + ], + stateMutability: "nonpayable", + type: "function" + } + ]; + var djedTefnutArtifact = { + contractName: contractName$2, + abi: abi$5 + }; + + var contractName$1 = "Coin"; + var abi$4 = [ + { + inputs: [ + { + internalType: "string", + name: "name", + type: "string" + }, + { + internalType: "string", + name: "symbol", + type: "string" + } + ], + stateMutability: "nonpayable", + type: "constructor" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "spender", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256" + } + ], + name: "Approval", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "from", + type: "address" + }, + { + indexed: true, + internalType: "address", + name: "to", + type: "address" + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256" + } + ], + name: "Transfer", + type: "event" + }, + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address" + }, + { + internalType: "address", + name: "spender", + type: "address" + } + ], + name: "allowance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool" + } + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "spender", type: "address" }, { @@ -1611,15 +3330,27 @@ ]; var coinArtifact = { contractName: contractName$1, - abi: abi$1 + abi: abi$4 }; - //setting up djed + // Standard Djed (Osiris / Native) const getDjedContract = (web3, DJED_ADDRESS) => { const djed = new web3.eth.Contract(djedArtifact.abi, DJED_ADDRESS); return djed; }; + // Djed Isis (ERC20 Backed) + const getDjedIsisContract = (web3, DJED_ADDRESS) => { + const djed = new web3.eth.Contract(djedIsisArtifact.abi, DJED_ADDRESS); + return djed; + }; + + // Djed Tefnut + const getDjedTefnutContract = (web3, DJED_ADDRESS) => { + const djed = new web3.eth.Contract(djedTefnutArtifact.abi, DJED_ADDRESS); + return djed; + }; + const getCoinContracts = async (djedContract, web3) => { const [stableCoinAddress, reserveCoinAddress] = await Promise.all([ web3Promise$1(djedContract, "stableCoin"), @@ -1632,6 +3363,7 @@ ); return { stableCoin, reserveCoin }; }; + const getDecimals = async (stableCoin, reserveCoin) => { const [scDecimals, rcDecimals] = await Promise.all([ convertInt(web3Promise$1(stableCoin, "decimals")), @@ -1780,8 +3512,84 @@ }; }; + /** + * Utility to listen for Djed contract events + * @param {Object} djedContract - The Web3 contract instance + * @param {Object} callbacks - Object containing callback functions for different events + */ + const subscribeToDjedEvents = (djedContract, callbacks) => { + const events = [ + { name: "BoughtStableCoins", cb: callbacks.onBoughtStableCoins }, + { name: "SoldStableCoins", cb: callbacks.onSoldStableCoins }, + { name: "BoughtReserveCoins", cb: callbacks.onBoughtReserveCoins }, + { name: "SoldReserveCoins", cb: callbacks.onSoldReserveCoins }, + { name: "SoldBothCoins", cb: callbacks.onSoldBothCoins }, + ]; + + const subscriptions = []; + + events.forEach((event) => { + if (event.cb) { + const sub = djedContract.events[event.name]({ + fromBlock: "latest", + }) + .on("data", (data) => { + event.cb(data.returnValues); + }) + .on("error", (err) => { + if (callbacks.onError) callbacks.onError(err); + else console.error(`Error in ${event.name} subscription:`, err); + }); + subscriptions.push(sub); + } + }); + + return { + unsubscribe: () => { + subscriptions.forEach((sub) => { + if (sub.unsubscribe) sub.unsubscribe(); + }); + }, + }; + }; + + /** + * Utility to fetch past events from the Djed contract + * @param {Object} djedContract - The Web3 contract instance + * @param {string} eventName - Name of the event + * @param {Object} filter - Web3 filter object (e.g., { buyer: '0x...' }) + * @param {number|string} fromBlock - Starting block + * @returns {Promise} - Array of past events + */ + const getPastDjedEvents = async ( + djedContract, + eventName, + filter = {}, + fromBlock = 0 + ) => { + try { + return await djedContract.getPastEvents(eventName, { + filter, + fromBlock, + toBlock: "latest", + }); + } catch (error) { + console.error(`Error fetching past events for ${eventName}:`, error); + throw error; + } + }; + + const approveTx = (tokenContract, owner, spender, amount) => { + const data = tokenContract.methods.approve(spender, amount).encodeABI(); + return buildTx(owner, tokenContract.options.address, 0, data); + }; + + const checkAllowance = async (tokenContract, owner, spender) => { + return await tokenContract.methods.allowance(owner, spender).call(); + }; + var contractName = "Oracle"; - var abi = [ + var abi$3 = [ { inputs: [ { @@ -2256,6 +4064,259 @@ ]; var oracleArtifact = { contractName: contractName, + abi: abi$3 + }; + + var abi$2 = [ + { + type: "constructor", + inputs: [ + { + name: "_ref", + type: "address", + internalType: "contract IStdReference" + }, + { + name: "_decimals", + type: "uint8", + internalType: "uint8" + }, + { + name: "_hebeSwapDecimals", + type: "uint8", + internalType: "uint8" + }, + { + name: "_baseToken", + type: "string", + internalType: "string" + }, + { + name: "_quoteToken", + type: "string", + internalType: "string" + } + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "acceptTermsOfService", + inputs: [ + ], + outputs: [ + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "baseToken", + inputs: [ + ], + outputs: [ + { + name: "", + type: "string", + internalType: "string" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "quoteToken", + inputs: [ + ], + outputs: [ + { + name: "", + type: "string", + internalType: "string" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "readData", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "ref", + inputs: [ + ], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IStdReference" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "scalingFactor", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + } + ]; + var hebeSwapOracleArtifact = { + abi: abi$2 + }; + + var abi$1 = [ + { + type: "constructor", + inputs: [ + { + name: "_dataFeedAddress", + type: "address", + internalType: "address" + }, + { + name: "_decimals", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "acceptTermsOfService", + inputs: [ + ], + outputs: [ + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "readData", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "scalingFactor", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + } + ]; + var chainlinkOracleArtifact = { + abi: abi$1 + }; + + var abi = [ + { + type: "constructor", + inputs: [ + { + name: "_proxyAddress", + type: "address", + internalType: "address" + }, + { + name: "_api3Decimals", + type: "uint256", + internalType: "uint256" + }, + { + name: "_decimals", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "acceptTermsOfService", + inputs: [ + ], + outputs: [ + ], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "proxyAddress", + inputs: [ + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "readData", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "scalingFactor", + inputs: [ + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256" + } + ], + stateMutability: "view" + } + ]; + var api3OracleArtifact = { abi: abi }; @@ -2270,9 +4331,42 @@ return oracle; }; + /** + * Added export to resolve Rollup error + */ + const getHebeSwapOracleContract = (web3, oracleAddress, msgSender) => { + const oracle = new web3.eth.Contract(hebeSwapOracleArtifact.abi, oracleAddress, { + from: msgSender + }); + return oracle; + }; + + /** + * Added export to resolve Rollup error + */ + const getChainlinkOracleContract = (web3, oracleAddress, msgSender) => { + const oracle = new web3.eth.Contract(chainlinkOracleArtifact.abi, oracleAddress, { + from: msgSender + }); + return oracle; + }; + + /** + * Added export to resolve Rollup error + */ + const getAPI3OracleContract = (web3, oracleAddress, msgSender) => { + const oracle = new web3.eth.Contract(api3OracleArtifact.abi, oracleAddress, { + from: msgSender + }); + return oracle; + }; + exports.FEE_UI_UNSCALED = FEE_UI_UNSCALED; exports.appendFees = appendFees; + exports.approveTx = approveTx; + exports.buyRcIsisTx = buyRcIsisTx; exports.buyRcTx = buyRcTx; + exports.buyScIsisTx = buyScIsisTx; exports.buyScTx = buyScTx; exports.calculateBcUsdEquivalent = calculateBcUsdEquivalent; exports.calculateFutureScPrice = calculateFutureScPrice; @@ -2280,17 +4374,24 @@ exports.calculateIsRatioBelowMax = calculateIsRatioBelowMax; exports.calculateRcUsdEquivalent = calculateRcUsdEquivalent; exports.calculateTxFees = calculateTxFees; + exports.checkAllowance = checkAllowance; exports.convertToBC = convertToBC; exports.deductFees = deductFees; + exports.getAPI3OracleContract = getAPI3OracleContract; exports.getAccountDetails = getAccountDetails; exports.getBcUsdEquivalent = getBcUsdEquivalent; + exports.getChainlinkOracleContract = getChainlinkOracleContract; exports.getCoinContracts = getCoinContracts; exports.getCoinDetails = getCoinDetails; exports.getDecimals = getDecimals; exports.getDjedContract = getDjedContract; + exports.getDjedIsisContract = getDjedIsisContract; + exports.getDjedTefnutContract = getDjedTefnutContract; exports.getFees = getFees; + exports.getHebeSwapOracleContract = getHebeSwapOracleContract; exports.getOracleAddress = getOracleAddress; exports.getOracleContract = getOracleContract; + exports.getPastDjedEvents = getPastDjedEvents; exports.getRcUsdEquivalent = getRcUsdEquivalent; exports.getScAdaEquivalent = getScAdaEquivalent; exports.getSystemParams = getSystemParams; @@ -2298,11 +4399,17 @@ exports.isTxLimitReached = isTxLimitReached; exports.promiseTx = promiseTx; exports.scalingFactor = scalingFactor; + exports.sellBothIsisTx = sellBothIsisTx; + exports.sellBothTx = sellBothTx; + exports.sellRcIsisTx = sellRcIsisTx; exports.sellRcTx = sellRcTx; + exports.sellScIsisTx = sellScIsisTx; exports.sellScTx = sellScTx; + exports.subscribeToDjedEvents = subscribeToDjedEvents; exports.tradeDataPriceBuyRc = tradeDataPriceBuyRc; exports.tradeDataPriceBuySc = tradeDataPriceBuySc; exports.tradeDataPriceCore = tradeDataPriceCore; + exports.tradeDataPriceSellBoth = tradeDataPriceSellBoth; exports.tradeDataPriceSellRc = tradeDataPriceSellRc; exports.tradeDataPriceSellSc = tradeDataPriceSellSc; exports.verifyTx = verifyTx; diff --git a/djed-sdk/package.json b/djed-sdk/package.json index e6bcdfa..749d947 100644 --- a/djed-sdk/package.json +++ b/djed-sdk/package.json @@ -5,7 +5,9 @@ "main": "dist/umd/index.js", "module": "dist/esm/index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "build": "rollup -c", + "prepare": "npm run build" }, "keywords": [], "author": "", diff --git a/djed-sdk/src/artifacts/API3OracleABI.json b/djed-sdk/src/artifacts/API3OracleABI.json new file mode 100644 index 0000000..854db02 --- /dev/null +++ b/djed-sdk/src/artifacts/API3OracleABI.json @@ -0,0 +1 @@ +{"abi":[{"type":"constructor","inputs":[{"name":"_proxyAddress","type":"address","internalType":"address"},{"name":"_api3Decimals","type":"uint256","internalType":"uint256"},{"name":"_decimals","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"acceptTermsOfService","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"proxyAddress","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"readData","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"scalingFactor","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}]} \ No newline at end of file diff --git a/djed-sdk/src/artifacts/ChainlinkOracleABI.json b/djed-sdk/src/artifacts/ChainlinkOracleABI.json new file mode 100644 index 0000000..edf3a6c --- /dev/null +++ b/djed-sdk/src/artifacts/ChainlinkOracleABI.json @@ -0,0 +1 @@ +{"abi":[{"type":"constructor","inputs":[{"name":"_dataFeedAddress","type":"address","internalType":"address"},{"name":"_decimals","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"acceptTermsOfService","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"readData","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"scalingFactor","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}]} \ No newline at end of file diff --git a/djed-sdk/src/artifacts/DjedIsisABI.json b/djed-sdk/src/artifacts/DjedIsisABI.json new file mode 100644 index 0000000..0860978 --- /dev/null +++ b/djed-sdk/src/artifacts/DjedIsisABI.json @@ -0,0 +1,61 @@ +{ + "contractName": "Djed", + "abi": [ + { + "inputs": [ + { "internalType": "address", "name": "oracleAddress", "type": "address" }, + { "internalType": "uint256", "name": "_scalingFactor", "type": "uint256" }, + { "internalType": "address", "name": "_treasury", "type": "address" }, + { "internalType": "uint256", "name": "_initialTreasuryFee", "type": "uint256" }, + { "internalType": "uint256", "name": "_treasuryRevenueTarget", "type": "uint256" }, + { "internalType": "uint256", "name": "_reserveRatioMin", "type": "uint256" }, + { "internalType": "uint256", "name": "_reserveRatioMax", "type": "uint256" }, + { "internalType": "uint256", "name": "_fee", "type": "uint256" }, + { "internalType": "uint256", "name": "_thresholdSupplySC", "type": "uint256" }, + { "internalType": "uint256", "name": "_rcMinPrice", "type": "uint256" }, + { "internalType": "uint256", "name": "_txLimit", "type": "uint256" } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "buyer", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "BoughtReserveCoins", "type": "event" }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "buyer", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "BoughtStableCoins", "type": "event" }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "seller", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "SoldBothCoins", "type": "event" }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "seller", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "SoldReserveCoins", "type": "event" }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "seller", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "SoldStableCoins", "type": "event" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "E", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "L", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "R", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "fee_ui", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "buyReserveCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "feeUI", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "buyStableCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [], "name": "fee", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "initialTreasuryFee", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "oracle", "outputs": [{ "internalType": "contract IFreeOracle", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "ratio", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "rcBuyingPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "rcDecimalScalingFactor", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "rcMinPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "rcTargetPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "reserveCoin", "outputs": [{ "internalType": "contract Coin", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "reserveRatioMax", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "reserveRatioMin", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "scDecimalScalingFactor", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "scPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "scalingFactor", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "fee_ui", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "sellBothCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "fee_ui", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "sellReserveCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "feeUI", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "sellStableCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [], "name": "stableCoin", "outputs": [{ "internalType": "contract Coin", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "thresholdSupplySC", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "treasury", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "treasuryFee", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "treasuryRevenue", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "treasuryRevenueTarget", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "txLimit", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "scMaxPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "scMinPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "ratioMax", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "ratioMin", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "updateOracleValues", "outputs": [], "stateMutability": "nonpayable", "type": "function" } + ] +} \ No newline at end of file diff --git a/djed-sdk/src/artifacts/DjedTefnutABI.json b/djed-sdk/src/artifacts/DjedTefnutABI.json new file mode 100644 index 0000000..0860978 --- /dev/null +++ b/djed-sdk/src/artifacts/DjedTefnutABI.json @@ -0,0 +1,61 @@ +{ + "contractName": "Djed", + "abi": [ + { + "inputs": [ + { "internalType": "address", "name": "oracleAddress", "type": "address" }, + { "internalType": "uint256", "name": "_scalingFactor", "type": "uint256" }, + { "internalType": "address", "name": "_treasury", "type": "address" }, + { "internalType": "uint256", "name": "_initialTreasuryFee", "type": "uint256" }, + { "internalType": "uint256", "name": "_treasuryRevenueTarget", "type": "uint256" }, + { "internalType": "uint256", "name": "_reserveRatioMin", "type": "uint256" }, + { "internalType": "uint256", "name": "_reserveRatioMax", "type": "uint256" }, + { "internalType": "uint256", "name": "_fee", "type": "uint256" }, + { "internalType": "uint256", "name": "_thresholdSupplySC", "type": "uint256" }, + { "internalType": "uint256", "name": "_rcMinPrice", "type": "uint256" }, + { "internalType": "uint256", "name": "_txLimit", "type": "uint256" } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "buyer", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "BoughtReserveCoins", "type": "event" }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "buyer", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "BoughtStableCoins", "type": "event" }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "seller", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "SoldBothCoins", "type": "event" }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "seller", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "SoldReserveCoins", "type": "event" }, + { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "seller", "type": "address" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amountBC", "type": "uint256" }], "name": "SoldStableCoins", "type": "event" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "E", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "L", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "R", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "fee_ui", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "buyReserveCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "feeUI", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "buyStableCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [], "name": "fee", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "initialTreasuryFee", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "oracle", "outputs": [{ "internalType": "contract IFreeOracle", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "ratio", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "rcBuyingPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "rcDecimalScalingFactor", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "rcMinPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "rcTargetPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "reserveCoin", "outputs": [{ "internalType": "contract Coin", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "reserveRatioMax", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "reserveRatioMin", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "scDecimalScalingFactor", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "scPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "scalingFactor", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "fee_ui", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "sellBothCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amountRC", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "fee_ui", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "sellReserveCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "amountSC", "type": "uint256" }, { "internalType": "address", "name": "receiver", "type": "address" }, { "internalType": "uint256", "name": "feeUI", "type": "uint256" }, { "internalType": "address", "name": "ui", "type": "address" }], "name": "sellStableCoins", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [], "name": "stableCoin", "outputs": [{ "internalType": "contract Coin", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "thresholdSupplySC", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "treasury", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "treasuryFee", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "treasuryRevenue", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "treasuryRevenueTarget", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "txLimit", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "scMaxPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [{ "internalType": "uint256", "name": "_currentPaymentAmount", "type": "uint256" }], "name": "scMinPrice", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "ratioMax", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "ratioMin", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "updateOracleValues", "outputs": [], "stateMutability": "nonpayable", "type": "function" } + ] +} \ No newline at end of file diff --git a/djed-sdk/src/artifacts/HebeSwapOracleABI.json b/djed-sdk/src/artifacts/HebeSwapOracleABI.json new file mode 100644 index 0000000..c4b0861 --- /dev/null +++ b/djed-sdk/src/artifacts/HebeSwapOracleABI.json @@ -0,0 +1 @@ +{"abi":[{"type":"constructor","inputs":[{"name":"_ref","type":"address","internalType":"contract IStdReference"},{"name":"_decimals","type":"uint8","internalType":"uint8"},{"name":"_hebeSwapDecimals","type":"uint8","internalType":"uint8"},{"name":"_baseToken","type":"string","internalType":"string"},{"name":"_quoteToken","type":"string","internalType":"string"}],"stateMutability":"nonpayable"},{"type":"function","name":"acceptTermsOfService","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"baseToken","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"quoteToken","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"readData","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"ref","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IStdReference"}],"stateMutability":"view"},{"type":"function","name":"scalingFactor","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}]} \ No newline at end of file diff --git a/djed-sdk/src/djed/djed.js b/djed-sdk/src/djed/djed.js index 443c313..b1c0746 100644 --- a/djed-sdk/src/djed/djed.js +++ b/djed-sdk/src/djed/djed.js @@ -1,13 +1,27 @@ import djedArtifact from "../artifacts/DjedABI.json"; +import djedIsisArtifact from "../artifacts/DjedIsisABI.json"; +import djedTefnutArtifact from "../artifacts/DjedTefnutABI.json"; import coinArtifact from "../artifacts/CoinABI.json"; import { convertInt, web3Promise } from "../helpers"; -//setting up djed +// Standard Djed (Osiris / Native) export const getDjedContract = (web3, DJED_ADDRESS) => { const djed = new web3.eth.Contract(djedArtifact.abi, DJED_ADDRESS); return djed; }; +// Djed Isis (ERC20 Backed) +export const getDjedIsisContract = (web3, DJED_ADDRESS) => { + const djed = new web3.eth.Contract(djedIsisArtifact.abi, DJED_ADDRESS); + return djed; +}; + +// Djed Tefnut +export const getDjedTefnutContract = (web3, DJED_ADDRESS) => { + const djed = new web3.eth.Contract(djedTefnutArtifact.abi, DJED_ADDRESS); + return djed; +}; + export const getCoinContracts = async (djedContract, web3) => { const [stableCoinAddress, reserveCoinAddress] = await Promise.all([ web3Promise(djedContract, "stableCoin"), @@ -20,6 +34,7 @@ export const getCoinContracts = async (djedContract, web3) => { ); return { stableCoin, reserveCoin }; }; + export const getDecimals = async (stableCoin, reserveCoin) => { const [scDecimals, rcDecimals] = await Promise.all([ convertInt(web3Promise(stableCoin, "decimals")), @@ -27,3 +42,13 @@ export const getDecimals = async (stableCoin, reserveCoin) => { ]); return { scDecimals, rcDecimals }; }; + +export const checkIfShu = async (djedContract) => { + try { + // Check if scMaxPrice exists on the contract + await djedContract.methods.scMaxPrice(0).call(); + return true; + } catch (e) { + return false; + } +}; diff --git a/djed-sdk/src/djed/index.js b/djed-sdk/src/djed/index.js index f9686a0..e64ded8 100644 --- a/djed-sdk/src/djed/index.js +++ b/djed-sdk/src/djed/index.js @@ -13,6 +13,20 @@ export { calculateFutureScPrice, } from "./stableCoin"; +export { + tradeDataPriceSellBoth, + sellBothTx, +} from "./sellBoth"; + +// New exports for Isis +export { + buyScIsisTx, + sellScIsisTx, + buyRcIsisTx, + sellRcIsisTx, + sellBothIsisTx +} from "./isis"; + export { scalingFactor, FEE_UI_UNSCALED, @@ -29,6 +43,13 @@ export { getFees, } from "./tradeUtils"; -export { getDjedContract, getCoinContracts, getDecimals } from "./djed"; +export { getDjedContract, getDjedIsisContract, getDjedTefnutContract, getCoinContracts, getDecimals } from "./djed"; export { getCoinDetails, getSystemParams, getAccountDetails } from "./system"; +export { subscribeToDjedEvents, getPastDjedEvents } from "./listeners"; + +// New exports for Token/Approvals +export { + approveTx, + checkAllowance, +} from "./token"; diff --git a/djed-sdk/src/djed/isis.js b/djed-sdk/src/djed/isis.js new file mode 100644 index 0000000..212fbdf --- /dev/null +++ b/djed-sdk/src/djed/isis.js @@ -0,0 +1,51 @@ +import { FEE_UI_UNSCALED } from "./tradeUtils"; +import { buildTx } from "../helpers"; + +// # ISIS / TEFNUT Transaction Functions (ERC20 Base Asset) + +/** + * Buy StableCoins (Isis/Tefnut Variant - ERC20 Base Asset) + * Note: Caller must APPROVE the Djed contract to spend `amountBC` of the Base Asset before calling this. + */ +export const buyScIsisTx = (djed, payer, receiver, amountBC, UI, DJED_ADDRESS) => { + // Signature: buyStableCoins(uint256 amountBC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .buyStableCoins(amountBC, receiver, FEE_UI_UNSCALED, UI) + .encodeABI(); + + // Value is 0 because Base Asset is ERC20 transferFrom, not msg.value + return buildTx(payer, DJED_ADDRESS, 0, data); +}; + +export const sellScIsisTx = (djed, account, amountSC, UI, DJED_ADDRESS) => { + // Signature: sellStableCoins(uint256 amountSC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .sellStableCoins(amountSC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); +}; + +export const buyRcIsisTx = (djed, payer, receiver, amountBC, UI, DJED_ADDRESS) => { + // Signature: buyReserveCoins(uint256 amountBC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .buyReserveCoins(amountBC, receiver, FEE_UI_UNSCALED, UI) + .encodeABI(); + + return buildTx(payer, DJED_ADDRESS, 0, data); + }; + +export const sellRcIsisTx = (djed, account, amountRC, UI, DJED_ADDRESS) => { + // Signature: sellReserveCoins(uint256 amountRC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .sellReserveCoins(amountRC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); +}; + +export const sellBothIsisTx = (djed, account, amountSC, amountRC, UI, DJED_ADDRESS) => { + // Signature: sellBothCoins(uint256 amountSC, uint256 amountRC, address receiver, uint256 feeUI, address ui) + const data = djed.methods + .sellBothCoins(amountSC, amountRC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); +}; \ No newline at end of file diff --git a/djed-sdk/src/djed/listeners.js b/djed-sdk/src/djed/listeners.js new file mode 100644 index 0000000..2b91974 --- /dev/null +++ b/djed-sdk/src/djed/listeners.js @@ -0,0 +1,66 @@ +/** + * Utility to listen for Djed contract events + * @param {Object} djedContract - The Web3 contract instance + * @param {Object} callbacks - Object containing callback functions for different events + */ +export const subscribeToDjedEvents = (djedContract, callbacks) => { + const events = [ + { name: "BoughtStableCoins", cb: callbacks.onBoughtStableCoins }, + { name: "SoldStableCoins", cb: callbacks.onSoldStableCoins }, + { name: "BoughtReserveCoins", cb: callbacks.onBoughtReserveCoins }, + { name: "SoldReserveCoins", cb: callbacks.onSoldReserveCoins }, + { name: "SoldBothCoins", cb: callbacks.onSoldBothCoins }, + ]; + + const subscriptions = []; + + events.forEach((event) => { + if (event.cb) { + const sub = djedContract.events[event.name]({ + fromBlock: "latest", + }) + .on("data", (data) => { + event.cb(data.returnValues); + }) + .on("error", (err) => { + if (callbacks.onError) callbacks.onError(err); + else console.error(`Error in ${event.name} subscription:`, err); + }); + subscriptions.push(sub); + } + }); + + return { + unsubscribe: () => { + subscriptions.forEach((sub) => { + if (sub.unsubscribe) sub.unsubscribe(); + }); + }, + }; +}; + +/** + * Utility to fetch past events from the Djed contract + * @param {Object} djedContract - The Web3 contract instance + * @param {string} eventName - Name of the event + * @param {Object} filter - Web3 filter object (e.g., { buyer: '0x...' }) + * @param {number|string} fromBlock - Starting block + * @returns {Promise} - Array of past events + */ +export const getPastDjedEvents = async ( + djedContract, + eventName, + filter = {}, + fromBlock = 0 +) => { + try { + return await djedContract.getPastEvents(eventName, { + filter, + fromBlock, + toBlock: "latest", + }); + } catch (error) { + console.error(`Error fetching past events for ${eventName}:`, error); + throw error; + } +}; \ No newline at end of file diff --git a/djed-sdk/src/djed/sellBoth.js b/djed-sdk/src/djed/sellBoth.js new file mode 100644 index 0000000..59fbe56 --- /dev/null +++ b/djed-sdk/src/djed/sellBoth.js @@ -0,0 +1,97 @@ +import { BC_DECIMALS } from "../constants"; +import { + decimalScaling, + decimalUnscaling, + buildTx, + web3Promise, + scaledUnscaledPromise, +} from "../helpers"; +import { + getFees, + convertToBC, + deductFees, + FEE_UI_UNSCALED, + getPriceMethod, +} from "./tradeUtils"; + +/** + * Function that calculates fees and how much BC (totalBCAmount) user will receive if he sells desired amount of stable coin and reserve coin + * @param {*} djed DjedContract + * @param {*} scDecimals Stable coin decimals + * @param {*} rcDecimals Reserve coin decimals + * @param {*} amountScScaled Stable coin amount that user wants to sell + * @param {*} amountRcScaled Reserve coin amount that user wants to sell + * @returns + */ +export const tradeDataPriceSellBoth = async ( + djed, + scDecimals, + rcDecimals, + amountScScaled, + amountRcScaled +) => { + try { + const scPriceMethod = await getPriceMethod(djed, 'sellSC'); + const [scPriceData, rcPriceData] = await Promise.all([ + scaledUnscaledPromise(web3Promise(djed, scPriceMethod, 0), BC_DECIMALS), + scaledUnscaledPromise(web3Promise(djed, "rcTargetPrice", 0), BC_DECIMALS), + ]); + + const amountScUnscaled = decimalUnscaling(amountScScaled, scDecimals); + const amountRcUnscaled = decimalUnscaling(amountRcScaled, rcDecimals); + + const scValueBC = convertToBC( + amountScUnscaled, + scPriceData[1], + scDecimals + ); + const rcValueBC = convertToBC( + amountRcUnscaled, + rcPriceData[1], + rcDecimals + ); + + const totalValueBC = (BigInt(scValueBC) + BigInt(rcValueBC)).toString(); + + const { treasuryFee, fee } = await getFees(djed); + const totalBCAmount = deductFees(totalValueBC, fee, treasuryFee); + + return { + scPriceScaled: scPriceData[0], + scPriceUnscaled: scPriceData[1], + rcPriceScaled: rcPriceData[0], + rcPriceUnscaled: rcPriceData[1], + amountScUnscaled, + amountRcUnscaled, + totalBCScaled: decimalScaling(totalBCAmount.toString(), BC_DECIMALS), + totalBCUnscaled: totalBCAmount.toString(), + }; + } catch (error) { + console.error("Error in tradeDataPriceSellBoth:", error); + throw error; + } +}; + +/** + * Function to sell both stablecoins and reservecoins + * @param {*} djed DjedContract + * @param {*} account User address + * @param {*} amountSC Unscaled amount of stablecoins to sell + * @param {*} amountRC Unscaled amount of reservecoins to sell + * @param {*} UI UI address for fee + * @param {*} DJED_ADDRESS Address of Djed contract + * @returns + */ +export const sellBothTx = ( + djed, + account, + amountSC, + amountRC, + UI, + DJED_ADDRESS +) => { + const data = djed.methods + .sellBothCoins(amountSC, amountRC, account, FEE_UI_UNSCALED, UI) + .encodeABI(); + return buildTx(account, DJED_ADDRESS, 0, data); +}; \ No newline at end of file diff --git a/djed-sdk/src/djed/token.js b/djed-sdk/src/djed/token.js new file mode 100644 index 0000000..060476e --- /dev/null +++ b/djed-sdk/src/djed/token.js @@ -0,0 +1,10 @@ +import { buildTx } from "../helpers"; + +export const approveTx = (tokenContract, owner, spender, amount) => { + const data = tokenContract.methods.approve(spender, amount).encodeABI(); + return buildTx(owner, tokenContract.options.address, 0, data); +}; + +export const checkAllowance = async (tokenContract, owner, spender) => { + return await tokenContract.methods.allowance(owner, spender).call(); +}; \ No newline at end of file diff --git a/djed-sdk/src/djed/tradeUtils.js b/djed-sdk/src/djed/tradeUtils.js index 456f1f1..6e00c1b 100644 --- a/djed-sdk/src/djed/tradeUtils.js +++ b/djed-sdk/src/djed/tradeUtils.js @@ -17,6 +17,7 @@ export const FEE_UI_UNSCALED = decimalUnscaling( (FEE_UI / 100).toString(), SCALING_DECIMALS ); + export const tradeDataPriceCore = (djed, method, decimals, amountScaled) => { const amountUnscaled = decimalUnscaling(amountScaled, decimals); return scaledUnscaledPromise(web3Promise(djed, method, 0), BC_DECIMALS).then( @@ -42,29 +43,11 @@ export const tradeDataPriceCore = (djed, method, decimals, amountScaled) => { ); }; -/** - * Function that converts coin amount to BC - * @param {*} amount unscaled coin amount to be converted to BC - * @param {*} price unscaled coin price - * @param {*} decimals coin decimals - * @returns unscaled BC amount - */ export const convertToBC = (amount, price, decimals) => { const decimalScalingFactor = BigInt(Math.pow(10, decimals)); return (BigInt(amount) * BigInt(price)) / decimalScalingFactor; }; -/** - * Calculate if the transaction will reach the maximum reserve ratio - * @param scPrice - Unscaled stablecoin price - * @param reserveBc - Unscaled reserve of base coin with appended potential transaction amount. - * Example: If user wants to buy 1RC, the reserveBc param will be calculated as sum of current reserve of BC and desired RC amount converted in BC - * @param totalScSupply - Unscaled total stablecoin supply - * @param reserveRatioMax - Unscaled maximum reserve ratio - * @param scDecimalScalingFactor - If stablecoin has 6 decimals, scDecimalScalingFactor will be calculated as 10^6 - * @param thresholdSupplySC - Unscaled threshold SC supply - * @returns - */ export const calculateIsRatioBelowMax = ({ scPrice, reserveBc, @@ -82,16 +65,6 @@ export const calculateIsRatioBelowMax = ({ ); }; -/** - * Calculate if the transaction will reach the minimum reserve ratio - * @param scPrice - Unscaled stablecoin price - * @param reserveBc - Unscaled reserve of base coin with calculated potential transaction amount. - * Example: If user wants to buy 1SC, the reserveBc param will be calculated as sum of current reserve of BC and desired SC amount converted in BC - * @param totalScSupply - Unscaled total stablecoin supply - * @param reserveRatioMin - Unscaled minimum reserve ratio - * @param scDecimalScalingFactor - If stablecoin has 6 decimals, scDecimalScalingFactor will be calculated as 10^6 - * @returns - */ export const calculateIsRatioAboveMin = ({ scPrice, reserveBc, @@ -107,15 +80,8 @@ export const calculateIsRatioAboveMin = ({ ); }; -/** - * - * @param {*} amountUSD - * @param {*} totalSCSupply - * @param {*} thresholdSCSupply - * @returns - */ -export const isTxLimitReached = (amountUSD, totalSCSupply, thresholdSCSupply) => - amountUSD > TRANSACTION_USD_LIMIT && +export const isTxLimitReached = (amountUSD, totalSCSupply, thresholdSCSupply, txLimit) => + (BigInt(amountUSD) > BigInt(txLimit || TRANSACTION_USD_LIMIT)) && BigInt(totalSCSupply) >= BigInt(thresholdSCSupply); export const promiseTx = (isWalletConnected, tx, signer) => { @@ -138,13 +104,6 @@ export const verifyTx = (web3, hash) => { }); }; -/** - * Function that deducts all platform fees from the BC amount - * @param {*} value The amount of BC from which fees should be deducted - * @param {*} fee The platform fee - * @param {*} treasuryFee The treasury fee - * @returns BC value with all fees calculated - */ export const calculateTxFees = (value, fee, treasuryFee, feeUI) => { const f = (BigInt(value) * BigInt(fee)) / BigInt(scalingFactor); const f_ui = @@ -154,26 +113,11 @@ export const calculateTxFees = (value, fee, treasuryFee, feeUI) => { return { f, f_ui, f_t }; }; -/** - * Function that deducts all platform fees from the BC amount - * @param {*} value The amount of BC from which fees should be deducted - * @param {*} fee The platform fee - * @param {*} treasuryFee The treasury fee - * @returns BC value with all fees calculated - */ export const deductFees = (value, fee, treasuryFee) => { const { f, f_ui, f_t } = calculateTxFees(value, fee, treasuryFee); return BigInt(value) - f - f_ui - f_t; }; -/** - * Function that appends all platform fees to the BC amount - * @param {*} amountBC The unscaled amount of BC (e.g. for 1BC, value should be 1 * 10^BC_DECIMALS) - * @param {*} treasuryFee Treasury fee unscaled (e.g. If the fee is 1%, than 1/100 * scalingFactor) - * @param {*} fee Fee unscaled (e.g. If the fee is 1%, than 1/100 * scalingFactor) - * @param {*} fee_UI UI fee unscaled (e.g. If the fee is 1%, than 1/100 * scalingFactor) - * @returns Unscaled BC amount with calculated fees - */ export const appendFees = (amountBC, treasuryFee, fee, fee_UI) => { const totalFees = BigInt(treasuryFee) + BigInt(fee) + BigInt(fee_UI); const substractedFees = BigInt(scalingFactor) - totalFees; @@ -183,11 +127,6 @@ export const appendFees = (amountBC, treasuryFee, fee, fee_UI) => { return appendedFeesAmount.toString(); }; -/** - * Function that returns treasury and platform fees - * @param {*} djed Djed contract - * @returns Treasury and platform fee - */ export const getFees = async (djed) => { try { const [treasuryFee, fee] = await Promise.all([ @@ -202,3 +141,20 @@ export const getFees = async (djed) => { console.log("error", error); } }; + +/** + * Added getPriceMethod export to fix Rollup Error + */ +export const getPriceMethod = async (djed, operation) => { + const isShu = await djed.methods.scMaxPrice(0).call().then(() => true).catch(() => false); + + if (!isShu) return "scPrice"; + + switch (operation) { + case 'buySC': return "scMaxPrice"; + case 'sellSC': return "scMinPrice"; + case 'buyRC': return "scMinPrice"; + case 'sellRC': return "scMaxPrice"; + default: return "scPrice"; + } +}; diff --git a/djed-sdk/src/oracle/index.js b/djed-sdk/src/oracle/index.js index a956ec5..eb96206 100644 --- a/djed-sdk/src/oracle/index.js +++ b/djed-sdk/src/oracle/index.js @@ -1 +1 @@ -export { getOracleAddress, getOracleContract } from "./oracle"; \ No newline at end of file +export {getOracleAddress,getOracleContract,getHebeSwapOracleContract,getChainlinkOracleContract,getAPI3OracleContract} from "./oracle"; \ No newline at end of file diff --git a/djed-sdk/src/oracle/oracle.js b/djed-sdk/src/oracle/oracle.js index 54644e0..aec3250 100644 --- a/djed-sdk/src/oracle/oracle.js +++ b/djed-sdk/src/oracle/oracle.js @@ -1,5 +1,8 @@ -import { convertInt,web3Promise } from "../helpers"; +import { convertInt, web3Promise } from "../helpers"; import oracleArtifact from "../artifacts/OracleABI.json"; +import hebeSwapOracleArtifact from "../artifacts/HebeSwapOracleABI.json"; +import chainlinkOracleArtifact from "../artifacts/ChainlinkOracleABI.json"; +import api3OracleArtifact from "../artifacts/API3OracleABI.json"; export const getOracleAddress = async (djedContract) => { return await web3Promise(djedContract, "oracle"); @@ -11,3 +14,33 @@ export const getOracleContract = (web3, oracleAddress, msgSender) => { }); return oracle; }; + +/** + * Added export to resolve Rollup error + */ +export const getHebeSwapOracleContract = (web3, oracleAddress, msgSender) => { + const oracle = new web3.eth.Contract(hebeSwapOracleArtifact.abi, oracleAddress, { + from: msgSender + }); + return oracle; +}; + +/** + * Added export to resolve Rollup error + */ +export const getChainlinkOracleContract = (web3, oracleAddress, msgSender) => { + const oracle = new web3.eth.Contract(chainlinkOracleArtifact.abi, oracleAddress, { + from: msgSender + }); + return oracle; +}; + +/** + * Added export to resolve Rollup error + */ +export const getAPI3OracleContract = (web3, oracleAddress, msgSender) => { + const oracle = new web3.eth.Contract(api3OracleArtifact.abi, oracleAddress, { + from: msgSender + }); + return oracle; +}; \ No newline at end of file diff --git a/stablepay-sdk/dist/esm/index.js b/stablepay-sdk/dist/esm/index.js index 907d446..430dfed 100644 --- a/stablepay-sdk/dist/esm/index.js +++ b/stablepay-sdk/dist/esm/index.js @@ -1 +1 @@ -import{getWeb3 as e,getDjedContract as t,getCoinContracts as n,getDecimals as r,getOracleAddress as a,getOracleContract as o,tradeDataPriceBuySc as i,buyScTx as s}from"djed-sdk";import c,{useContext as l,createContext as d,useState as m,useEffect as u,useRef as h,useCallback as w}from"react";import{defineChain as k,createWalletClient as g,custom as C,createPublicClient as p,http as b,parseEther as f,parseUnits as v,encodeFunctionData as E}from"viem";import{sepolia as y}from"viem/chains";const _={sepolia:{uri:"https://ethereum-sepolia.publicnode.com/",chainId:11155111,djedAddress:"0x624FcD0a1F9B5820c950FefD48087531d38387f4",tokens:{stablecoin:{symbol:"SOD",address:"0x6b930182787F346F18666D167e8d32166dC5eFBD",decimals:18,isDirectTransfer:!0},native:{symbol:"ETH",decimals:18,isNative:!0}},feeUI:0},"milkomeda-mainnet":{uri:"https://rpc-mainnet-cardano-evm.c1.milkomeda.com",chainId:2001,djedAddress:"0x67A30B399F5Ed499C1a6Bc0358FA6e42Ea4BCe76",tokens:{stablecoin:{symbol:"MOD",address:"0xcbA90fB1003b9D1bc6a2b66257D2585011b004e9",decimals:18,isDirectTransfer:!0},native:{symbol:"mADA",decimals:18,isNative:!0}},feeUI:0},"ethereum-classic":{uri:"https://etc.rivet.link",chainId:61,djedAddress:"0xCc3664d7021FD36B1Fe2b136e2324710c8442cCf",tokens:{stablecoin:{symbol:"ECSD",address:"0x5A7Ca94F6E969C94bef4CE5e2f90ed9d4891918A",decimals:18,isDirectTransfer:!0},native:{symbol:"ETC",decimals:18,isNative:!0}},feeUI:0}};class N{constructor(e,t){this.networkUri=e,this.djedAddress=t}async init(){if(!this.networkUri||!this.djedAddress)throw new Error("Network URI and DJED address are required");try{this.web3=await e(this.networkUri),this.djedContract=t(this.web3,this.djedAddress);try{const{stableCoin:e,reserveCoin:t}=await n(this.djedContract,this.web3),{scDecimals:i,rcDecimals:s}=await r(e,t);this.stableCoin=e,this.reserveCoin=t,this.scDecimals=i,this.rcDecimals=s,this.oracleContract=await a(this.djedContract).then((e=>o(this.web3,e,this.djedContract._address))),this.oracleAddress=this.oracleContract._address}catch(e){if(console.error("[Transaction] Error fetching contract details:",e),e.message&&e.message.includes("execution reverted")){const e=e=>e.includes("milkomeda")?{name:"Milkomeda",chainId:"2001"}:e.includes("mordor")?{name:"Mordor Testnet",chainId:"63"}:e.includes("sepolia")?{name:"Sepolia",chainId:"11155111"}:e.includes("etc.rivet.link")?{name:"Ethereum Classic",chainId:"61"}:{name:"the selected network",chainId:"unknown"},{name:t,chainId:n}=e(this.networkUri);throw new Error(`Failed to interact with Djed contract at ${this.djedAddress} on ${t}.\n\nPossible causes:\n- The contract address may be incorrect\n- The contract may not be deployed on ${t}\n- The contract may not be a valid Djed contract\n\nPlease verify the contract address is correct for ${t} (Chain ID: ${n}).`)}throw e}}catch(e){if(console.error("[Transaction] Error initializing transaction:",e),e.message&&(e.message.includes("CONNECTION ERROR")||e.message.includes("ERR_NAME_NOT_RESOLVED"))){const e=(e=>e.includes("milkomeda")?"Milkomeda":e.includes("mordor")?"Mordor":e.includes("sepolia")?"Sepolia":"the selected network")(this.networkUri);throw new Error(`Failed to connect to ${e} RPC endpoint: ${this.networkUri}\n\nPossible causes:\n- The RPC endpoint may be temporarily unavailable\n- DNS resolution issue (check your internet connection)\n- Network firewall blocking the connection\n\nPlease try again in a few moments or check the network status.`)}throw e}}getBlockchainDetails(){return{web3Available:!!this.web3,djedContractAvailable:!!this.djedContract,stableCoinAddress:this.stableCoin?this.stableCoin._address:"N/A",reserveCoinAddress:this.reserveCoin?this.reserveCoin._address:"N/A",stableCoinDecimals:this.scDecimals,reserveCoinDecimals:this.rcDecimals,oracleAddress:this.oracleAddress||"N/A",oracleContractAvailable:!!this.oracleContract}}async handleTradeDataBuySc(e){if(!this.djedContract)throw new Error("DJED contract is not initialized");if("string"!=typeof e)throw new Error("Amount must be a string");try{return(await i(this.djedContract,this.scDecimals,e)).totalBCScaled}catch(e){throw console.error("Error fetching trade data for buying stablecoins: ",e),e}}async buyStablecoins(e,t,n){if(!this.djedContract)throw new Error("DJED contract is not initialized");try{const r="0x0232556C83791b8291E9b23BfEa7d67405Bd9839";return await s(this.djedContract,e,t,n,r,this.djedAddress)}catch(e){throw console.error("Error executing buyStablecoins transaction: ",e),e}}}var T="main_stablePayButton__UA7HC",S="main_logo__ITyEy",A="main_buttonText__N-ewy";const D=({onClick:e,size:t="medium"})=>{const n={small:{width:"200px",height:"50px",fontSize:"14px"},medium:{width:"250px",height:"60px",fontSize:"16px"},large:{width:"300px",height:"70px",fontSize:"18px"}},r={small:{width:"35px",height:"33px"},medium:{width:"40px",height:"38px"},large:{width:"45px",height:"43px"}},a=n[t]||n.medium,o=r[t]||r.medium;return c.createElement("button",{className:T,onClick:e,style:a},c.createElement("div",{className:S,style:o}),c.createElement("span",{className:A},"Pay with StablePay"))};var x={dialogOverlay:"PricingCard_dialogOverlay__0XJrE",pricingCard:"PricingCard_pricingCard__LrWb9",small:"PricingCard_small__J4CHj",medium:"PricingCard_medium__EVmTB",large:"PricingCard_large__A6pnX",dialogClose:"PricingCard_dialogClose__jJ1tM",pricingCardHeader:"PricingCard_pricingCardHeader__wGczA",allianceLogo:"PricingCard_allianceLogo__URa-U",stablepayTitle:"PricingCard_stablepayTitle__4t848",pricingCardBody:"PricingCard_pricingCardBody__0wKQn",selectField:"PricingCard_selectField__LBPoZ",transactionReview:"PricingCard_transactionReview__Ix-eL",transactionInfo:"PricingCard_transactionInfo__Ck-Rc",transactionLabel:"PricingCard_transactionLabel__GDux7",transactionValue:"PricingCard_transactionValue__q-xxp",infoSection:"PricingCard_infoSection__gyjMQ",infoIcon:"PricingCard_infoIcon__rraxD",infoText:"PricingCard_infoText__l4b7A",walletButton:"PricingCard_walletButton__llw4v",loading:"PricingCard_loading__2-tGA",error:"PricingCard_error__m5fK-",networkError:"PricingCard_networkError__zR-36",errorText:"PricingCard_errorText__qZRJt","message-box":"PricingCard_message-box__vkUKy",detailsButton:"PricingCard_detailsButton__jHglL",errorDetails:"PricingCard_errorDetails__CzN-7",loadingContainer:"PricingCard_loadingContainer__6nOVa",spinner:"PricingCard_spinner__9ucQv",spin:"PricingCard_spin__24tni"};const P=({children:e,onClose:t,size:n="medium"})=>c.createElement("div",{className:x.dialogOverlay},c.createElement("div",{className:`${x.pricingCard} ${x[n]}`},c.createElement("button",{className:x.dialogClose,onClick:t},"×"),c.createElement("div",{className:x.pricingCardHeader},c.createElement("div",{className:x.allianceLogo}),c.createElement("h2",{className:x.stablepayTitle},"StablePay")),c.createElement("div",{className:x.pricingCardBody},e)));class I{constructor(e){this.networkSelector=e,this.selectedToken=null}selectToken(e){const t=this.networkSelector.getSelectedNetworkConfig();return!(!t||!t.tokens[e])&&(this.selectedToken={key:e,...t.tokens[e]},!0)}getSelectedToken(){return this.selectedToken}getAvailableTokens(){const e=this.networkSelector.getSelectedNetworkConfig();return e?Object.entries(e.tokens).map((([e,t])=>({key:e,...t}))):[]}resetSelection(){this.selectedToken=null}}const B=d(),M=({children:e,networkSelector:t})=>{const[n]=m((()=>new I(t))),[r,a]=m(null),[o,i]=m(null),[s,l]=m(null),d=()=>{i(null),l(null)};return u((()=>{a(t.selectedNetwork)}),[t.selectedNetwork]),c.createElement(B.Provider,{value:{networkSelector:t,tokenSelector:n,selectedNetwork:r,selectedToken:o,transactionDetails:s,setTransactionDetails:l,selectNetwork:e=>!!t.selectNetwork(e)&&(a(e),d(),!0),selectToken:e=>{if(n.selectToken(e)){const e=n.getSelectedToken();return i(e),!0}return!1},resetSelections:()=>{t.selectNetwork(null),a(null),d()}}},e)},$=()=>{const e=l(B);if(void 0===e)throw new Error("useNetwork must be used within a NetworkProvider");return e},j=()=>{const{networkSelector:e,selectedNetwork:t,selectNetwork:n}=$();return c.createElement("div",{className:x.selectField},c.createElement("label",{htmlFor:"network-select"},"Select Network"),c.createElement("select",{id:"network-select",onChange:e=>{n(e.target.value)},value:t||""},c.createElement("option",{value:"",disabled:!0},"Select a network"),Object.keys(e.availableNetworks).map((e=>c.createElement("option",{key:e,value:e},e)))))},U=()=>{const{networkSelector:e,tokenSelector:t,selectedNetwork:n,selectedToken:r,selectToken:a,setTransactionDetails:o}=$(),[i,s]=m(!1),[l,d]=m(null),u=n?t.getAvailableTokens():[];return c.createElement("div",{className:x.selectField},c.createElement("label",{htmlFor:"token-select"},"Select Token"),c.createElement("select",{id:"token-select",onChange:async r=>{const i=r.target.value;d(null),s(!0);try{if(a(i)){const r=e.getSelectedNetworkConfig(),a=new N(r.uri,r.djedAddress);await a.init();const s=e.getTokenAmount(i),c=a.getBlockchainDetails();let l=null;"native"===i&&(l=await a.handleTradeDataBuySc(String(s))),o({network:n,token:i,tokenSymbol:t.getSelectedToken().symbol,amount:s,receivingAddress:e.getReceivingAddress(),djedContractAddress:r.djedAddress,isDirectTransfer:t.getSelectedToken().isDirectTransfer||!1,isNativeToken:t.getSelectedToken().isNative||!1,tradeAmount:l?l.amount:null,...c})}}catch(e){console.error("Error fetching transaction details:",e),d("Failed to fetch transaction details. Please try again.")}finally{s(!1)}},value:r?r.key:"",disabled:!n||i},c.createElement("option",{value:"",disabled:!0},n?i?"Loading...":"Select a token":"Please select a network first"),u.map((e=>c.createElement("option",{key:e.key,value:e.key},e.symbol," (",e.isDirectTransfer?"Direct Transfer":"Native",")")))),l&&c.createElement("div",{className:x.error},l))};k({id:63,name:"Mordor Testnet",network:"mordor",nativeCurrency:{decimals:18,name:"Mordor Ether",symbol:"METC"},rpcUrls:{default:{http:["https://rpc.mordor.etccooperative.org"],webSocket:["wss://rpc.mordor.etccooperative.org/ws"]}},blockExplorers:{default:{name:"BlockScout",url:"https://blockscout.com/etc/mordor"}},testnet:!0});const F=k({id:2001,name:"Milkomeda C1 Mainnet",network:"milkomeda",nativeCurrency:{decimals:18,name:"Milkomeda ADA",symbol:"mADA"},rpcUrls:{default:{http:["https://rpc-mainnet-cardano-evm.c1.milkomeda.com"]}},blockExplorers:{default:{name:"Milkomeda Explorer",url:"https://explorer-mainnet-cardano-evm.c1.milkomeda.com"}},testnet:!1}),R=k({id:61,name:"Ethereum Classic",network:"etc",nativeCurrency:{decimals:18,name:"Ethereum Classic",symbol:"ETC"},rpcUrls:{default:{http:["https://etc.rivet.link"]}},blockExplorers:{default:{name:"Blockscout",url:"https://blockscout.com/etc/mainnet"}},testnet:!1}),L=d(null),z=async e=>{if(!window.ethereum)throw new Error("MetaMask not installed");const t=(e=>{switch(e){case"sepolia":return{chainId:`0x${y.id.toString(16)}`,chainName:"Sepolia",nativeCurrency:{name:"Ether",symbol:"ETH",decimals:18},rpcUrls:y.rpcUrls.default.http,blockExplorerUrls:y.blockExplorers?.default?.url?[y.blockExplorers.default.url]:[]};case"ethereum-classic":return{chainId:`0x${R.id.toString(16)}`,chainName:"Ethereum Classic",nativeCurrency:{name:"Ethereum Classic",symbol:"ETC",decimals:18},rpcUrls:["https://etc.rivet.link"],blockExplorerUrls:["https://blockscout.com/etc/mainnet"]};case"milkomeda-mainnet":return{chainId:`0x${F.id.toString(16)}`,chainName:"Milkomeda C1 Mainnet",nativeCurrency:{name:"Milkomeda ADA",symbol:"mADA",decimals:18},rpcUrls:["https://rpc-mainnet-cardano-evm.c1.milkomeda.com"],blockExplorerUrls:["https://explorer-mainnet-cardano-evm.c1.milkomeda.com"]};default:return null}})(e);if(!t)throw new Error(`Unsupported network: ${e}`);try{await window.ethereum.request({method:"wallet_switchEthereumChain",params:[{chainId:t.chainId}]})}catch(e){if(4902!==e.code)throw 4001===e.code?new Error(`User rejected switching to ${t.chainName}. Please switch manually in MetaMask.`):new Error(`Failed to switch to ${t.chainName}: ${e.message}`);try{await window.ethereum.request({method:"wallet_addEthereumChain",params:[t]})}catch(e){if(4001===e.code)throw new Error(`User rejected adding ${t.chainName} to MetaMask. Please add it manually.`);throw new Error(`Failed to add ${t.chainName} to MetaMask: ${e.message}`)}}},W=({children:e})=>{const{selectedNetwork:t}=$(),[n,r]=m(null),[a,o]=m(null),[i,s]=m(null),[l,d]=m(null),[k,f]=m(null),[v,E]=m(null),[_,N]=m(!1),T=t?(e=>{switch(e){case"sepolia":return y;case"ethereum-classic":return R;case"milkomeda-mainnet":return F;default:return null}})(t):null,S=T?T.id:null,A=h(null),D=h(null),x=w((()=>{r(null),o(null),s(null),d(null),f(null),E(null)}),[]),P=w((async e=>{const n=parseInt(e,16);if(d(n),T&&n===S){if(E(null),window.ethereum&&T){const e=g({chain:T,transport:C(window.ethereum)});r(e)}}else if(T&&n!==S){E(`Wrong network detected. Please switch to ${T?.name||t||"selected network"}`)}}),[T,S,t]);D.current=P;const I=w((async e=>{if(0===e.length){if(x(),window.ethereum){const e=A.current,t=D.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}}else if(s(e[0]),T)try{const t=p({chain:T,transport:b()});o(t);const n=await t.getBalance({address:e[0]});f(parseFloat(n)/Math.pow(10,18))}catch(e){console.error("Error fetching balance:",e),f(null)}}),[T,x]);A.current=I;const B=w((()=>{if(x(),window.ethereum){const e=A.current,t=D.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}}),[x]),M=w((()=>{T&&o(p({chain:T,transport:b()}))}),[T]),j=w((async()=>{if(!window.ethereum)return E("Please install MetaMask or another Web3 wallet"),!1;if(!t||!T)return E("Please select a network first"),!1;N(!0),E(null);try{const e=await window.ethereum.request({method:"eth_requestAccounts"});if(0===e.length)throw new Error("No wallet address found. Please unlock your wallet.");const n=await window.ethereum.request({method:"eth_chainId"});parseInt(n,16)!==S&&await z(t);const a=g({chain:T,transport:C(window.ethereum)});r(a),s(e[0]),d(S);const i=p({chain:T,transport:b()});o(i);try{const t=await i.getBalance({address:e[0]});f(parseFloat(t)/Math.pow(10,18))}catch(e){console.error("Error fetching balance:",e),f(null)}return A.current=I,D.current=P,window.ethereum.on("accountsChanged",I),window.ethereum.on("chainChanged",P),!0}catch(e){return console.error("Error connecting wallet:",e),E(e.message),!1}finally{N(!1)}}),[t,T,S,I,P]),U=w((async()=>{if(!(window.ethereum&&t&&T&&i)){return E("Wallet not connected or network not selected"),null}try{const e=await window.ethereum.request({method:"eth_chainId"});if(parseInt(e,16)!==S){E(null),await z(t);const e=500;await new Promise((t=>setTimeout(t,e)));const n=await window.ethereum.request({method:"eth_chainId"}),r=parseInt(n,16);if(r!==S)throw new Error(`Failed to switch network. MetaMask is still on chain ${r}, expected ${S}`)}const n=g({chain:T,transport:C(window.ethereum)});return r(n),d(S),E(null),n}catch(e){return E(e.message),null}}),[t,T,S,i]),W=h(t);return u((()=>{if(null!==W.current&&W.current!==t&&i&&(x(),window.ethereum)){const e=A.current,t=D.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}W.current=t}),[t,i,x]),u((()=>{M()}),[M]),c.createElement(L.Provider,{value:{walletClient:n,publicClient:a,account:i,chainId:l,balance:k,error:v,isConnecting:_,connectWallet:j,disconnectWallet:B,ensureCorrectNetwork:U,expectedChainId:S}},e)},O=({onTransactionComplete:e})=>{const{networkSelector:t,selectedNetwork:n,selectedToken:r,transactionDetails:a,setTransactionDetails:o}=$(),{connectWallet:i,account:s,walletClient:d,publicClient:h,isConnecting:w,ensureCorrectNetwork:k,expectedChainId:g}=(()=>{const e=l(L);if(!e)throw new Error("useWallet must be used within a WalletProvider");return e})(),[C,p]=m(null),[b,y]=m(null),[_,T]=m(null),[S,A]=m(""),[D,P]=m(null),[I,B]=m(null),[M,j]=m(!1);if(u((()=>{T(null),y(null),A(""),B(null),P(null)}),[n,r]),u((()=>{(async()=>{if(n&&r)try{const e=t.getSelectedNetworkConfig(),a=t.getReceivingAddress(),i=t.getTokenAmount(r.key),s=new N(e.uri,e.djedAddress);await s.init(),p(s);let c=null;if("native"===r.key)try{c=await s.handleTradeDataBuySc(String(i)),y(c)}catch(e){console.error("Error fetching trade data:",e)}o({network:n,token:r.key,tokenSymbol:r.symbol,amount:i||"0",receivingAddress:a,djedContractAddress:e.djedAddress,isDirectTransfer:r.isDirectTransfer||!1,isNativeToken:r.isNative||!1,tradeAmount:c?c.amount:null,...s.getBlockchainDetails()})}catch(e){console.error("Error initializing transaction:",e)}})()}),[n,r,t,o]),!a)return c.createElement("div",{className:x.loading},"Initializing transaction...");const U=()=>{if(!D||!n)return null;const e={"ethereum-classic":"https://blockscout.com/etc/mainnet/tx/",sepolia:"https://sepolia.etherscan.io/tx/","milkomeda-mainnet":"https://explorer-mainnet-cardano-evm.c1.milkomeda.com/tx/"};return e[n]?`${e[n]}${D}`:null};return c.createElement("div",{className:x.transactionReview},c.createElement("div",{className:x.transactionInfo},c.createElement("span",{className:x.transactionLabel},"Network:"),c.createElement("span",{className:x.transactionValue},a.network)),c.createElement("div",{className:x.transactionInfo},c.createElement("span",{className:x.transactionLabel},"You Pay:"),c.createElement("span",{className:x.transactionValue},"stablecoin"===r.key?`${a.amount} ${a.tokenSymbol}`:`${b||"Calculating..."} ${a.tokenSymbol}`)),c.createElement("button",{className:x.walletButton,onClick:async()=>{await i()},disabled:w},w?"Connecting...":"Connect Wallet"),s&&!_&&c.createElement("button",{className:x.walletButton,onClick:async()=>{if(s&&a&&C)try{T(null),B(null),A("⏳ Preparing transaction...");const e=a.receivingAddress;let n;if("native"===r.key){const t="0x0232556C83791b8291E9b23BfEa7d67405Bd9839",r=f(String(b||"0"));n=await C.buyStablecoins(s,e,r,t),n={...n,value:r,account:s}}else{const r=t.getSelectedNetworkConfig(),o=r?.tokens?.stablecoin?.address;if(!o)throw new Error("Stablecoin address not found in network configuration");const i=a.amount?v(String(a.amount),a.stableCoinDecimals):"0";n={to:o,value:0n,data:E({abi:[{inputs:[{internalType:"address",name:"to",type:"address"},{internalType:"uint256",name:"amount",type:"uint256"}],name:"transfer",outputs:[{internalType:"bool",name:"",type:"bool"}],stateMutability:"nonpayable",type:"function"}],functionName:"transfer",args:[e,i]}),account:s}}T(n),A("✅ Transaction ready! Click 'Send Transaction' to proceed.")}catch(e){B(e),A("❌ Transaction preparation failed.")}else A("❌ Wallet not connected or transaction details missing")}},"Prepare Transaction"),s&&_&&c.createElement("button",{className:x.walletButton,onClick:async()=>{B(null);try{if(!s||!_)return void A("❌ Wallet account or transaction data is missing");if(!n)return void A("❌ Network not selected");const o=t.getSelectedNetworkConfig();if(!o)return void A("❌ Network configuration not found");A("⏳ Verifying network...");const i=await k();if(!i)return void A("❌ Failed to switch to correct network. Please approve the network switch in MetaMask and try again.");if(!window.ethereum)return void A("❌ MetaMask not available");const c=await window.ethereum.request({method:"eth_chainId"}),l=parseInt(c,16);if(l!==o.chainId){const e=`Network mismatch. MetaMask is on chain ${l}, but ${n} requires chain ${o.chainId}. Please switch networks in MetaMask.`;return A(`❌ ${e}`),void B(new Error(e))}if(i.chain.id!==o.chainId){const e=`Wallet client chain mismatch. Wallet client is on chain ${i.chain.id}, but expected ${o.chainId}.`;return A(`❌ ${e}`),void B(new Error(e))}A("⏳ Sending transaction...");const d=await i.sendTransaction({..._,account:s});P(d),A("✅ Transaction sent!"),e&&e({txHash:d,network:n,token:r?.key,tokenSymbol:r?.symbol,amount:a?.amount,receivingAddress:a?.receivingAddress})}catch(e){B(e),A("❌ Transaction failed."),console.error("Transaction error:",e)}},disabled:null!==D},"Send Transaction"),S&&c.createElement("div",{className:"message-box"},S,I&&c.createElement("button",{onClick:()=>j(!M),className:x.detailsButton},M?"Hide Details":"Show Details")),M&&I&&c.createElement("div",{className:x.errorDetails},c.createElement("pre",null,I.message)),D&&c.createElement("div",{className:x.transactionLink},"✅ Transaction Hash:"," ",U()?c.createElement("a",{href:U(),target:"_blank",rel:"noopener noreferrer",className:x.explorerLink,style:{color:"#007bff",textDecoration:"underline",fontWeight:"bold",cursor:"pointer",wordBreak:"break-word"}},D.slice(0,6),"...",D.slice(-6)):c.createElement("span",{style:{wordBreak:"break-word"}},D)))},q=({onClose:e,buttonSize:t,onTransactionComplete:n})=>{const{resetSelections:r}=$();return c.createElement(P,{onClose:()=>{r(),e()},size:t},c.createElement(j,null),c.createElement(U,null),c.createElement(O,{onTransactionComplete:n}))},H=({onClose:e,buttonSize:t,networkSelector:n,onTransactionComplete:r})=>c.createElement(M,{networkSelector:n},c.createElement(W,null,c.createElement(q,{onClose:e,buttonSize:t,onTransactionComplete:r}))),V={NetworkSelector:class{constructor(e){this.merchantConfig=e,this.blacklist=e.getBlacklist(),this.availableNetworks=this.getAvailableNetworks(),this.selectedNetwork=null}getAvailableNetworks(){return Object.entries(_).reduce(((e,[t,n])=>(this.blacklist.includes(n.chainId)||(e[t]=n),e)),{})}selectNetwork(e){return null===e?(this.selectedNetwork=null,console.log("Network selection reset"),!0):this.availableNetworks[e]?(this.selectedNetwork=e,console.log(`Network selected: ${e}`),!0):(console.error(`Invalid network: ${e}`),!1)}getSelectedNetworkConfig(){return this.selectedNetwork?this.availableNetworks[this.selectedNetwork]:null}getReceivingAddress(){return this.merchantConfig.getReceivingAddress()}getTokenAmount(e){return this.merchantConfig.getTokenAmount(this.selectedNetwork,e)}},Transaction:N,Config:class{constructor(e={}){this.receivingAddress=e.receivingAddress||"",this.blacklist=e.blacklist||[],this.amounts=e.Amounts||{},this.validateConfig()}validateConfig(){if(!this.receivingAddress)throw new Error("Receiving address is required");for(const[e,t]of Object.entries(this.amounts)){if(!_[e])throw new Error(`Invalid network: ${e}`);if(!t.stablecoin||"number"!=typeof t.stablecoin||t.stablecoin<=0)throw new Error(`Invalid stablecoin amount for network ${e}`)}}getBlacklist(){return this.blacklist}getReceivingAddress(){return this.receivingAddress}getTokenAmount(e){console.log("Getting amount for network:",e),console.log("Amounts object:",this.amounts);const t=this.amounts[e]?.stablecoin;return console.log("Returning amount:",t),t||0}},Widget:({networkSelector:e,buttonSize:t="medium",onTransactionComplete:n,onSuccess:r})=>{const[a,o]=m(!1),i=n||r;return c.createElement("div",{className:x.widgetContainer},!a&&c.createElement(D,{onClick:()=>{o(!0)},size:t}),a&&c.createElement(H,{onClose:()=>{o(!1)},buttonSize:t,networkSelector:e,onTransactionComplete:i}))},PayButton:D,Dialog:P,NetworkDropdown:j};export{V as default}; +import{getWeb3 as e,getDjedContract as t,getCoinContracts as n,getDecimals as a,getOracleAddress as r,getOracleContract as s,tradeDataPriceBuySc as o,buyScIsisTx as i,buyScTx as c,approveTx as l,checkAllowance as d}from"djed-sdk";import m from"djed-sdk/src/artifacts/CoinABI.json";import u,{useContext as h,createContext as w,useState as b,useEffect as g,useRef as k,useCallback as C}from"react";import{defineChain as p,createWalletClient as A,custom as v,createPublicClient as f,http as E,parseUnits as y}from"viem";import{sepolia as _}from"viem/chains";const N={sepolia:{uri:"https://ethereum-sepolia.publicnode.com/",chainId:11155111,stablecoins:[{id:"djed-eth-sepolia",name:"Djed (ETH Backed)",protocol:"djed",contractAddress:"0x624FcD0a1F9B5820c950FefD48087531d38387f4",baseAsset:{symbol:"ETH",decimals:18,isNative:!0,address:null},stableCoin:{symbol:"SOD",address:"0x6b930182787F346F18666D167e8d32166dC5eFBD",decimals:18,isDirectTransfer:!0}}],feeUI:0},"milkomeda-mainnet":{uri:"https://rpc-mainnet-cardano-evm.c1.milkomeda.com",chainId:2001,stablecoins:[{id:"djed-mada",name:"Djed (mADA Backed)",protocol:"djed",contractAddress:"0x67A30B399F5Ed499C1a6Bc0358FA6e42Ea4BCe76",baseAsset:{symbol:"mADA",decimals:18,isNative:!0,address:null},stableCoin:{symbol:"MOD",address:"0xcbA90fB1003b9D1bc6a2b66257D2585011b004e9",decimals:18,isDirectTransfer:!0}},{id:"djed-usdt-isis",name:"Djed (USDT Backed)",protocol:"isis",contractAddress:"0x0000000000000000000000000000000000000000",baseAsset:{symbol:"USDT",decimals:6,isNative:!1,address:"0x0000000000000000000000000000000000000000"},stableCoin:{symbol:"iUSD",address:"0x0000000000000000000000000000000000000000",decimals:18,isDirectTransfer:!0}}],feeUI:0},"ethereum-classic":{uri:"https://etc.rivet.link",chainId:61,stablecoins:[{id:"djed-etc",name:"Djed (ETC Backed)",protocol:"djed",contractAddress:"0xCc3664d7021FD36B1Fe2b136e2324710c8442cCf",baseAsset:{symbol:"ETC",decimals:18,isNative:!0,address:null},stableCoin:{symbol:"ECSD",address:"0x5A7Ca94F6E969C94bef4CE5e2f90ed9d4891918A",decimals:18,isDirectTransfer:!0}}],feeUI:0}};class T{constructor(e,t){this.networkUri=e,this.config=t,this.djedAddress=t.contractAddress,this.baseAsset=t.baseAsset}async init(){if(!this.networkUri||!this.djedAddress)throw new Error("Network URI and DJED address are required");try{this.web3=await e(this.networkUri),this.djedContract=t(this.web3,this.djedAddress),!this.baseAsset.isNative&&this.baseAsset.address&&(this.baseAssetContract=new this.web3.eth.Contract(m.abi,this.baseAsset.address));try{const{stableCoin:e,reserveCoin:t}=await n(this.djedContract,this.web3),{scDecimals:o,rcDecimals:i}=await a(e,t);this.stableCoin=e,this.reserveCoin=t,this.scDecimals=o,this.rcDecimals=i,this.oracleContract=await r(this.djedContract).then((e=>s(this.web3,e,this.djedContract._address))),this.oracleAddress=this.oracleContract._address}catch(e){throw console.error("[Transaction] Error fetching contract details:",e),e}}catch(e){throw console.error("[Transaction] Error initializing transaction:",e),e}}getBlockchainDetails(){return{web3Available:!!this.web3,djedContractAvailable:!!this.djedContract,stableCoinAddress:this.stableCoin?this.stableCoin._address:"N/A",reserveCoinAddress:this.reserveCoin?this.reserveCoin._address:"N/A",stableCoinDecimals:this.scDecimals,reserveCoinDecimals:this.rcDecimals,oracleAddress:this.oracleAddress||"N/A",oracleContractAvailable:!!this.oracleContract,baseAssetSymbol:this.baseAsset.symbol,baseAssetIsNative:this.baseAsset.isNative,baseAssetDecimals:this.baseAsset.decimals}}async handleTradeDataBuySc(e){if(!this.djedContract)throw new Error("DJED contract is not initialized");try{return(await o(this.djedContract,this.scDecimals,e)).totalBCScaled}catch(e){throw console.error("Error fetching trade data for buying stablecoins: ",e),e}}async buyStablecoins(e,t,n){if(!this.djedContract)throw new Error("DJED contract is not initialized");try{const a="0x0232556C83791b8291E9b23BfEa7d67405Bd9839";if(this.baseAsset.isNative)return c(this.djedContract,e,t,n,a,this.djedAddress);if(!this.baseAssetContract)throw new Error("Base Asset contract not initialized for ERC20 flow");return i(this.djedContract,e,t,n,a,this.djedAddress)}catch(e){throw console.error("Error executing buyStablecoins transaction: ",e),e}}async approveBaseAsset(e,t){if(this.baseAsset.isNative)throw new Error("Cannot approve native asset");if(!this.baseAssetContract)throw new Error("No Base Asset contract to approve");return l(this.baseAssetContract,e,this.djedAddress,t)}async checkBaseAssetAllowance(e,t){if(this.baseAsset.isNative)return!0;if(!this.baseAssetContract)return!1;const n=await d(this.baseAssetContract,e,this.djedAddress);return BigInt(n)>=BigInt(t)}}var S="main_stablePayButton__UA7HC",D="main_logo__ITyEy",x="main_buttonText__N-ewy";const P=({onClick:e,size:t="medium"})=>{const n={small:{width:"200px",height:"50px",fontSize:"14px"},medium:{width:"250px",height:"60px",fontSize:"16px"},large:{width:"300px",height:"70px",fontSize:"18px"}},a={small:{width:"35px",height:"33px"},medium:{width:"40px",height:"38px"},large:{width:"45px",height:"43px"}},r=n[t]||n.medium,s=a[t]||a.medium;return u.createElement("button",{className:S,onClick:e,style:r},u.createElement("div",{className:D,style:s}),u.createElement("span",{className:x},"Pay with StablePay"))};var B={dialogOverlay:"PricingCard_dialogOverlay__0XJrE",pricingCard:"PricingCard_pricingCard__LrWb9",small:"PricingCard_small__J4CHj",medium:"PricingCard_medium__EVmTB",large:"PricingCard_large__A6pnX",dialogClose:"PricingCard_dialogClose__jJ1tM",pricingCardHeader:"PricingCard_pricingCardHeader__wGczA",allianceLogo:"PricingCard_allianceLogo__URa-U",stablepayTitle:"PricingCard_stablepayTitle__4t848",pricingCardBody:"PricingCard_pricingCardBody__0wKQn",selectField:"PricingCard_selectField__LBPoZ",transactionReview:"PricingCard_transactionReview__Ix-eL",transactionInfo:"PricingCard_transactionInfo__Ck-Rc",transactionLabel:"PricingCard_transactionLabel__GDux7",transactionValue:"PricingCard_transactionValue__q-xxp",infoSection:"PricingCard_infoSection__gyjMQ",infoIcon:"PricingCard_infoIcon__rraxD",infoText:"PricingCard_infoText__l4b7A",walletButton:"PricingCard_walletButton__llw4v",loading:"PricingCard_loading__2-tGA",error:"PricingCard_error__m5fK-",networkError:"PricingCard_networkError__zR-36",errorText:"PricingCard_errorText__qZRJt","message-box":"PricingCard_message-box__vkUKy",detailsButton:"PricingCard_detailsButton__jHglL",errorDetails:"PricingCard_errorDetails__CzN-7",loadingContainer:"PricingCard_loadingContainer__6nOVa",spinner:"PricingCard_spinner__9ucQv",spin:"PricingCard_spin__24tni"};const j=({children:e,onClose:t,size:n="medium"})=>u.createElement("div",{className:B.dialogOverlay},u.createElement("div",{className:`${B.pricingCard} ${B[n]}`},u.createElement("button",{className:B.dialogClose,onClick:t},"×"),u.createElement("div",{className:B.pricingCardHeader},u.createElement("div",{className:B.allianceLogo}),u.createElement("h2",{className:B.stablepayTitle},"StablePay")),u.createElement("div",{className:B.pricingCardBody},e)));class I{constructor(e){this.networkSelector=e,this.selectedToken=null}selectToken(e){const t=this.getAvailableTokens().find((t=>t.key===e));return!!t&&(this.selectedToken=t,!0)}getSelectedToken(){return this.selectedToken}getAvailableTokens(){const e=this.networkSelector.getSelectedNetworkConfig();return e&&e.stablecoins?e.stablecoins.map((e=>({key:e.id,name:e.name,symbol:e.stableCoin.symbol,baseAsset:e.baseAsset.symbol,isDirectTransfer:e.stableCoin.isDirectTransfer,config:e}))):[]}resetSelection(){this.selectedToken=null}}const M=w(),U=({children:e,networkSelector:t})=>{const[n]=b((()=>new I(t))),[a,r]=b(null),[s,o]=b(null),[i,c]=b(null),l=()=>{o(null),c(null)};return g((()=>{r(t.selectedNetwork)}),[t.selectedNetwork]),u.createElement(M.Provider,{value:{networkSelector:t,tokenSelector:n,selectedNetwork:a,selectedToken:s,transactionDetails:i,setTransactionDetails:c,selectNetwork:e=>!!t.selectNetwork(e)&&(r(e),l(),!0),selectToken:e=>{if(n.selectToken(e)){const e=n.getSelectedToken();return o(e),!0}return!1},resetSelections:()=>{t.selectNetwork(null),r(null),l()}}},e)},F=()=>{const e=h(M);if(void 0===e)throw new Error("useNetwork must be used within a NetworkProvider");return e},$=()=>{const{networkSelector:e,selectedNetwork:t,selectNetwork:n}=F();return u.createElement("div",{className:B.selectField},u.createElement("label",{htmlFor:"network-select"},"Select Network"),u.createElement("select",{id:"network-select",onChange:e=>{n(e.target.value)},value:t||""},u.createElement("option",{value:"",disabled:!0},"Select a network"),Object.keys(e.availableNetworks).map((e=>u.createElement("option",{key:e,value:e},e)))))},z=()=>{const{networkSelector:e,tokenSelector:t,selectedNetwork:n,selectedToken:a,selectToken:r,setTransactionDetails:s}=F(),[o,i]=b(!1),[c,l]=b(null),d=n?t.getAvailableTokens():[];return u.createElement("div",{className:B.selectField},u.createElement("label",{htmlFor:"token-select"},"Select Token"),u.createElement("select",{id:"token-select",onChange:async t=>{const a=t.target.value;l(null),i(!0);try{if(r(a)){const t=e.getSelectedNetworkConfig(),r=t.stablecoins.find((e=>e.id===a));if(!r)throw new Error("Stablecoin configuration not found");const o=new T(t.uri,r);await o.init();const i=e.getTokenAmount(a),c=o.getBlockchainDetails();s({network:n,token:a,tokenSymbol:r.stableCoin.symbol,amount:i,receivingAddress:e.getReceivingAddress(),djedContractAddress:r.contractAddress,isDirectTransfer:r.stableCoin.isDirectTransfer||!1,baseAssetSymbol:r.baseAsset.symbol,baseAssetDecimals:r.baseAsset.decimals,baseAssetIsNative:r.baseAsset.isNative,...c})}}catch(e){console.error("Error updating transaction details:",e),l("Failed to initialize transaction.")}finally{i(!1)}},value:a?a.key:"",disabled:!n||o},u.createElement("option",{value:"",disabled:!0},n?o?"Loading...":"Select a token":"Please select a network first"),d.map((e=>u.createElement("option",{key:e.key,value:e.key},e.symbol," (",e.isDirectTransfer?"Direct Transfer":"Native",")")))),c&&u.createElement("div",{className:B.error},c))};p({id:63,name:"Mordor Testnet",network:"mordor",nativeCurrency:{decimals:18,name:"Mordor Ether",symbol:"METC"},rpcUrls:{default:{http:["https://rpc.mordor.etccooperative.org"],webSocket:["wss://rpc.mordor.etccooperative.org/ws"]}},blockExplorers:{default:{name:"BlockScout",url:"https://blockscout.com/etc/mordor"}},testnet:!0});const L=p({id:2001,name:"Milkomeda C1 Mainnet",network:"milkomeda",nativeCurrency:{decimals:18,name:"Milkomeda ADA",symbol:"mADA"},rpcUrls:{default:{http:["https://rpc-mainnet-cardano-evm.c1.milkomeda.com"]}},blockExplorers:{default:{name:"Milkomeda Explorer",url:"https://explorer-mainnet-cardano-evm.c1.milkomeda.com"}},testnet:!1}),R=p({id:61,name:"Ethereum Classic",network:"etc",nativeCurrency:{decimals:18,name:"Ethereum Classic",symbol:"ETC"},rpcUrls:{default:{http:["https://etc.rivet.link"]}},blockExplorers:{default:{name:"Blockscout",url:"https://blockscout.com/etc/mainnet"}},testnet:!1}),W=w(null),q=async e=>{if(!window.ethereum)throw new Error("MetaMask not installed");const t=(e=>{switch(e){case"sepolia":return{chainId:`0x${_.id.toString(16)}`,chainName:"Sepolia",nativeCurrency:{name:"Ether",symbol:"ETH",decimals:18},rpcUrls:_.rpcUrls.default.http,blockExplorerUrls:_.blockExplorers?.default?.url?[_.blockExplorers.default.url]:[]};case"ethereum-classic":return{chainId:`0x${R.id.toString(16)}`,chainName:"Ethereum Classic",nativeCurrency:{name:"Ethereum Classic",symbol:"ETC",decimals:18},rpcUrls:["https://etc.rivet.link"],blockExplorerUrls:["https://blockscout.com/etc/mainnet"]};case"milkomeda-mainnet":return{chainId:`0x${L.id.toString(16)}`,chainName:"Milkomeda C1 Mainnet",nativeCurrency:{name:"Milkomeda ADA",symbol:"mADA",decimals:18},rpcUrls:["https://rpc-mainnet-cardano-evm.c1.milkomeda.com"],blockExplorerUrls:["https://explorer-mainnet-cardano-evm.c1.milkomeda.com"]};default:return null}})(e);if(!t)throw new Error(`Unsupported network: ${e}`);try{await window.ethereum.request({method:"wallet_switchEthereumChain",params:[{chainId:t.chainId}]})}catch(e){if(4902!==e.code)throw 4001===e.code?new Error(`User rejected switching to ${t.chainName}. Please switch manually in MetaMask.`):new Error(`Failed to switch to ${t.chainName}: ${e.message}`);try{await window.ethereum.request({method:"wallet_addEthereumChain",params:[t]})}catch(e){if(4001===e.code)throw new Error(`User rejected adding ${t.chainName} to MetaMask. Please add it manually.`);throw new Error(`Failed to add ${t.chainName} to MetaMask: ${e.message}`)}}},H=({children:e})=>{const{selectedNetwork:t}=F(),[n,a]=b(null),[r,s]=b(null),[o,i]=b(null),[c,l]=b(null),[d,m]=b(null),[h,w]=b(null),[p,y]=b(!1),N=t?(e=>{switch(e){case"sepolia":return _;case"ethereum-classic":return R;case"milkomeda-mainnet":return L;default:return null}})(t):null,T=N?N.id:null,S=k(null),D=k(null),x=C((()=>{a(null),s(null),i(null),l(null),m(null),w(null)}),[]),P=C((async e=>{const n=parseInt(e,16);if(l(n),N&&n===T){if(w(null),window.ethereum&&N){const e=A({chain:N,transport:v(window.ethereum)});a(e)}}else if(N&&n!==T){w(`Wrong network detected. Please switch to ${N?.name||t||"selected network"}`)}}),[N,T,t]);D.current=P;const B=C((async e=>{if(0===e.length){if(x(),window.ethereum){const e=S.current,t=D.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}}else if(i(e[0]),N)try{const t=f({chain:N,transport:E()});s(t);const n=await t.getBalance({address:e[0]});m(parseFloat(n)/Math.pow(10,18))}catch(e){console.error("Error fetching balance:",e),m(null)}}),[N,x]);S.current=B;const j=C((()=>{if(x(),window.ethereum){const e=S.current,t=D.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}}),[x]),I=C((()=>{N&&s(f({chain:N,transport:E()}))}),[N]),M=C((async()=>{if(!window.ethereum)return w("Please install MetaMask or another Web3 wallet"),!1;if(!t||!N)return w("Please select a network first"),!1;y(!0),w(null);try{const e=await window.ethereum.request({method:"eth_requestAccounts"});if(0===e.length)throw new Error("No wallet address found. Please unlock your wallet.");const n=await window.ethereum.request({method:"eth_chainId"});parseInt(n,16)!==T&&await q(t);const r=A({chain:N,transport:v(window.ethereum)});a(r),i(e[0]),l(T);const o=f({chain:N,transport:E()});s(o);try{const t=await o.getBalance({address:e[0]});m(parseFloat(t)/Math.pow(10,18))}catch(e){console.error("Error fetching balance:",e),m(null)}return S.current=B,D.current=P,window.ethereum.on("accountsChanged",B),window.ethereum.on("chainChanged",P),!0}catch(e){return console.error("Error connecting wallet:",e),w(e.message),!1}finally{y(!1)}}),[t,N,T,B,P]),U=C((async()=>{if(!(window.ethereum&&t&&N&&o)){return w("Wallet not connected or network not selected"),null}try{const e=await window.ethereum.request({method:"eth_chainId"});if(parseInt(e,16)!==T){w(null),await q(t);const e=500;await new Promise((t=>setTimeout(t,e)));const n=await window.ethereum.request({method:"eth_chainId"}),a=parseInt(n,16);if(a!==T)throw new Error(`Failed to switch network. MetaMask is still on chain ${a}, expected ${T}`)}const n=A({chain:N,transport:v(window.ethereum)});return a(n),l(T),w(null),n}catch(e){return w(e.message),null}}),[t,N,T,o]),$=k(t);return g((()=>{if(null!==$.current&&$.current!==t&&o&&(x(),window.ethereum)){const e=S.current,t=D.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}$.current=t}),[t,o,x]),g((()=>{I()}),[I]),u.createElement(W.Provider,{value:{walletClient:n,publicClient:r,account:o,chainId:c,balance:d,error:h,isConnecting:p,connectWallet:M,disconnectWallet:j,ensureCorrectNetwork:U,expectedChainId:T}},e)},O=({onTransactionComplete:e})=>{const{networkSelector:t,tokenSelector:n,selectedNetwork:a,selectedToken:r,transactionDetails:s,setTransactionDetails:o}=F(),{connectWallet:i,account:c,walletClient:l,ensureCorrectNetwork:d,isConnecting:m}=(()=>{const e=h(W);if(!e)throw new Error("useWallet must be used within a WalletProvider");return e})(),[w,k]=b(null),[C,p]=b(null),[A,v]=b(""),[f,E]=b(null),[_,N]=b(null);b(!1);const[S,D]=b(!1),[x,P]=b(!1);g((()=>{p(null),v(""),N(null),E(null),D(!1)}),[a,r]),g((()=>{(async()=>{if(a&&r)try{const e=t.getSelectedNetworkConfig(),s=n.getSelectedToken().config,i=new T(e.uri,s);await i.init(),k(i);const l=t.getTokenAmount(r.key);let d=null;try{d=await i.handleTradeDataBuySc(String(l))}catch(e){console.error("Error fetching trade data:",e)}o({network:a,token:r.key,tokenSymbol:r.symbol,amount:l||"0",receivingAddress:t.getReceivingAddress(),tradeAmount:d,...i.getBlockchainDetails()}),c&&(!s.baseAsset.isNative&&d?j(i,c,d,s.baseAsset.decimals):s.baseAsset.isNative&&D(!0))}catch(e){console.error("Error initializing transaction:",e)}})()}),[a,r,t,c]);const j=async(e,t,n,a)=>{try{const r=y(String(n),a),s=await e.checkBaseAssetAllowance(t,r);D(s)}catch(e){console.error("Error checking allowance",e)}};return u.createElement("div",{className:B.transactionReview},u.createElement("div",{className:B.transactionInfo},u.createElement("span",{className:B.transactionLabel},"Network:"),u.createElement("span",{className:B.transactionValue},s.network)),u.createElement("div",{className:B.transactionInfo},u.createElement("span",{className:B.transactionLabel},"You Pay:"),u.createElement("span",{className:B.transactionValue},s.tradeAmount?`${s.tradeAmount} ${s.baseAssetSymbol}`:"Calculating...")),u.createElement("button",{className:B.walletButton,onClick:async()=>{await i()},disabled:m},m?"Connecting...":c?`Connected: ${c.slice(0,6)}...`:"Connect Wallet"),c&&!s.baseAssetIsNative&&!S&&u.createElement("button",{className:B.walletButton,onClick:async()=>{if(w&&c){P(!0),v("⏳ Approving token usage...");try{const{tradeAmount:e,baseAssetDecimals:t}=s,n=y(String(e),t),a=await w.approveBaseAsset(c,n),r=await d(),o=await r.sendTransaction({...a,account:c});v("⏳ Approval Sent. Waiting...");"success"===(await l.waitForTransactionReceipt({hash:o})).status?(D(!0),v("✅ Approved! You can now send the payment.")):v("❌ Approval transaction failed on-chain."),P(!1)}catch(e){console.error(e),N(e),v("❌ Approval failed."),P(!1)}}},disabled:x},x?"Approving...":`Approve ${s.baseAssetSymbol}`),c&&(s.baseAssetIsNative||S)&&!C&&u.createElement("button",{className:B.walletButton,onClick:async()=>{if(c&&w)try{p(null),N(null),v("⏳ Preparing transaction...");const{tradeAmount:e,receivingAddress:t,baseAssetDecimals:n,baseAssetIsNative:a}=s,r=y(String(e),n),o={...await w.buyStablecoins(c,t,r),account:c};o.value=a?r:0n,p(o),v("✅ Transaction ready! Click 'Send Transaction' to proceed.")}catch(e){N(e),v("❌ Transaction preparation failed.")}}},"Prepare Transaction"),c&&C&&u.createElement("button",{className:B.walletButton,onClick:async()=>{try{if(!c||!C)return;const t=await d();v("⏳ Sending transaction...");const n=await t.sendTransaction(C);E(n),v("✅ Transaction sent!"),e&&e({txHash:n,network:a,amount:s?.amount})}catch(e){N(e),v("❌ Transaction failed.")}},disabled:null!==f},"Send Transaction"),A&&u.createElement("div",{className:"message-box"},A),f&&u.createElement("div",{className:B.transactionLink},"✅ Transaction Hash: ",f.slice(0,10),"..."))},J=({onClose:e,buttonSize:t,onTransactionComplete:n})=>{const{resetSelections:a}=F();return u.createElement(j,{onClose:()=>{a(),e()},size:t},u.createElement($,null),u.createElement(z,null),u.createElement(O,{onTransactionComplete:n}))},V=({onClose:e,buttonSize:t,networkSelector:n,onTransactionComplete:a})=>u.createElement(U,{networkSelector:n},u.createElement(H,null,u.createElement(J,{onClose:e,buttonSize:t,onTransactionComplete:a}))),G={NetworkSelector:class{constructor(e){this.merchantConfig=e,this.blacklist=e.getBlacklist(),this.availableNetworks=this.getAvailableNetworks(),this.selectedNetwork=null}getAvailableNetworks(){return Object.entries(N).reduce(((e,[t,n])=>(this.blacklist.includes(n.chainId)||(e[t]=n),e)),{})}selectNetwork(e){return null===e?(this.selectedNetwork=null,console.log("Network selection reset"),!0):this.availableNetworks[e]?(this.selectedNetwork=e,console.log(`Network selected: ${e}`),!0):(console.error(`Invalid network: ${e}`),!1)}getSelectedNetworkConfig(){return this.selectedNetwork?this.availableNetworks[this.selectedNetwork]:null}getReceivingAddress(){return this.merchantConfig.getReceivingAddress()}getTokenAmount(e,t){console.log("Getting amount for network:",e),console.log("Amounts object:",this.amounts);return(this.amounts[e]?.[t]??this.amounts[e]?.stablecoin)||0}},Transaction:T,Config:class{constructor(e={}){this.receivingAddress=e.receivingAddress||"",this.blacklist=e.blacklist||[],this.amounts=e.Amounts||{},this.validateConfig()}validateConfig(){if(!this.receivingAddress)throw new Error("Receiving address is required");for(const[e,t]of Object.entries(this.amounts)){if(!N[e])throw new Error(`Invalid network: ${e}`);if(!t.stablecoin||"number"!=typeof t.stablecoin||t.stablecoin<=0)throw new Error(`Invalid stablecoin amount for network ${e}`)}}getBlacklist(){return this.blacklist}getReceivingAddress(){return this.receivingAddress}getTokenAmount(e){console.log("Getting amount for network:",e),console.log("Amounts object:",this.amounts);const t=this.amounts[e]?.stablecoin;return console.log("Returning amount:",t),t||0}},Widget:({networkSelector:e,buttonSize:t="medium",onTransactionComplete:n,onSuccess:a})=>{const[r,s]=b(!1),o=n||a;return u.createElement("div",{className:B.widgetContainer},!r&&u.createElement(P,{onClick:()=>{s(!0)},size:t}),r&&u.createElement(V,{onClose:()=>{s(!1)},buttonSize:t,networkSelector:e,onTransactionComplete:o}))},PayButton:P,Dialog:j,NetworkDropdown:$};export{G as default}; diff --git a/stablepay-sdk/dist/umd/index.js b/stablepay-sdk/dist/umd/index.js index 0e43e66..e29c064 100644 --- a/stablepay-sdk/dist/umd/index.js +++ b/stablepay-sdk/dist/umd/index.js @@ -1,2 +1,2 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("djed-sdk"),require("react"),require("viem"),require("viem/chains")):"function"==typeof define&&define.amd?define(["djed-sdk","react","viem","viem/chains"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).StablePay=t(e.DjedSdk,e.React,e.viem,e.viemChains)}(this,(function(e,t,n,r){"use strict";const a={sepolia:{uri:"https://ethereum-sepolia.publicnode.com/",chainId:11155111,djedAddress:"0x624FcD0a1F9B5820c950FefD48087531d38387f4",tokens:{stablecoin:{symbol:"SOD",address:"0x6b930182787F346F18666D167e8d32166dC5eFBD",decimals:18,isDirectTransfer:!0},native:{symbol:"ETH",decimals:18,isNative:!0}},feeUI:0},"milkomeda-mainnet":{uri:"https://rpc-mainnet-cardano-evm.c1.milkomeda.com",chainId:2001,djedAddress:"0x67A30B399F5Ed499C1a6Bc0358FA6e42Ea4BCe76",tokens:{stablecoin:{symbol:"MOD",address:"0xcbA90fB1003b9D1bc6a2b66257D2585011b004e9",decimals:18,isDirectTransfer:!0},native:{symbol:"mADA",decimals:18,isNative:!0}},feeUI:0},"ethereum-classic":{uri:"https://etc.rivet.link",chainId:61,djedAddress:"0xCc3664d7021FD36B1Fe2b136e2324710c8442cCf",tokens:{stablecoin:{symbol:"ECSD",address:"0x5A7Ca94F6E969C94bef4CE5e2f90ed9d4891918A",decimals:18,isDirectTransfer:!0},native:{symbol:"ETC",decimals:18,isNative:!0}},feeUI:0}};class i{constructor(e,t){this.networkUri=e,this.djedAddress=t}async init(){if(!this.networkUri||!this.djedAddress)throw new Error("Network URI and DJED address are required");try{this.web3=await e.getWeb3(this.networkUri),this.djedContract=e.getDjedContract(this.web3,this.djedAddress);try{const{stableCoin:t,reserveCoin:n}=await e.getCoinContracts(this.djedContract,this.web3),{scDecimals:r,rcDecimals:a}=await e.getDecimals(t,n);this.stableCoin=t,this.reserveCoin=n,this.scDecimals=r,this.rcDecimals=a,this.oracleContract=await e.getOracleAddress(this.djedContract).then((t=>e.getOracleContract(this.web3,t,this.djedContract._address))),this.oracleAddress=this.oracleContract._address}catch(e){if(console.error("[Transaction] Error fetching contract details:",e),e.message&&e.message.includes("execution reverted")){const e=e=>e.includes("milkomeda")?{name:"Milkomeda",chainId:"2001"}:e.includes("mordor")?{name:"Mordor Testnet",chainId:"63"}:e.includes("sepolia")?{name:"Sepolia",chainId:"11155111"}:e.includes("etc.rivet.link")?{name:"Ethereum Classic",chainId:"61"}:{name:"the selected network",chainId:"unknown"},{name:t,chainId:n}=e(this.networkUri);throw new Error(`Failed to interact with Djed contract at ${this.djedAddress} on ${t}.\n\nPossible causes:\n- The contract address may be incorrect\n- The contract may not be deployed on ${t}\n- The contract may not be a valid Djed contract\n\nPlease verify the contract address is correct for ${t} (Chain ID: ${n}).`)}throw e}}catch(e){if(console.error("[Transaction] Error initializing transaction:",e),e.message&&(e.message.includes("CONNECTION ERROR")||e.message.includes("ERR_NAME_NOT_RESOLVED"))){const e=(e=>e.includes("milkomeda")?"Milkomeda":e.includes("mordor")?"Mordor":e.includes("sepolia")?"Sepolia":"the selected network")(this.networkUri);throw new Error(`Failed to connect to ${e} RPC endpoint: ${this.networkUri}\n\nPossible causes:\n- The RPC endpoint may be temporarily unavailable\n- DNS resolution issue (check your internet connection)\n- Network firewall blocking the connection\n\nPlease try again in a few moments or check the network status.`)}throw e}}getBlockchainDetails(){return{web3Available:!!this.web3,djedContractAvailable:!!this.djedContract,stableCoinAddress:this.stableCoin?this.stableCoin._address:"N/A",reserveCoinAddress:this.reserveCoin?this.reserveCoin._address:"N/A",stableCoinDecimals:this.scDecimals,reserveCoinDecimals:this.rcDecimals,oracleAddress:this.oracleAddress||"N/A",oracleContractAvailable:!!this.oracleContract}}async handleTradeDataBuySc(t){if(!this.djedContract)throw new Error("DJED contract is not initialized");if("string"!=typeof t)throw new Error("Amount must be a string");try{return(await e.tradeDataPriceBuySc(this.djedContract,this.scDecimals,t)).totalBCScaled}catch(e){throw console.error("Error fetching trade data for buying stablecoins: ",e),e}}async buyStablecoins(t,n,r){if(!this.djedContract)throw new Error("DJED contract is not initialized");try{const a="0x0232556C83791b8291E9b23BfEa7d67405Bd9839";return await e.buyScTx(this.djedContract,t,n,r,a,this.djedAddress)}catch(e){throw console.error("Error executing buyStablecoins transaction: ",e),e}}}var o="main_stablePayButton__UA7HC",s="main_logo__ITyEy",c="main_buttonText__N-ewy";const l=({onClick:e,size:n="medium"})=>{const r={small:{width:"200px",height:"50px",fontSize:"14px"},medium:{width:"250px",height:"60px",fontSize:"16px"},large:{width:"300px",height:"70px",fontSize:"18px"}},a={small:{width:"35px",height:"33px"},medium:{width:"40px",height:"38px"},large:{width:"45px",height:"43px"}},i=r[n]||r.medium,l=a[n]||a.medium;return t.createElement("button",{className:o,onClick:e,style:i},t.createElement("div",{className:s,style:l}),t.createElement("span",{className:c},"Pay with StablePay"))};var d={dialogOverlay:"PricingCard_dialogOverlay__0XJrE",pricingCard:"PricingCard_pricingCard__LrWb9",small:"PricingCard_small__J4CHj",medium:"PricingCard_medium__EVmTB",large:"PricingCard_large__A6pnX",dialogClose:"PricingCard_dialogClose__jJ1tM",pricingCardHeader:"PricingCard_pricingCardHeader__wGczA",allianceLogo:"PricingCard_allianceLogo__URa-U",stablepayTitle:"PricingCard_stablepayTitle__4t848",pricingCardBody:"PricingCard_pricingCardBody__0wKQn",selectField:"PricingCard_selectField__LBPoZ",transactionReview:"PricingCard_transactionReview__Ix-eL",transactionInfo:"PricingCard_transactionInfo__Ck-Rc",transactionLabel:"PricingCard_transactionLabel__GDux7",transactionValue:"PricingCard_transactionValue__q-xxp",infoSection:"PricingCard_infoSection__gyjMQ",infoIcon:"PricingCard_infoIcon__rraxD",infoText:"PricingCard_infoText__l4b7A",walletButton:"PricingCard_walletButton__llw4v",loading:"PricingCard_loading__2-tGA",error:"PricingCard_error__m5fK-",networkError:"PricingCard_networkError__zR-36",errorText:"PricingCard_errorText__qZRJt","message-box":"PricingCard_message-box__vkUKy",detailsButton:"PricingCard_detailsButton__jHglL",errorDetails:"PricingCard_errorDetails__CzN-7",loadingContainer:"PricingCard_loadingContainer__6nOVa",spinner:"PricingCard_spinner__9ucQv",spin:"PricingCard_spin__24tni"};const u=({children:e,onClose:n,size:r="medium"})=>t.createElement("div",{className:d.dialogOverlay},t.createElement("div",{className:`${d.pricingCard} ${d[r]}`},t.createElement("button",{className:d.dialogClose,onClick:n},"×"),t.createElement("div",{className:d.pricingCardHeader},t.createElement("div",{className:d.allianceLogo}),t.createElement("h2",{className:d.stablepayTitle},"StablePay")),t.createElement("div",{className:d.pricingCardBody},e)));class m{constructor(e){this.networkSelector=e,this.selectedToken=null}selectToken(e){const t=this.networkSelector.getSelectedNetworkConfig();return!(!t||!t.tokens[e])&&(this.selectedToken={key:e,...t.tokens[e]},!0)}getSelectedToken(){return this.selectedToken}getAvailableTokens(){const e=this.networkSelector.getSelectedNetworkConfig();return e?Object.entries(e.tokens).map((([e,t])=>({key:e,...t}))):[]}resetSelection(){this.selectedToken=null}}const h=t.createContext(),w=({children:e,networkSelector:n})=>{const[r]=t.useState((()=>new m(n))),[a,i]=t.useState(null),[o,s]=t.useState(null),[c,l]=t.useState(null),d=()=>{s(null),l(null)};return t.useEffect((()=>{i(n.selectedNetwork)}),[n.selectedNetwork]),t.createElement(h.Provider,{value:{networkSelector:n,tokenSelector:r,selectedNetwork:a,selectedToken:o,transactionDetails:c,setTransactionDetails:l,selectNetwork:e=>!!n.selectNetwork(e)&&(i(e),d(),!0),selectToken:e=>{if(r.selectToken(e)){const e=r.getSelectedToken();return s(e),!0}return!1},resetSelections:()=>{n.selectNetwork(null),i(null),d()}}},e)},k=()=>{const e=t.useContext(h);if(void 0===e)throw new Error("useNetwork must be used within a NetworkProvider");return e},g=()=>{const{networkSelector:e,selectedNetwork:n,selectNetwork:r}=k();return t.createElement("div",{className:d.selectField},t.createElement("label",{htmlFor:"network-select"},"Select Network"),t.createElement("select",{id:"network-select",onChange:e=>{r(e.target.value)},value:n||""},t.createElement("option",{value:"",disabled:!0},"Select a network"),Object.keys(e.availableNetworks).map((e=>t.createElement("option",{key:e,value:e},e)))))},C=()=>{const{networkSelector:e,tokenSelector:n,selectedNetwork:r,selectedToken:a,selectToken:o,setTransactionDetails:s}=k(),[c,l]=t.useState(!1),[u,m]=t.useState(null),h=r?n.getAvailableTokens():[];return t.createElement("div",{className:d.selectField},t.createElement("label",{htmlFor:"token-select"},"Select Token"),t.createElement("select",{id:"token-select",onChange:async t=>{const a=t.target.value;m(null),l(!0);try{if(o(a)){const t=e.getSelectedNetworkConfig(),o=new i(t.uri,t.djedAddress);await o.init();const c=e.getTokenAmount(a),l=o.getBlockchainDetails();let d=null;"native"===a&&(d=await o.handleTradeDataBuySc(String(c))),s({network:r,token:a,tokenSymbol:n.getSelectedToken().symbol,amount:c,receivingAddress:e.getReceivingAddress(),djedContractAddress:t.djedAddress,isDirectTransfer:n.getSelectedToken().isDirectTransfer||!1,isNativeToken:n.getSelectedToken().isNative||!1,tradeAmount:d?d.amount:null,...l})}}catch(e){console.error("Error fetching transaction details:",e),m("Failed to fetch transaction details. Please try again.")}finally{l(!1)}},value:a?a.key:"",disabled:!r||c},t.createElement("option",{value:"",disabled:!0},r?c?"Loading...":"Select a token":"Please select a network first"),h.map((e=>t.createElement("option",{key:e.key,value:e.key},e.symbol," (",e.isDirectTransfer?"Direct Transfer":"Native",")")))),u&&t.createElement("div",{className:d.error},u))};n.defineChain({id:63,name:"Mordor Testnet",network:"mordor",nativeCurrency:{decimals:18,name:"Mordor Ether",symbol:"METC"},rpcUrls:{default:{http:["https://rpc.mordor.etccooperative.org"],webSocket:["wss://rpc.mordor.etccooperative.org/ws"]}},blockExplorers:{default:{name:"BlockScout",url:"https://blockscout.com/etc/mordor"}},testnet:!0});const b=n.defineChain({id:2001,name:"Milkomeda C1 Mainnet",network:"milkomeda",nativeCurrency:{decimals:18,name:"Milkomeda ADA",symbol:"mADA"},rpcUrls:{default:{http:["https://rpc-mainnet-cardano-evm.c1.milkomeda.com"]}},blockExplorers:{default:{name:"Milkomeda Explorer",url:"https://explorer-mainnet-cardano-evm.c1.milkomeda.com"}},testnet:!1}),p=n.defineChain({id:61,name:"Ethereum Classic",network:"etc",nativeCurrency:{decimals:18,name:"Ethereum Classic",symbol:"ETC"},rpcUrls:{default:{http:["https://etc.rivet.link"]}},blockExplorers:{default:{name:"Blockscout",url:"https://blockscout.com/etc/mainnet"}},testnet:!1}),f=t.createContext(null),v=async e=>{if(!window.ethereum)throw new Error("MetaMask not installed");const t=(e=>{switch(e){case"sepolia":return{chainId:`0x${r.sepolia.id.toString(16)}`,chainName:"Sepolia",nativeCurrency:{name:"Ether",symbol:"ETH",decimals:18},rpcUrls:r.sepolia.rpcUrls.default.http,blockExplorerUrls:r.sepolia.blockExplorers?.default?.url?[r.sepolia.blockExplorers.default.url]:[]};case"ethereum-classic":return{chainId:`0x${p.id.toString(16)}`,chainName:"Ethereum Classic",nativeCurrency:{name:"Ethereum Classic",symbol:"ETC",decimals:18},rpcUrls:["https://etc.rivet.link"],blockExplorerUrls:["https://blockscout.com/etc/mainnet"]};case"milkomeda-mainnet":return{chainId:`0x${b.id.toString(16)}`,chainName:"Milkomeda C1 Mainnet",nativeCurrency:{name:"Milkomeda ADA",symbol:"mADA",decimals:18},rpcUrls:["https://rpc-mainnet-cardano-evm.c1.milkomeda.com"],blockExplorerUrls:["https://explorer-mainnet-cardano-evm.c1.milkomeda.com"]};default:return null}})(e);if(!t)throw new Error(`Unsupported network: ${e}`);try{await window.ethereum.request({method:"wallet_switchEthereumChain",params:[{chainId:t.chainId}]})}catch(e){if(4902!==e.code)throw 4001===e.code?new Error(`User rejected switching to ${t.chainName}. Please switch manually in MetaMask.`):new Error(`Failed to switch to ${t.chainName}: ${e.message}`);try{await window.ethereum.request({method:"wallet_addEthereumChain",params:[t]})}catch(e){if(4001===e.code)throw new Error(`User rejected adding ${t.chainName} to MetaMask. Please add it manually.`);throw new Error(`Failed to add ${t.chainName} to MetaMask: ${e.message}`)}}},y=({children:e})=>{const{selectedNetwork:a}=k(),[i,o]=t.useState(null),[s,c]=t.useState(null),[l,d]=t.useState(null),[u,m]=t.useState(null),[h,w]=t.useState(null),[g,C]=t.useState(null),[y,E]=t.useState(!1),_=a?(e=>{switch(e){case"sepolia":return r.sepolia;case"ethereum-classic":return p;case"milkomeda-mainnet":return b;default:return null}})(a):null,S=_?_.id:null,N=t.useRef(null),T=t.useRef(null),A=t.useCallback((()=>{o(null),c(null),d(null),m(null),w(null),C(null)}),[]),D=t.useCallback((async e=>{const t=parseInt(e,16);if(m(t),_&&t===S){if(C(null),window.ethereum&&_){const e=n.createWalletClient({chain:_,transport:n.custom(window.ethereum)});o(e)}}else if(_&&t!==S){C(`Wrong network detected. Please switch to ${_?.name||a||"selected network"}`)}}),[_,S,a]);T.current=D;const x=t.useCallback((async e=>{if(0===e.length){if(A(),window.ethereum){const e=N.current,t=T.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}}else if(d(e[0]),_)try{const t=n.createPublicClient({chain:_,transport:n.http()});c(t);const r=await t.getBalance({address:e[0]});w(parseFloat(r)/Math.pow(10,18))}catch(e){console.error("Error fetching balance:",e),w(null)}}),[_,A]);N.current=x;const P=t.useCallback((()=>{if(A(),window.ethereum){const e=N.current,t=T.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}}),[A]),I=t.useCallback((()=>{_&&c(n.createPublicClient({chain:_,transport:n.http()}))}),[_]),j=t.useCallback((async()=>{if(!window.ethereum)return C("Please install MetaMask or another Web3 wallet"),!1;if(!a||!_)return C("Please select a network first"),!1;E(!0),C(null);try{const e=await window.ethereum.request({method:"eth_requestAccounts"});if(0===e.length)throw new Error("No wallet address found. Please unlock your wallet.");const t=await window.ethereum.request({method:"eth_chainId"});parseInt(t,16)!==S&&await v(a);const r=n.createWalletClient({chain:_,transport:n.custom(window.ethereum)});o(r),d(e[0]),m(S);const i=n.createPublicClient({chain:_,transport:n.http()});c(i);try{const t=await i.getBalance({address:e[0]});w(parseFloat(t)/Math.pow(10,18))}catch(e){console.error("Error fetching balance:",e),w(null)}return N.current=x,T.current=D,window.ethereum.on("accountsChanged",x),window.ethereum.on("chainChanged",D),!0}catch(e){return console.error("Error connecting wallet:",e),C(e.message),!1}finally{E(!1)}}),[a,_,S,x,D]),B=t.useCallback((async()=>{if(!(window.ethereum&&a&&_&&l)){return C("Wallet not connected or network not selected"),null}try{const e=await window.ethereum.request({method:"eth_chainId"});if(parseInt(e,16)!==S){C(null),await v(a);const e=500;await new Promise((t=>setTimeout(t,e)));const t=await window.ethereum.request({method:"eth_chainId"}),n=parseInt(t,16);if(n!==S)throw new Error(`Failed to switch network. MetaMask is still on chain ${n}, expected ${S}`)}const t=n.createWalletClient({chain:_,transport:n.custom(window.ethereum)});return o(t),m(S),C(null),t}catch(e){return C(e.message),null}}),[a,_,S,l]),M=t.useRef(a);return t.useEffect((()=>{if(null!==M.current&&M.current!==a&&l&&(A(),window.ethereum)){const e=N.current,t=T.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}M.current=a}),[a,l,A]),t.useEffect((()=>{I()}),[I]),t.createElement(f.Provider,{value:{walletClient:i,publicClient:s,account:l,chainId:u,balance:h,error:g,isConnecting:y,connectWallet:j,disconnectWallet:P,ensureCorrectNetwork:B,expectedChainId:S}},e)},E=({onTransactionComplete:e})=>{const{networkSelector:r,selectedNetwork:a,selectedToken:o,transactionDetails:s,setTransactionDetails:c}=k(),{connectWallet:l,account:u,walletClient:m,publicClient:h,isConnecting:w,ensureCorrectNetwork:g,expectedChainId:C}=(()=>{const e=t.useContext(f);if(!e)throw new Error("useWallet must be used within a WalletProvider");return e})(),[b,p]=t.useState(null),[v,y]=t.useState(null),[E,_]=t.useState(null),[S,N]=t.useState(""),[T,A]=t.useState(null),[D,x]=t.useState(null),[P,I]=t.useState(!1);if(t.useEffect((()=>{_(null),y(null),N(""),x(null),A(null)}),[a,o]),t.useEffect((()=>{(async()=>{if(a&&o)try{const e=r.getSelectedNetworkConfig(),t=r.getReceivingAddress(),n=r.getTokenAmount(o.key),s=new i(e.uri,e.djedAddress);await s.init(),p(s);let l=null;if("native"===o.key)try{l=await s.handleTradeDataBuySc(String(n)),y(l)}catch(e){console.error("Error fetching trade data:",e)}c({network:a,token:o.key,tokenSymbol:o.symbol,amount:n||"0",receivingAddress:t,djedContractAddress:e.djedAddress,isDirectTransfer:o.isDirectTransfer||!1,isNativeToken:o.isNative||!1,tradeAmount:l?l.amount:null,...s.getBlockchainDetails()})}catch(e){console.error("Error initializing transaction:",e)}})()}),[a,o,r,c]),!s)return t.createElement("div",{className:d.loading},"Initializing transaction...");const j=()=>{if(!T||!a)return null;const e={"ethereum-classic":"https://blockscout.com/etc/mainnet/tx/",sepolia:"https://sepolia.etherscan.io/tx/","milkomeda-mainnet":"https://explorer-mainnet-cardano-evm.c1.milkomeda.com/tx/"};return e[a]?`${e[a]}${T}`:null};return t.createElement("div",{className:d.transactionReview},t.createElement("div",{className:d.transactionInfo},t.createElement("span",{className:d.transactionLabel},"Network:"),t.createElement("span",{className:d.transactionValue},s.network)),t.createElement("div",{className:d.transactionInfo},t.createElement("span",{className:d.transactionLabel},"You Pay:"),t.createElement("span",{className:d.transactionValue},"stablecoin"===o.key?`${s.amount} ${s.tokenSymbol}`:`${v||"Calculating..."} ${s.tokenSymbol}`)),t.createElement("button",{className:d.walletButton,onClick:async()=>{await l()},disabled:w},w?"Connecting...":"Connect Wallet"),u&&!E&&t.createElement("button",{className:d.walletButton,onClick:async()=>{if(u&&s&&b)try{_(null),x(null),N("⏳ Preparing transaction...");const e=s.receivingAddress;let t;if("native"===o.key){const r="0x0232556C83791b8291E9b23BfEa7d67405Bd9839",a=v||"0",i=n.parseEther(String(a));t=await b.buyStablecoins(u,e,i,r),t={...t,value:i,account:u}}else{const a=r.getSelectedNetworkConfig(),i=a?.tokens?.stablecoin?.address;if(!i)throw new Error("Stablecoin address not found in network configuration");const o=s.amount?n.parseUnits(String(s.amount),s.stableCoinDecimals):"0";t={to:i,value:0n,data:n.encodeFunctionData({abi:[{inputs:[{internalType:"address",name:"to",type:"address"},{internalType:"uint256",name:"amount",type:"uint256"}],name:"transfer",outputs:[{internalType:"bool",name:"",type:"bool"}],stateMutability:"nonpayable",type:"function"}],functionName:"transfer",args:[e,o]}),account:u}}_(t),N("✅ Transaction ready! Click 'Send Transaction' to proceed.")}catch(e){x(e),N("❌ Transaction preparation failed.")}else N("❌ Wallet not connected or transaction details missing")}},"Prepare Transaction"),u&&E&&t.createElement("button",{className:d.walletButton,onClick:async()=>{x(null);try{if(!u||!E)return void N("❌ Wallet account or transaction data is missing");if(!a)return void N("❌ Network not selected");const t=r.getSelectedNetworkConfig();if(!t)return void N("❌ Network configuration not found");N("⏳ Verifying network...");const n=await g();if(!n)return void N("❌ Failed to switch to correct network. Please approve the network switch in MetaMask and try again.");if(!window.ethereum)return void N("❌ MetaMask not available");const i=await window.ethereum.request({method:"eth_chainId"}),c=parseInt(i,16);if(c!==t.chainId){const e=`Network mismatch. MetaMask is on chain ${c}, but ${a} requires chain ${t.chainId}. Please switch networks in MetaMask.`;return N(`❌ ${e}`),void x(new Error(e))}if(n.chain.id!==t.chainId){const e=`Wallet client chain mismatch. Wallet client is on chain ${n.chain.id}, but expected ${t.chainId}.`;return N(`❌ ${e}`),void x(new Error(e))}N("⏳ Sending transaction...");const l=await n.sendTransaction({...E,account:u});A(l),N("✅ Transaction sent!"),e&&e({txHash:l,network:a,token:o?.key,tokenSymbol:o?.symbol,amount:s?.amount,receivingAddress:s?.receivingAddress})}catch(e){x(e),N("❌ Transaction failed."),console.error("Transaction error:",e)}},disabled:null!==T},"Send Transaction"),S&&t.createElement("div",{className:"message-box"},S,D&&t.createElement("button",{onClick:()=>I(!P),className:d.detailsButton},P?"Hide Details":"Show Details")),P&&D&&t.createElement("div",{className:d.errorDetails},t.createElement("pre",null,D.message)),T&&t.createElement("div",{className:d.transactionLink},"✅ Transaction Hash:"," ",j()?t.createElement("a",{href:j(),target:"_blank",rel:"noopener noreferrer",className:d.explorerLink,style:{color:"#007bff",textDecoration:"underline",fontWeight:"bold",cursor:"pointer",wordBreak:"break-word"}},T.slice(0,6),"...",T.slice(-6)):t.createElement("span",{style:{wordBreak:"break-word"}},T)))},_=({onClose:e,buttonSize:n,onTransactionComplete:r})=>{const{resetSelections:a}=k();return t.createElement(u,{onClose:()=>{a(),e()},size:n},t.createElement(g,null),t.createElement(C,null),t.createElement(E,{onTransactionComplete:r}))},S=({onClose:e,buttonSize:n,networkSelector:r,onTransactionComplete:a})=>t.createElement(w,{networkSelector:r},t.createElement(y,null,t.createElement(_,{onClose:e,buttonSize:n,onTransactionComplete:a})));return{NetworkSelector:class{constructor(e){this.merchantConfig=e,this.blacklist=e.getBlacklist(),this.availableNetworks=this.getAvailableNetworks(),this.selectedNetwork=null}getAvailableNetworks(){return Object.entries(a).reduce(((e,[t,n])=>(this.blacklist.includes(n.chainId)||(e[t]=n),e)),{})}selectNetwork(e){return null===e?(this.selectedNetwork=null,console.log("Network selection reset"),!0):this.availableNetworks[e]?(this.selectedNetwork=e,console.log(`Network selected: ${e}`),!0):(console.error(`Invalid network: ${e}`),!1)}getSelectedNetworkConfig(){return this.selectedNetwork?this.availableNetworks[this.selectedNetwork]:null}getReceivingAddress(){return this.merchantConfig.getReceivingAddress()}getTokenAmount(e){return this.merchantConfig.getTokenAmount(this.selectedNetwork,e)}},Transaction:i,Config:class{constructor(e={}){this.receivingAddress=e.receivingAddress||"",this.blacklist=e.blacklist||[],this.amounts=e.Amounts||{},this.validateConfig()}validateConfig(){if(!this.receivingAddress)throw new Error("Receiving address is required");for(const[e,t]of Object.entries(this.amounts)){if(!a[e])throw new Error(`Invalid network: ${e}`);if(!t.stablecoin||"number"!=typeof t.stablecoin||t.stablecoin<=0)throw new Error(`Invalid stablecoin amount for network ${e}`)}}getBlacklist(){return this.blacklist}getReceivingAddress(){return this.receivingAddress}getTokenAmount(e){console.log("Getting amount for network:",e),console.log("Amounts object:",this.amounts);const t=this.amounts[e]?.stablecoin;return console.log("Returning amount:",t),t||0}},Widget:({networkSelector:e,buttonSize:n="medium",onTransactionComplete:r,onSuccess:a})=>{const[i,o]=t.useState(!1),s=r||a;return t.createElement("div",{className:d.widgetContainer},!i&&t.createElement(l,{onClick:()=>{o(!0)},size:n}),i&&t.createElement(S,{onClose:()=>{o(!1)},buttonSize:n,networkSelector:e,onTransactionComplete:s}))},PayButton:l,Dialog:u,NetworkDropdown:g}})); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("djed-sdk"),require("djed-sdk/src/artifacts/CoinABI.json"),require("react"),require("viem"),require("viem/chains")):"function"==typeof define&&define.amd?define(["djed-sdk","djed-sdk/src/artifacts/CoinABI.json","react","viem","viem/chains"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).StablePay=t(e.DjedSdk,e.coinArtifact,e.React,e.viem,e.viemChains)}(this,(function(e,t,a,n,s){"use strict";const r={sepolia:{uri:"https://ethereum-sepolia.publicnode.com/",chainId:11155111,stablecoins:[{id:"djed-eth-sepolia",name:"Djed (ETH Backed)",protocol:"djed",contractAddress:"0x624FcD0a1F9B5820c950FefD48087531d38387f4",baseAsset:{symbol:"ETH",decimals:18,isNative:!0,address:null},stableCoin:{symbol:"SOD",address:"0x6b930182787F346F18666D167e8d32166dC5eFBD",decimals:18,isDirectTransfer:!0}}],feeUI:0},"milkomeda-mainnet":{uri:"https://rpc-mainnet-cardano-evm.c1.milkomeda.com",chainId:2001,stablecoins:[{id:"djed-mada",name:"Djed (mADA Backed)",protocol:"djed",contractAddress:"0x67A30B399F5Ed499C1a6Bc0358FA6e42Ea4BCe76",baseAsset:{symbol:"mADA",decimals:18,isNative:!0,address:null},stableCoin:{symbol:"MOD",address:"0xcbA90fB1003b9D1bc6a2b66257D2585011b004e9",decimals:18,isDirectTransfer:!0}},{id:"djed-usdt-isis",name:"Djed (USDT Backed)",protocol:"isis",contractAddress:"0x0000000000000000000000000000000000000000",baseAsset:{symbol:"USDT",decimals:6,isNative:!1,address:"0x0000000000000000000000000000000000000000"},stableCoin:{symbol:"iUSD",address:"0x0000000000000000000000000000000000000000",decimals:18,isDirectTransfer:!0}}],feeUI:0},"ethereum-classic":{uri:"https://etc.rivet.link",chainId:61,stablecoins:[{id:"djed-etc",name:"Djed (ETC Backed)",protocol:"djed",contractAddress:"0xCc3664d7021FD36B1Fe2b136e2324710c8442cCf",baseAsset:{symbol:"ETC",decimals:18,isNative:!0,address:null},stableCoin:{symbol:"ECSD",address:"0x5A7Ca94F6E969C94bef4CE5e2f90ed9d4891918A",decimals:18,isDirectTransfer:!0}}],feeUI:0}};class i{constructor(e,t){this.networkUri=e,this.config=t,this.djedAddress=t.contractAddress,this.baseAsset=t.baseAsset}async init(){if(!this.networkUri||!this.djedAddress)throw new Error("Network URI and DJED address are required");try{this.web3=await e.getWeb3(this.networkUri),this.djedContract=e.getDjedContract(this.web3,this.djedAddress),!this.baseAsset.isNative&&this.baseAsset.address&&(this.baseAssetContract=new this.web3.eth.Contract(t.abi,this.baseAsset.address));try{const{stableCoin:t,reserveCoin:a}=await e.getCoinContracts(this.djedContract,this.web3),{scDecimals:n,rcDecimals:s}=await e.getDecimals(t,a);this.stableCoin=t,this.reserveCoin=a,this.scDecimals=n,this.rcDecimals=s,this.oracleContract=await e.getOracleAddress(this.djedContract).then((t=>e.getOracleContract(this.web3,t,this.djedContract._address))),this.oracleAddress=this.oracleContract._address}catch(e){throw console.error("[Transaction] Error fetching contract details:",e),e}}catch(e){throw console.error("[Transaction] Error initializing transaction:",e),e}}getBlockchainDetails(){return{web3Available:!!this.web3,djedContractAvailable:!!this.djedContract,stableCoinAddress:this.stableCoin?this.stableCoin._address:"N/A",reserveCoinAddress:this.reserveCoin?this.reserveCoin._address:"N/A",stableCoinDecimals:this.scDecimals,reserveCoinDecimals:this.rcDecimals,oracleAddress:this.oracleAddress||"N/A",oracleContractAvailable:!!this.oracleContract,baseAssetSymbol:this.baseAsset.symbol,baseAssetIsNative:this.baseAsset.isNative,baseAssetDecimals:this.baseAsset.decimals}}async handleTradeDataBuySc(t){if(!this.djedContract)throw new Error("DJED contract is not initialized");try{return(await e.tradeDataPriceBuySc(this.djedContract,this.scDecimals,t)).totalBCScaled}catch(e){throw console.error("Error fetching trade data for buying stablecoins: ",e),e}}async buyStablecoins(t,a,n){if(!this.djedContract)throw new Error("DJED contract is not initialized");try{const s="0x0232556C83791b8291E9b23BfEa7d67405Bd9839";if(this.baseAsset.isNative)return e.buyScTx(this.djedContract,t,a,n,s,this.djedAddress);if(!this.baseAssetContract)throw new Error("Base Asset contract not initialized for ERC20 flow");return e.buyScIsisTx(this.djedContract,t,a,n,s,this.djedAddress)}catch(e){throw console.error("Error executing buyStablecoins transaction: ",e),e}}async approveBaseAsset(t,a){if(this.baseAsset.isNative)throw new Error("Cannot approve native asset");if(!this.baseAssetContract)throw new Error("No Base Asset contract to approve");return e.approveTx(this.baseAssetContract,t,this.djedAddress,a)}async checkBaseAssetAllowance(t,a){if(this.baseAsset.isNative)return!0;if(!this.baseAssetContract)return!1;const n=await e.checkAllowance(this.baseAssetContract,t,this.djedAddress);return BigInt(n)>=BigInt(a)}}var o="main_stablePayButton__UA7HC",c="main_logo__ITyEy",l="main_buttonText__N-ewy";const d=({onClick:e,size:t="medium"})=>{const n={small:{width:"200px",height:"50px",fontSize:"14px"},medium:{width:"250px",height:"60px",fontSize:"16px"},large:{width:"300px",height:"70px",fontSize:"18px"}},s={small:{width:"35px",height:"33px"},medium:{width:"40px",height:"38px"},large:{width:"45px",height:"43px"}},r=n[t]||n.medium,i=s[t]||s.medium;return a.createElement("button",{className:o,onClick:e,style:r},a.createElement("div",{className:c,style:i}),a.createElement("span",{className:l},"Pay with StablePay"))};var u={dialogOverlay:"PricingCard_dialogOverlay__0XJrE",pricingCard:"PricingCard_pricingCard__LrWb9",small:"PricingCard_small__J4CHj",medium:"PricingCard_medium__EVmTB",large:"PricingCard_large__A6pnX",dialogClose:"PricingCard_dialogClose__jJ1tM",pricingCardHeader:"PricingCard_pricingCardHeader__wGczA",allianceLogo:"PricingCard_allianceLogo__URa-U",stablepayTitle:"PricingCard_stablepayTitle__4t848",pricingCardBody:"PricingCard_pricingCardBody__0wKQn",selectField:"PricingCard_selectField__LBPoZ",transactionReview:"PricingCard_transactionReview__Ix-eL",transactionInfo:"PricingCard_transactionInfo__Ck-Rc",transactionLabel:"PricingCard_transactionLabel__GDux7",transactionValue:"PricingCard_transactionValue__q-xxp",infoSection:"PricingCard_infoSection__gyjMQ",infoIcon:"PricingCard_infoIcon__rraxD",infoText:"PricingCard_infoText__l4b7A",walletButton:"PricingCard_walletButton__llw4v",loading:"PricingCard_loading__2-tGA",error:"PricingCard_error__m5fK-",networkError:"PricingCard_networkError__zR-36",errorText:"PricingCard_errorText__qZRJt","message-box":"PricingCard_message-box__vkUKy",detailsButton:"PricingCard_detailsButton__jHglL",errorDetails:"PricingCard_errorDetails__CzN-7",loadingContainer:"PricingCard_loadingContainer__6nOVa",spinner:"PricingCard_spinner__9ucQv",spin:"PricingCard_spin__24tni"};const m=({children:e,onClose:t,size:n="medium"})=>a.createElement("div",{className:u.dialogOverlay},a.createElement("div",{className:`${u.pricingCard} ${u[n]}`},a.createElement("button",{className:u.dialogClose,onClick:t},"×"),a.createElement("div",{className:u.pricingCardHeader},a.createElement("div",{className:u.allianceLogo}),a.createElement("h2",{className:u.stablepayTitle},"StablePay")),a.createElement("div",{className:u.pricingCardBody},e)));class h{constructor(e){this.networkSelector=e,this.selectedToken=null}selectToken(e){const t=this.getAvailableTokens().find((t=>t.key===e));return!!t&&(this.selectedToken=t,!0)}getSelectedToken(){return this.selectedToken}getAvailableTokens(){const e=this.networkSelector.getSelectedNetworkConfig();return e&&e.stablecoins?e.stablecoins.map((e=>({key:e.id,name:e.name,symbol:e.stableCoin.symbol,baseAsset:e.baseAsset.symbol,isDirectTransfer:e.stableCoin.isDirectTransfer,config:e}))):[]}resetSelection(){this.selectedToken=null}}const w=a.createContext(),b=({children:e,networkSelector:t})=>{const[n]=a.useState((()=>new h(t))),[s,r]=a.useState(null),[i,o]=a.useState(null),[c,l]=a.useState(null),d=()=>{o(null),l(null)};return a.useEffect((()=>{r(t.selectedNetwork)}),[t.selectedNetwork]),a.createElement(w.Provider,{value:{networkSelector:t,tokenSelector:n,selectedNetwork:s,selectedToken:i,transactionDetails:c,setTransactionDetails:l,selectNetwork:e=>!!t.selectNetwork(e)&&(r(e),d(),!0),selectToken:e=>{if(n.selectToken(e)){const e=n.getSelectedToken();return o(e),!0}return!1},resetSelections:()=>{t.selectNetwork(null),r(null),d()}}},e)},C=()=>{const e=a.useContext(w);if(void 0===e)throw new Error("useNetwork must be used within a NetworkProvider");return e},k=()=>{const{networkSelector:e,selectedNetwork:t,selectNetwork:n}=C();return a.createElement("div",{className:u.selectField},a.createElement("label",{htmlFor:"network-select"},"Select Network"),a.createElement("select",{id:"network-select",onChange:e=>{n(e.target.value)},value:t||""},a.createElement("option",{value:"",disabled:!0},"Select a network"),Object.keys(e.availableNetworks).map((e=>a.createElement("option",{key:e,value:e},e)))))},g=()=>{const{networkSelector:e,tokenSelector:t,selectedNetwork:n,selectedToken:s,selectToken:r,setTransactionDetails:o}=C(),[c,l]=a.useState(!1),[d,m]=a.useState(null),h=n?t.getAvailableTokens():[];return a.createElement("div",{className:u.selectField},a.createElement("label",{htmlFor:"token-select"},"Select Token"),a.createElement("select",{id:"token-select",onChange:async t=>{const a=t.target.value;m(null),l(!0);try{if(r(a)){const t=e.getSelectedNetworkConfig(),s=t.stablecoins.find((e=>e.id===a));if(!s)throw new Error("Stablecoin configuration not found");const r=new i(t.uri,s);await r.init();const c=e.getTokenAmount(a),l=r.getBlockchainDetails();o({network:n,token:a,tokenSymbol:s.stableCoin.symbol,amount:c,receivingAddress:e.getReceivingAddress(),djedContractAddress:s.contractAddress,isDirectTransfer:s.stableCoin.isDirectTransfer||!1,baseAssetSymbol:s.baseAsset.symbol,baseAssetDecimals:s.baseAsset.decimals,baseAssetIsNative:s.baseAsset.isNative,...l})}}catch(e){console.error("Error updating transaction details:",e),m("Failed to initialize transaction.")}finally{l(!1)}},value:s?s.key:"",disabled:!n||c},a.createElement("option",{value:"",disabled:!0},n?c?"Loading...":"Select a token":"Please select a network first"),h.map((e=>a.createElement("option",{key:e.key,value:e.key},e.symbol," (",e.isDirectTransfer?"Direct Transfer":"Native",")")))),d&&a.createElement("div",{className:u.error},d))};n.defineChain({id:63,name:"Mordor Testnet",network:"mordor",nativeCurrency:{decimals:18,name:"Mordor Ether",symbol:"METC"},rpcUrls:{default:{http:["https://rpc.mordor.etccooperative.org"],webSocket:["wss://rpc.mordor.etccooperative.org/ws"]}},blockExplorers:{default:{name:"BlockScout",url:"https://blockscout.com/etc/mordor"}},testnet:!0});const p=n.defineChain({id:2001,name:"Milkomeda C1 Mainnet",network:"milkomeda",nativeCurrency:{decimals:18,name:"Milkomeda ADA",symbol:"mADA"},rpcUrls:{default:{http:["https://rpc-mainnet-cardano-evm.c1.milkomeda.com"]}},blockExplorers:{default:{name:"Milkomeda Explorer",url:"https://explorer-mainnet-cardano-evm.c1.milkomeda.com"}},testnet:!1}),f=n.defineChain({id:61,name:"Ethereum Classic",network:"etc",nativeCurrency:{decimals:18,name:"Ethereum Classic",symbol:"ETC"},rpcUrls:{default:{http:["https://etc.rivet.link"]}},blockExplorers:{default:{name:"Blockscout",url:"https://blockscout.com/etc/mainnet"}},testnet:!1}),A=a.createContext(null),v=async e=>{if(!window.ethereum)throw new Error("MetaMask not installed");const t=(e=>{switch(e){case"sepolia":return{chainId:`0x${s.sepolia.id.toString(16)}`,chainName:"Sepolia",nativeCurrency:{name:"Ether",symbol:"ETH",decimals:18},rpcUrls:s.sepolia.rpcUrls.default.http,blockExplorerUrls:s.sepolia.blockExplorers?.default?.url?[s.sepolia.blockExplorers.default.url]:[]};case"ethereum-classic":return{chainId:`0x${f.id.toString(16)}`,chainName:"Ethereum Classic",nativeCurrency:{name:"Ethereum Classic",symbol:"ETC",decimals:18},rpcUrls:["https://etc.rivet.link"],blockExplorerUrls:["https://blockscout.com/etc/mainnet"]};case"milkomeda-mainnet":return{chainId:`0x${p.id.toString(16)}`,chainName:"Milkomeda C1 Mainnet",nativeCurrency:{name:"Milkomeda ADA",symbol:"mADA",decimals:18},rpcUrls:["https://rpc-mainnet-cardano-evm.c1.milkomeda.com"],blockExplorerUrls:["https://explorer-mainnet-cardano-evm.c1.milkomeda.com"]};default:return null}})(e);if(!t)throw new Error(`Unsupported network: ${e}`);try{await window.ethereum.request({method:"wallet_switchEthereumChain",params:[{chainId:t.chainId}]})}catch(e){if(4902!==e.code)throw 4001===e.code?new Error(`User rejected switching to ${t.chainName}. Please switch manually in MetaMask.`):new Error(`Failed to switch to ${t.chainName}: ${e.message}`);try{await window.ethereum.request({method:"wallet_addEthereumChain",params:[t]})}catch(e){if(4001===e.code)throw new Error(`User rejected adding ${t.chainName} to MetaMask. Please add it manually.`);throw new Error(`Failed to add ${t.chainName} to MetaMask: ${e.message}`)}}},E=({children:e})=>{const{selectedNetwork:t}=C(),[r,i]=a.useState(null),[o,c]=a.useState(null),[l,d]=a.useState(null),[u,m]=a.useState(null),[h,w]=a.useState(null),[b,k]=a.useState(null),[g,E]=a.useState(!1),y=t?(e=>{switch(e){case"sepolia":return s.sepolia;case"ethereum-classic":return f;case"milkomeda-mainnet":return p;default:return null}})(t):null,_=y?y.id:null,S=a.useRef(null),N=a.useRef(null),T=a.useCallback((()=>{i(null),c(null),d(null),m(null),w(null),k(null)}),[]),D=a.useCallback((async e=>{const a=parseInt(e,16);if(m(a),y&&a===_){if(k(null),window.ethereum&&y){const e=n.createWalletClient({chain:y,transport:n.custom(window.ethereum)});i(e)}}else if(y&&a!==_){k(`Wrong network detected. Please switch to ${y?.name||t||"selected network"}`)}}),[y,_,t]);N.current=D;const x=a.useCallback((async e=>{if(0===e.length){if(T(),window.ethereum){const e=S.current,t=N.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}}else if(d(e[0]),y)try{const t=n.createPublicClient({chain:y,transport:n.http()});c(t);const a=await t.getBalance({address:e[0]});w(parseFloat(a)/Math.pow(10,18))}catch(e){console.error("Error fetching balance:",e),w(null)}}),[y,T]);S.current=x;const P=a.useCallback((()=>{if(T(),window.ethereum){const e=S.current,t=N.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}}),[T]),B=a.useCallback((()=>{y&&c(n.createPublicClient({chain:y,transport:n.http()}))}),[y]),j=a.useCallback((async()=>{if(!window.ethereum)return k("Please install MetaMask or another Web3 wallet"),!1;if(!t||!y)return k("Please select a network first"),!1;E(!0),k(null);try{const e=await window.ethereum.request({method:"eth_requestAccounts"});if(0===e.length)throw new Error("No wallet address found. Please unlock your wallet.");const a=await window.ethereum.request({method:"eth_chainId"});parseInt(a,16)!==_&&await v(t);const s=n.createWalletClient({chain:y,transport:n.custom(window.ethereum)});i(s),d(e[0]),m(_);const r=n.createPublicClient({chain:y,transport:n.http()});c(r);try{const t=await r.getBalance({address:e[0]});w(parseFloat(t)/Math.pow(10,18))}catch(e){console.error("Error fetching balance:",e),w(null)}return S.current=x,N.current=D,window.ethereum.on("accountsChanged",x),window.ethereum.on("chainChanged",D),!0}catch(e){return console.error("Error connecting wallet:",e),k(e.message),!1}finally{E(!1)}}),[t,y,_,x,D]),I=a.useCallback((async()=>{if(!(window.ethereum&&t&&y&&l)){return k("Wallet not connected or network not selected"),null}try{const e=await window.ethereum.request({method:"eth_chainId"});if(parseInt(e,16)!==_){k(null),await v(t);const e=500;await new Promise((t=>setTimeout(t,e)));const a=await window.ethereum.request({method:"eth_chainId"}),n=parseInt(a,16);if(n!==_)throw new Error(`Failed to switch network. MetaMask is still on chain ${n}, expected ${_}`)}const a=n.createWalletClient({chain:y,transport:n.custom(window.ethereum)});return i(a),m(_),k(null),a}catch(e){return k(e.message),null}}),[t,y,_,l]),U=a.useRef(t);return a.useEffect((()=>{if(null!==U.current&&U.current!==t&&l&&(T(),window.ethereum)){const e=S.current,t=N.current;e&&window.ethereum.removeListener("accountsChanged",e),t&&window.ethereum.removeListener("chainChanged",t)}U.current=t}),[t,l,T]),a.useEffect((()=>{B()}),[B]),a.createElement(A.Provider,{value:{walletClient:r,publicClient:o,account:l,chainId:u,balance:h,error:b,isConnecting:g,connectWallet:j,disconnectWallet:P,ensureCorrectNetwork:I,expectedChainId:_}},e)},y=({onTransactionComplete:e})=>{const{networkSelector:t,tokenSelector:s,selectedNetwork:r,selectedToken:o,transactionDetails:c,setTransactionDetails:l}=C(),{connectWallet:d,account:m,walletClient:h,ensureCorrectNetwork:w,isConnecting:b}=(()=>{const e=a.useContext(A);if(!e)throw new Error("useWallet must be used within a WalletProvider");return e})(),[k,g]=a.useState(null),[p,f]=a.useState(null),[v,E]=a.useState(""),[y,_]=a.useState(null),[S,N]=a.useState(null);a.useState(!1);const[T,D]=a.useState(!1),[x,P]=a.useState(!1);a.useEffect((()=>{f(null),E(""),N(null),_(null),D(!1)}),[r,o]),a.useEffect((()=>{(async()=>{if(r&&o)try{const e=t.getSelectedNetworkConfig(),a=s.getSelectedToken().config,n=new i(e.uri,a);await n.init(),g(n);const c=t.getTokenAmount(o.key);let d=null;try{d=await n.handleTradeDataBuySc(String(c))}catch(e){console.error("Error fetching trade data:",e)}l({network:r,token:o.key,tokenSymbol:o.symbol,amount:c||"0",receivingAddress:t.getReceivingAddress(),tradeAmount:d,...n.getBlockchainDetails()}),m&&(!a.baseAsset.isNative&&d?B(n,m,d,a.baseAsset.decimals):a.baseAsset.isNative&&D(!0))}catch(e){console.error("Error initializing transaction:",e)}})()}),[r,o,t,m]);const B=async(e,t,a,s)=>{try{const r=n.parseUnits(String(a),s),i=await e.checkBaseAssetAllowance(t,r);D(i)}catch(e){console.error("Error checking allowance",e)}};return a.createElement("div",{className:u.transactionReview},a.createElement("div",{className:u.transactionInfo},a.createElement("span",{className:u.transactionLabel},"Network:"),a.createElement("span",{className:u.transactionValue},c.network)),a.createElement("div",{className:u.transactionInfo},a.createElement("span",{className:u.transactionLabel},"You Pay:"),a.createElement("span",{className:u.transactionValue},c.tradeAmount?`${c.tradeAmount} ${c.baseAssetSymbol}`:"Calculating...")),a.createElement("button",{className:u.walletButton,onClick:async()=>{await d()},disabled:b},b?"Connecting...":m?`Connected: ${m.slice(0,6)}...`:"Connect Wallet"),m&&!c.baseAssetIsNative&&!T&&a.createElement("button",{className:u.walletButton,onClick:async()=>{if(k&&m){P(!0),E("⏳ Approving token usage...");try{const{tradeAmount:e,baseAssetDecimals:t}=c,a=n.parseUnits(String(e),t),s=await k.approveBaseAsset(m,a),r=await w(),i=await r.sendTransaction({...s,account:m});E("⏳ Approval Sent. Waiting...");"success"===(await h.waitForTransactionReceipt({hash:i})).status?(D(!0),E("✅ Approved! You can now send the payment.")):E("❌ Approval transaction failed on-chain."),P(!1)}catch(e){console.error(e),N(e),E("❌ Approval failed."),P(!1)}}},disabled:x},x?"Approving...":`Approve ${c.baseAssetSymbol}`),m&&(c.baseAssetIsNative||T)&&!p&&a.createElement("button",{className:u.walletButton,onClick:async()=>{if(m&&k)try{f(null),N(null),E("⏳ Preparing transaction...");const{tradeAmount:e,receivingAddress:t,baseAssetDecimals:a,baseAssetIsNative:s}=c,r=n.parseUnits(String(e),a),i={...await k.buyStablecoins(m,t,r),account:m};i.value=s?r:0n,f(i),E("✅ Transaction ready! Click 'Send Transaction' to proceed.")}catch(e){N(e),E("❌ Transaction preparation failed.")}}},"Prepare Transaction"),m&&p&&a.createElement("button",{className:u.walletButton,onClick:async()=>{try{if(!m||!p)return;const t=await w();E("⏳ Sending transaction...");const a=await t.sendTransaction(p);_(a),E("✅ Transaction sent!"),e&&e({txHash:a,network:r,amount:c?.amount})}catch(e){N(e),E("❌ Transaction failed.")}},disabled:null!==y},"Send Transaction"),v&&a.createElement("div",{className:"message-box"},v),y&&a.createElement("div",{className:u.transactionLink},"✅ Transaction Hash: ",y.slice(0,10),"..."))},_=({onClose:e,buttonSize:t,onTransactionComplete:n})=>{const{resetSelections:s}=C();return a.createElement(m,{onClose:()=>{s(),e()},size:t},a.createElement(k,null),a.createElement(g,null),a.createElement(y,{onTransactionComplete:n}))},S=({onClose:e,buttonSize:t,networkSelector:n,onTransactionComplete:s})=>a.createElement(b,{networkSelector:n},a.createElement(E,null,a.createElement(_,{onClose:e,buttonSize:t,onTransactionComplete:s})));return{NetworkSelector:class{constructor(e){this.merchantConfig=e,this.blacklist=e.getBlacklist(),this.availableNetworks=this.getAvailableNetworks(),this.selectedNetwork=null}getAvailableNetworks(){return Object.entries(r).reduce(((e,[t,a])=>(this.blacklist.includes(a.chainId)||(e[t]=a),e)),{})}selectNetwork(e){return null===e?(this.selectedNetwork=null,console.log("Network selection reset"),!0):this.availableNetworks[e]?(this.selectedNetwork=e,console.log(`Network selected: ${e}`),!0):(console.error(`Invalid network: ${e}`),!1)}getSelectedNetworkConfig(){return this.selectedNetwork?this.availableNetworks[this.selectedNetwork]:null}getReceivingAddress(){return this.merchantConfig.getReceivingAddress()}getTokenAmount(e,t){console.log("Getting amount for network:",e),console.log("Amounts object:",this.amounts);return(this.amounts[e]?.[t]??this.amounts[e]?.stablecoin)||0}},Transaction:i,Config:class{constructor(e={}){this.receivingAddress=e.receivingAddress||"",this.blacklist=e.blacklist||[],this.amounts=e.Amounts||{},this.validateConfig()}validateConfig(){if(!this.receivingAddress)throw new Error("Receiving address is required");for(const[e,t]of Object.entries(this.amounts)){if(!r[e])throw new Error(`Invalid network: ${e}`);if(!t.stablecoin||"number"!=typeof t.stablecoin||t.stablecoin<=0)throw new Error(`Invalid stablecoin amount for network ${e}`)}}getBlacklist(){return this.blacklist}getReceivingAddress(){return this.receivingAddress}getTokenAmount(e){console.log("Getting amount for network:",e),console.log("Amounts object:",this.amounts);const t=this.amounts[e]?.stablecoin;return console.log("Returning amount:",t),t||0}},Widget:({networkSelector:e,buttonSize:t="medium",onTransactionComplete:n,onSuccess:s})=>{const[r,i]=a.useState(!1),o=n||s;return a.createElement("div",{className:u.widgetContainer},!r&&a.createElement(d,{onClick:()=>{i(!0)},size:t}),r&&a.createElement(S,{onClose:()=>{i(!1)},buttonSize:t,networkSelector:e,onTransactionComplete:o}))},PayButton:d,Dialog:m,NetworkDropdown:k}})); //# sourceMappingURL=index.js.map diff --git a/stablepay-sdk/dist/umd/index.js.map b/stablepay-sdk/dist/umd/index.js.map index f7c2fad..f6761fd 100644 --- a/stablepay-sdk/dist/umd/index.js.map +++ b/stablepay-sdk/dist/umd/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sources":["../../src/utils/config.js","../../src/core/Transaction.js","../../src/widget/PayButton.jsx","../../src/widget/Dialog.jsx","../../src/core/TokenSelector.js","../../src/contexts/NetworkContext.jsx","../../src/widget/NetworkDropdown.jsx","../../src/widget/TokenDropdown.jsx","../../src/contexts/chains.js","../../src/contexts/WalletContext.jsx","../../src/widget/TransactionReview.jsx","../../src/widget/Widget.jsx","../../src/index.js","../../src/core/NetworkSelector.js","../../src/core/MerchantConfig.js"],"sourcesContent":["// src/utils/config.js\nexport const networksConfig = {\n 'sepolia': {\n uri: 'https://ethereum-sepolia.publicnode.com/',\n chainId: 11155111,\n djedAddress: '0x624FcD0a1F9B5820c950FefD48087531d38387f4',\n tokens: {\n stablecoin: {\n symbol: 'SOD',\n address: '0x6b930182787F346F18666D167e8d32166dC5eFBD',\n decimals: 18,\n isDirectTransfer: true\n },\n native: {\n symbol: 'ETH',\n decimals: 18,\n isNative: true\n }\n },\n feeUI: 0\n },\n 'milkomeda-mainnet': {\n uri: 'https://rpc-mainnet-cardano-evm.c1.milkomeda.com',\n chainId: 2001,\n djedAddress: '0x67A30B399F5Ed499C1a6Bc0358FA6e42Ea4BCe76',\n tokens: {\n stablecoin: {\n symbol: 'MOD',\n address: '0xcbA90fB1003b9D1bc6a2b66257D2585011b004e9',\n decimals: 18,\n isDirectTransfer: true\n },\n native: {\n symbol: 'mADA',\n decimals: 18,\n isNative: true\n }\n },\n feeUI: 0\n },\n 'ethereum-classic': {\n uri: 'https://etc.rivet.link',\n chainId: 61,\n djedAddress: '0xCc3664d7021FD36B1Fe2b136e2324710c8442cCf',\n tokens: {\n stablecoin: {\n symbol: 'ECSD',\n address: '0x5A7Ca94F6E969C94bef4CE5e2f90ed9d4891918A',\n decimals: 18,\n isDirectTransfer: true\n },\n native: {\n symbol: 'ETC',\n decimals: 18,\n isNative: true\n }\n },\n feeUI: 0\n }\n};","import { getWeb3, getDjedContract, getCoinContracts, getDecimals, getOracleAddress, getOracleContract, tradeDataPriceBuySc, buyScTx } from 'djed-sdk';\n\nexport class Transaction {\n constructor(networkUri, djedAddress) {\n this.networkUri = networkUri;\n this.djedAddress = djedAddress;\n }\n\n async init() {\n if (!this.networkUri || !this.djedAddress) {\n throw new Error('Network URI and DJED address are required');\n }\n\n try {\n this.web3 = await getWeb3(this.networkUri);\n this.djedContract = getDjedContract(this.web3, this.djedAddress);\n \n try {\n const { stableCoin, reserveCoin } = await getCoinContracts(this.djedContract, this.web3);\n const { scDecimals, rcDecimals } = await getDecimals(stableCoin, reserveCoin);\n this.stableCoin = stableCoin;\n this.reserveCoin = reserveCoin;\n this.scDecimals = scDecimals;\n this.rcDecimals = rcDecimals;\n\n this.oracleContract = await getOracleAddress(this.djedContract).then((addr) =>\n getOracleContract(this.web3, addr, this.djedContract._address)\n );\n\n this.oracleAddress = this.oracleContract._address;\n } catch (contractError) {\n console.error('[Transaction] Error fetching contract details:', contractError);\n if (contractError.message && contractError.message.includes('execution reverted')) {\n const getNetworkInfo = (uri) => {\n if (uri.includes('milkomeda')) return { name: 'Milkomeda', chainId: '2001' };\n if (uri.includes('mordor')) return { name: 'Mordor Testnet', chainId: '63' };\n if (uri.includes('sepolia')) return { name: 'Sepolia', chainId: '11155111' };\n if (uri.includes('etc.rivet.link')) return { name: 'Ethereum Classic', chainId: '61' };\n return { name: 'the selected network', chainId: 'unknown' };\n };\n const { name: networkName, chainId } = getNetworkInfo(this.networkUri);\n throw new Error(\n `Failed to interact with Djed contract at ${this.djedAddress} on ${networkName}.\\n\\n` +\n `Possible causes:\\n` +\n `- The contract address may be incorrect\\n` +\n `- The contract may not be deployed on ${networkName}\\n` +\n `- The contract may not be a valid Djed contract\\n\\n` +\n `Please verify the contract address is correct for ${networkName} (Chain ID: ${chainId}).`\n );\n }\n throw contractError;\n }\n } catch (error) {\n console.error('[Transaction] Error initializing transaction:', error);\n if (error.message && (error.message.includes('CONNECTION ERROR') || error.message.includes('ERR_NAME_NOT_RESOLVED'))) {\n const getNetworkName = (uri) => {\n if (uri.includes('milkomeda')) return 'Milkomeda';\n if (uri.includes('mordor')) return 'Mordor';\n if (uri.includes('sepolia')) return 'Sepolia';\n return 'the selected network';\n };\n const networkName = getNetworkName(this.networkUri);\n throw new Error(\n `Failed to connect to ${networkName} RPC endpoint: ${this.networkUri}\\n\\n` +\n `Possible causes:\\n` +\n `- The RPC endpoint may be temporarily unavailable\\n` +\n `- DNS resolution issue (check your internet connection)\\n` +\n `- Network firewall blocking the connection\\n\\n` +\n `Please try again in a few moments or check the network status.`\n );\n }\n throw error;\n }\n }\n\n getBlockchainDetails() {\n return {\n web3Available: !!this.web3,\n djedContractAvailable: !!this.djedContract,\n stableCoinAddress: this.stableCoin ? this.stableCoin._address : 'N/A',\n reserveCoinAddress: this.reserveCoin ? this.reserveCoin._address : 'N/A',\n stableCoinDecimals: this.scDecimals,\n reserveCoinDecimals: this.rcDecimals,\n oracleAddress: this.oracleAddress || 'N/A',\n oracleContractAvailable: !!this.oracleContract,\n };\n }\n\n async handleTradeDataBuySc(amountScaled) {\n if (!this.djedContract) {\n throw new Error(\"DJED contract is not initialized\");\n }\n if (typeof amountScaled !== 'string') {\n throw new Error(\"Amount must be a string\");\n }\n try {\n const result = await tradeDataPriceBuySc(this.djedContract, this.scDecimals, amountScaled);\n return result.totalBCScaled;\n } catch (error) {\n console.error(\"Error fetching trade data for buying stablecoins: \", error);\n throw error;\n }\n }\n\n async buyStablecoins(payer, receiver, value) {\n if (!this.djedContract) {\n throw new Error(\"DJED contract is not initialized\");\n }\n try {\n const UI = '0x0232556C83791b8291E9b23BfEa7d67405Bd9839';\n\n const txData = await buyScTx(this.djedContract, payer, receiver, value, UI, this.djedAddress);\n\n return txData;\n } catch (error) {\n console.error(\"Error executing buyStablecoins transaction: \", error);\n throw error;\n }\n }\n}\n","import React from \"react\";\nimport styles from \"../styles/main.css\";\n\nconst PayButton = ({ onClick, size = \"medium\" }) => {\n const sizeStyles = {\n small: { width: \"200px\", height: \"50px\", fontSize: \"14px\" },\n medium: { width: \"250px\", height: \"60px\", fontSize: \"16px\" },\n large: { width: \"300px\", height: \"70px\", fontSize: \"18px\" },\n };\n\n const logoSizes = {\n small: { width: \"35px\", height: \"33px\" },\n medium: { width: \"40px\", height: \"38px\" },\n large: { width: \"45px\", height: \"43px\" },\n };\n\n const buttonStyle = sizeStyles[size] || sizeStyles.medium;\n const logoStyle = logoSizes[size] || logoSizes.medium;\n\n return (\n \n
\n Pay with StablePay\n \n );\n};\n\nexport default PayButton;\n","import React from 'react';\nimport styles from '../styles/PricingCard.css';\n\n\nconst Dialog = ({ children, onClose, size = 'medium' }) => {\n return (\n
\n
\n \n
\n
\n\n

StablePay

\n
\n
\n {children}\n
\n
\n
\n );\n};\n\nexport default Dialog;","// TokenSelector.js\n\nexport class TokenSelector {\n constructor(networkSelector) {\n this.networkSelector = networkSelector;\n this.selectedToken = null;\n }\n\n selectToken(tokenKey) {\n const networkConfig = this.networkSelector.getSelectedNetworkConfig();\n if (networkConfig && networkConfig.tokens[tokenKey]) {\n this.selectedToken = {\n key: tokenKey,\n ...networkConfig.tokens[tokenKey]\n };\n return true;\n }\n return false;\n }\n\n getSelectedToken() {\n return this.selectedToken;\n }\n\n getAvailableTokens() {\n const networkConfig = this.networkSelector.getSelectedNetworkConfig();\n if (!networkConfig) return [];\n\n return Object.entries(networkConfig.tokens).map(([key, config]) => ({\n key,\n ...config\n }));\n }\n\n resetSelection() {\n this.selectedToken = null;\n }\n}","import React, { createContext, useState, useContext, useEffect } from 'react';\nimport { TokenSelector } from '../core/TokenSelector';\n\nconst NetworkContext = createContext();\n\nexport const NetworkProvider = ({ children, networkSelector }) => {\n const [tokenSelector] = useState(() => new TokenSelector(networkSelector));\n const [selectedNetwork, setSelectedNetwork] = useState(null);\n const [selectedToken, setSelectedToken] = useState(null);\n const [transactionDetails, setTransactionDetails] = useState(null);\n\n const resetState = () => {\n setSelectedToken(null);\n setTransactionDetails(null);\n };\n\n const selectNetwork = (networkKey) => {\n if (networkSelector.selectNetwork(networkKey)) {\n setSelectedNetwork(networkKey);\n resetState(); \n return true;\n }\n return false;\n };\n\n const selectToken = (tokenKey) => {\n if (tokenSelector.selectToken(tokenKey)) {\n const token = tokenSelector.getSelectedToken();\n setSelectedToken(token);\n return true;\n }\n return false;\n };\n\n const resetSelections = () => {\n networkSelector.selectNetwork(null);\n setSelectedNetwork(null);\n resetState();\n };\n\n // Synchronize context state with NetworkSelector\n useEffect(() => {\n setSelectedNetwork(networkSelector.selectedNetwork);\n }, [networkSelector.selectedNetwork]);\n\n return (\n \n {children}\n \n );\n};\n\nexport const useNetwork = () => {\n const context = useContext(NetworkContext);\n if (context === undefined) {\n throw new Error('useNetwork must be used within a NetworkProvider');\n }\n return context;\n};\n\nexport default NetworkContext;","import React from 'react';\nimport { useNetwork } from '../contexts/NetworkContext';\nimport styles from '../styles/PricingCard.css';\n\nconst NetworkDropdown = () => {\n const { networkSelector, selectedNetwork, selectNetwork } = useNetwork();\n\n const handleNetworkChange = (event) => {\n selectNetwork(event.target.value);\n };\n\n return (\n
\n \n \n
\n );\n};\n\nexport default NetworkDropdown;","import React, { useState } from \"react\";\nimport { useNetwork } from \"../contexts/NetworkContext\";\nimport { Transaction } from \"../core/Transaction\";\nimport styles from \"../styles/PricingCard.css\";\n\nconst TokenDropdown = () => {\n const {\n networkSelector,\n tokenSelector,\n selectedNetwork,\n selectedToken,\n selectToken,\n setTransactionDetails,\n } = useNetwork();\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(null);\n\n const handleTokenChange = async (event) => {\n const newValue = event.target.value;\n setError(null);\n setLoading(true);\n\n try {\n if (selectToken(newValue)) {\n const networkConfig = networkSelector.getSelectedNetworkConfig();\n const transaction = new Transaction(\n networkConfig.uri,\n networkConfig.djedAddress\n );\n await transaction.init();\n\n const tokenAmount = networkSelector.getTokenAmount(newValue);\n const blockchainDetails = transaction.getBlockchainDetails();\n\n let tradeData = null;\n if (newValue === \"native\") {\n tradeData = await transaction.handleTradeDataBuySc(\n String(tokenAmount)\n );\n }\n\n setTransactionDetails({\n network: selectedNetwork,\n token: newValue,\n tokenSymbol: tokenSelector.getSelectedToken().symbol,\n amount: tokenAmount,\n receivingAddress: networkSelector.getReceivingAddress(),\n djedContractAddress: networkConfig.djedAddress,\n isDirectTransfer:\n tokenSelector.getSelectedToken().isDirectTransfer || false,\n isNativeToken: tokenSelector.getSelectedToken().isNative || false,\n tradeAmount: tradeData ? tradeData.amount : null,\n ...blockchainDetails,\n });\n }\n } catch (err) {\n console.error(\"Error fetching transaction details:\", err);\n setError(\"Failed to fetch transaction details. Please try again.\");\n } finally {\n setLoading(false);\n }\n };\n\n const availableTokens = selectedNetwork\n ? tokenSelector.getAvailableTokens()\n : [];\n\n return (\n
\n \n \n \n {availableTokens.map((token) => (\n \n ))}\n \n {error &&
{error}
}\n
\n );\n};\n\nexport default TokenDropdown;\n","import { defineChain } from 'viem';\nimport { sepolia } from 'viem/chains';\n\nexport const mordor = defineChain({\n id: 63,\n name: 'Mordor Testnet',\n network: 'mordor',\n nativeCurrency: {\n decimals: 18,\n name: 'Mordor Ether',\n symbol: 'METC',\n },\n rpcUrls: {\n default: {\n http: ['https://rpc.mordor.etccooperative.org'],\n webSocket: ['wss://rpc.mordor.etccooperative.org/ws'],\n },\n },\n blockExplorers: {\n default: { name: 'BlockScout', url: 'https://blockscout.com/etc/mordor' },\n },\n testnet: true,\n});\n\nexport const milkomeda = defineChain({\n id: 2001,\n name: 'Milkomeda C1 Mainnet',\n network: 'milkomeda',\n nativeCurrency: {\n decimals: 18,\n name: 'Milkomeda ADA',\n symbol: 'mADA',\n },\n rpcUrls: {\n default: {\n http: ['https://rpc-mainnet-cardano-evm.c1.milkomeda.com'],\n },\n },\n blockExplorers: {\n default: { name: 'Milkomeda Explorer', url: 'https://explorer-mainnet-cardano-evm.c1.milkomeda.com' },\n },\n testnet: false,\n});\n\nexport const etcMainnet = defineChain({\n id: 61,\n name: 'Ethereum Classic',\n network: 'etc',\n nativeCurrency: {\n decimals: 18,\n name: 'Ethereum Classic',\n symbol: 'ETC',\n },\n rpcUrls: {\n default: {\n http: ['https://etc.rivet.link'],\n },\n },\n blockExplorers: {\n default: { name: 'Blockscout', url: 'https://blockscout.com/etc/mainnet' },\n },\n testnet: false,\n});\n\nexport const getChainByNetworkKey = (networkKey) => {\n switch (networkKey) {\n case 'sepolia':\n return sepolia;\n case 'ethereum-classic':\n return etcMainnet;\n case 'milkomeda-mainnet':\n return milkomeda;\n default:\n return null;\n }\n};\n\nexport const getChainConfigForWallet = (networkKey) => {\n switch (networkKey) {\n case 'sepolia':\n return {\n chainId: `0x${sepolia.id.toString(16)}`,\n chainName: 'Sepolia',\n nativeCurrency: {\n name: 'Ether',\n symbol: 'ETH',\n decimals: 18,\n },\n rpcUrls: sepolia.rpcUrls.default.http,\n blockExplorerUrls: sepolia.blockExplorers?.default?.url ? [sepolia.blockExplorers.default.url] : [],\n };\n case 'ethereum-classic':\n return {\n chainId: `0x${etcMainnet.id.toString(16)}`,\n chainName: 'Ethereum Classic',\n nativeCurrency: {\n name: 'Ethereum Classic',\n symbol: 'ETC',\n decimals: 18,\n },\n rpcUrls: ['https://etc.rivet.link'],\n blockExplorerUrls: ['https://blockscout.com/etc/mainnet'],\n };\n case 'milkomeda-mainnet':\n return {\n chainId: `0x${milkomeda.id.toString(16)}`,\n chainName: 'Milkomeda C1 Mainnet',\n nativeCurrency: {\n name: 'Milkomeda ADA',\n symbol: 'mADA',\n decimals: 18,\n },\n rpcUrls: ['https://rpc-mainnet-cardano-evm.c1.milkomeda.com'],\n blockExplorerUrls: ['https://explorer-mainnet-cardano-evm.c1.milkomeda.com'],\n };\n default:\n return null;\n }\n};\n","import React, { createContext, useContext, useState, useCallback, useEffect, useRef } from 'react';\nimport { createWalletClient, createPublicClient, custom, http } from 'viem';\nimport { useNetwork } from './NetworkContext';\nimport { getChainByNetworkKey, getChainConfigForWallet } from './chains';\n\nconst WalletContext = createContext(null);\n\nexport const useWallet = () => {\n const context = useContext(WalletContext);\n if (!context) {\n throw new Error('useWallet must be used within a WalletProvider');\n }\n return context;\n};\n\nconst switchToNetwork = async (networkKey) => {\n if (!window.ethereum) {\n throw new Error('MetaMask not installed');\n }\n\n const chainConfig = getChainConfigForWallet(networkKey);\n if (!chainConfig) {\n throw new Error(`Unsupported network: ${networkKey}`);\n }\n\n try {\n await window.ethereum.request({\n method: 'wallet_switchEthereumChain',\n params: [{ chainId: chainConfig.chainId }],\n });\n } catch (switchError) {\n if (switchError.code === 4902) {\n try {\n await window.ethereum.request({\n method: 'wallet_addEthereumChain',\n params: [chainConfig],\n });\n } catch (addError) {\n if (addError.code === 4001) {\n throw new Error(`User rejected adding ${chainConfig.chainName} to MetaMask. Please add it manually.`);\n }\n throw new Error(`Failed to add ${chainConfig.chainName} to MetaMask: ${addError.message}`);\n }\n } else if (switchError.code === 4001) {\n throw new Error(`User rejected switching to ${chainConfig.chainName}. Please switch manually in MetaMask.`);\n } else {\n throw new Error(`Failed to switch to ${chainConfig.chainName}: ${switchError.message}`);\n }\n }\n};\n\nexport const WalletProvider = ({ children }) => {\n const { selectedNetwork } = useNetwork();\n const [walletClient, setWalletClient] = useState(null);\n const [publicClient, setPublicClient] = useState(null);\n const [account, setAccount] = useState(null);\n const [chainId, setChainId] = useState(null);\n const [balance, setBalance] = useState(null);\n const [error, setError] = useState(null);\n const [isConnecting, setIsConnecting] = useState(false);\n\n const selectedChain = selectedNetwork ? getChainByNetworkKey(selectedNetwork) : null;\n const expectedChainId = selectedChain ? selectedChain.id : null;\n\n const handleAccountsChangedRef = useRef(null);\n const handleChainChangedRef = useRef(null);\n\n const disconnectWalletInternal = useCallback(() => {\n setWalletClient(null);\n setPublicClient(null);\n setAccount(null);\n setChainId(null);\n setBalance(null);\n setError(null);\n }, []);\n\n const handleChainChanged = useCallback(async (chainIdHex) => {\n const newChainId = parseInt(chainIdHex, 16);\n setChainId(newChainId);\n\n if (selectedChain && newChainId === expectedChainId) {\n setError(null);\n if (window.ethereum && selectedChain) {\n const newWalletClient = createWalletClient({ \n chain: selectedChain, \n transport: custom(window.ethereum) \n });\n setWalletClient(newWalletClient);\n }\n } else if (selectedChain && newChainId !== expectedChainId) {\n const chainName = selectedChain?.name || selectedNetwork || 'selected network';\n setError(`Wrong network detected. Please switch to ${chainName}`);\n }\n }, [selectedChain, expectedChainId, selectedNetwork]);\n\n handleChainChangedRef.current = handleChainChanged;\n\n const handleAccountsChanged = useCallback(async (accounts) => {\n if (accounts.length === 0) {\n disconnectWalletInternal();\n if (window.ethereum) {\n const accountsHandler = handleAccountsChangedRef.current;\n const chainHandler = handleChainChangedRef.current;\n if (accountsHandler) {\n window.ethereum.removeListener('accountsChanged', accountsHandler);\n }\n if (chainHandler) {\n window.ethereum.removeListener('chainChanged', chainHandler);\n }\n }\n } else {\n setAccount(accounts[0]);\n if (selectedChain) {\n try {\n const newPublicClient = createPublicClient({ chain: selectedChain, transport: http() });\n setPublicClient(newPublicClient);\n const balance = await newPublicClient.getBalance({ address: accounts[0] });\n setBalance(parseFloat(balance) / Math.pow(10, 18));\n } catch (error) {\n console.error('Error fetching balance:', error);\n setBalance(null);\n }\n }\n }\n }, [selectedChain, disconnectWalletInternal]);\n\n handleAccountsChangedRef.current = handleAccountsChanged;\n\n const disconnectWallet = useCallback(() => {\n disconnectWalletInternal();\n if (window.ethereum) {\n const accountsHandler = handleAccountsChangedRef.current;\n const chainHandler = handleChainChangedRef.current;\n if (accountsHandler) {\n window.ethereum.removeListener('accountsChanged', accountsHandler);\n }\n if (chainHandler) {\n window.ethereum.removeListener('chainChanged', chainHandler);\n }\n }\n }, [disconnectWalletInternal]);\n\n const connectPublicClient = useCallback(() => {\n if (selectedChain) {\n setPublicClient(createPublicClient({ chain: selectedChain, transport: http() }));\n }\n }, [selectedChain]);\n\n const connectWallet = useCallback(async () => {\n if (!window.ethereum) {\n setError('Please install MetaMask or another Web3 wallet');\n return false;\n }\n\n if (!selectedNetwork || !selectedChain) {\n setError('Please select a network first');\n return false;\n }\n\n setIsConnecting(true);\n setError(null);\n\n try {\n const accounts = await window.ethereum.request({ \n method: 'eth_requestAccounts' \n });\n\n if (accounts.length === 0) {\n throw new Error('No wallet address found. Please unlock your wallet.');\n }\n\n const chainIdHex = await window.ethereum.request({ method: 'eth_chainId' });\n const currentChainId = parseInt(chainIdHex, 16);\n\n if (currentChainId !== expectedChainId) {\n await switchToNetwork(selectedNetwork);\n }\n\n const newWalletClient = createWalletClient({\n chain: selectedChain,\n transport: custom(window.ethereum),\n });\n\n setWalletClient(newWalletClient);\n setAccount(accounts[0]);\n setChainId(expectedChainId);\n\n const newPublicClient = createPublicClient({ chain: selectedChain, transport: http() });\n setPublicClient(newPublicClient);\n try {\n const balance = await newPublicClient.getBalance({ address: accounts[0] });\n setBalance(parseFloat(balance) / Math.pow(10, 18));\n } catch (error) {\n console.error('Error fetching balance:', error);\n setBalance(null);\n }\n\n handleAccountsChangedRef.current = handleAccountsChanged;\n handleChainChangedRef.current = handleChainChanged;\n window.ethereum.on('accountsChanged', handleAccountsChanged);\n window.ethereum.on('chainChanged', handleChainChanged);\n\n return true;\n } catch (err) {\n console.error('Error connecting wallet:', err);\n setError(err.message);\n return false;\n } finally {\n setIsConnecting(false);\n }\n }, [selectedNetwork, selectedChain, expectedChainId, handleAccountsChanged, handleChainChanged]);\n\n const ensureCorrectNetwork = useCallback(async () => {\n if (!window.ethereum || !selectedNetwork || !selectedChain || !account) {\n const errorMsg = 'Wallet not connected or network not selected';\n setError(errorMsg);\n return null;\n }\n\n try {\n const chainIdHex = await window.ethereum.request({ method: 'eth_chainId' });\n const currentChainId = parseInt(chainIdHex, 16);\n\n if (currentChainId !== expectedChainId) {\n setError(null);\n await switchToNetwork(selectedNetwork);\n const NETWORK_SWITCH_DELAY_MS = 500;\n await new Promise(resolve => setTimeout(resolve, NETWORK_SWITCH_DELAY_MS));\n \n const newChainIdHex = await window.ethereum.request({ method: 'eth_chainId' });\n const newChainId = parseInt(newChainIdHex, 16);\n if (newChainId !== expectedChainId) {\n throw new Error(`Failed to switch network. MetaMask is still on chain ${newChainId}, expected ${expectedChainId}`);\n }\n }\n\n const freshWalletClient = createWalletClient({\n chain: selectedChain,\n transport: custom(window.ethereum),\n });\n\n setWalletClient(freshWalletClient);\n setChainId(expectedChainId);\n setError(null);\n return freshWalletClient;\n } catch (err) {\n setError(err.message);\n return null;\n }\n }, [selectedNetwork, selectedChain, expectedChainId, account]);\n\n const prevNetworkRef = useRef(selectedNetwork);\n\n useEffect(() => {\n if (prevNetworkRef.current !== null && \n prevNetworkRef.current !== selectedNetwork && \n account) {\n disconnectWalletInternal();\n if (window.ethereum) {\n const accountsHandler = handleAccountsChangedRef.current;\n const chainHandler = handleChainChangedRef.current;\n if (accountsHandler) {\n window.ethereum.removeListener('accountsChanged', accountsHandler);\n }\n if (chainHandler) {\n window.ethereum.removeListener('chainChanged', chainHandler);\n }\n }\n }\n prevNetworkRef.current = selectedNetwork;\n }, [selectedNetwork, account, disconnectWalletInternal]);\n\n useEffect(() => {\n connectPublicClient();\n }, [connectPublicClient]);\n\n return (\n \n {children}\n \n );\n};","import React, { useState, useEffect } from \"react\";\nimport { useNetwork } from \"../contexts/NetworkContext\";\nimport { useWallet } from \"../contexts/WalletContext\";\nimport { Transaction } from \"../core/Transaction\";\nimport { parseEther, encodeFunctionData, parseUnits } from \"viem\"; \nimport styles from \"../styles/PricingCard.css\"; \n\nconst TransactionReview = ({ onTransactionComplete }) => {\n const {\n networkSelector,\n selectedNetwork,\n selectedToken,\n transactionDetails: contextTransactionDetails,\n setTransactionDetails,\n } = useNetwork();\n\n const {\n connectWallet,\n account,\n walletClient,\n publicClient,\n isConnecting,\n ensureCorrectNetwork,\n expectedChainId,\n } = useWallet();\n\n const [transaction, setTransaction] = useState(null);\n const [tradeDataBuySc, setTradeDataBuySc] = useState(null);\n const [txData, setTxData] = useState(null);\n const [message, setMessage] = useState(\"\");\n const [txHash, setTxHash] = useState(null);\n const [error, setError] = useState(null);\n const [isErrorDetailsVisible, setIsErrorDetailsVisible] = useState(false);\n\n useEffect(() => {\n setTxData(null);\n setTradeDataBuySc(null);\n setMessage(\"\");\n setError(null);\n setTxHash(null);\n }, [selectedNetwork, selectedToken]);\n\n useEffect(() => {\n const initializeTransaction = async () => {\n if (!selectedNetwork || !selectedToken) return;\n\n try {\n const networkConfig = networkSelector.getSelectedNetworkConfig();\n const receivingAddress = networkSelector.getReceivingAddress();\n const tokenAmount = networkSelector.getTokenAmount(selectedToken.key);\n\n const newTransaction = new Transaction(\n networkConfig.uri,\n networkConfig.djedAddress\n );\n await newTransaction.init();\n setTransaction(newTransaction);\n\n let tradeData = null;\n if (selectedToken.key === \"native\") {\n try {\n tradeData = await newTransaction.handleTradeDataBuySc(String(tokenAmount));\n setTradeDataBuySc(tradeData);\n } catch (tradeError) {\n console.error(\"Error fetching trade data:\", tradeError);\n }\n }\n\n setTransactionDetails({\n network: selectedNetwork,\n token: selectedToken.key,\n tokenSymbol: selectedToken.symbol,\n amount: tokenAmount || \"0\",\n receivingAddress,\n djedContractAddress: networkConfig.djedAddress,\n isDirectTransfer: selectedToken.isDirectTransfer || false,\n isNativeToken: selectedToken.isNative || false,\n tradeAmount: tradeData ? tradeData.amount : null,\n ...newTransaction.getBlockchainDetails(),\n });\n } catch (err) {\n console.error(\"Error initializing transaction:\", err);\n }\n };\n\n initializeTransaction();\n }, [selectedNetwork, selectedToken, networkSelector, setTransactionDetails]);\n\n if (!contextTransactionDetails) {\n return
Initializing transaction...
;\n }\n\n const handleConnectWallet = async () => {\n await connectWallet();\n };\n\n const handleSendTransaction = async () => {\n if (!account || !contextTransactionDetails || !transaction) {\n setMessage(\"❌ Wallet not connected or transaction details missing\");\n return;\n }\n\n try {\n setTxData(null);\n setError(null);\n setMessage(\"⏳ Preparing transaction...\");\n\n const receiver = contextTransactionDetails.receivingAddress;\n let builtTx;\n\n if (selectedToken.key === \"native\") {\n const UI = \"0x0232556C83791b8291E9b23BfEa7d67405Bd9839\";\n const amountToSend = tradeDataBuySc || \"0\";\n const valueInWei = parseEther(String(amountToSend));\n\n builtTx = await transaction.buyStablecoins(\n account,\n receiver,\n valueInWei,\n UI\n );\n\n builtTx = {\n ...builtTx,\n value: valueInWei,\n account: account,\n };\n } else {\n const networkConfig = networkSelector.getSelectedNetworkConfig();\n const stablecoinAddress = networkConfig?.tokens?.stablecoin?.address;\n \n if (!stablecoinAddress) {\n throw new Error('Stablecoin address not found in network configuration');\n }\n\n const amountToSend = contextTransactionDetails.amount\n ? parseUnits(\n String(contextTransactionDetails.amount),\n contextTransactionDetails.stableCoinDecimals\n )\n : \"0\";\n\n builtTx = {\n to: stablecoinAddress,\n value: 0n,\n data: encodeFunctionData({\n abi: [\n {\n inputs: [\n { internalType: \"address\", name: \"to\", type: \"address\" },\n { internalType: \"uint256\", name: \"amount\", type: \"uint256\" },\n ],\n name: \"transfer\",\n outputs: [{ internalType: \"bool\", name: \"\", type: \"bool\" }],\n stateMutability: \"nonpayable\",\n type: \"function\",\n },\n ],\n functionName: \"transfer\",\n args: [receiver, amountToSend],\n }),\n account: account,\n };\n }\n\n setTxData(builtTx);\n setMessage(\"✅ Transaction ready! Click 'Send Transaction' to proceed.\");\n } catch (error) {\n setError(error);\n setMessage(`❌ Transaction preparation failed.`);\n }\n };\n\n const handleBuySc = async () => {\n setError(null);\n \n try {\n if (!account || !txData) {\n setMessage(\"❌ Wallet account or transaction data is missing\");\n return;\n }\n\n if (!selectedNetwork) {\n setMessage(\"❌ Network not selected\");\n return;\n }\n\n const networkConfig = networkSelector.getSelectedNetworkConfig();\n if (!networkConfig) {\n setMessage(\"❌ Network configuration not found\");\n return;\n }\n\n setMessage(\"⏳ Verifying network...\");\n\n const freshWalletClient = await ensureCorrectNetwork();\n if (!freshWalletClient) {\n setMessage(\"❌ Failed to switch to correct network. Please approve the network switch in MetaMask and try again.\");\n return;\n }\n\n if (!window.ethereum) {\n setMessage(\"❌ MetaMask not available\");\n return;\n }\n\n const chainIdHex = await window.ethereum.request({ method: 'eth_chainId' });\n const currentChainId = parseInt(chainIdHex, 16);\n\n if (currentChainId !== networkConfig.chainId) {\n const errorMsg = `Network mismatch. MetaMask is on chain ${currentChainId}, but ${selectedNetwork} requires chain ${networkConfig.chainId}. Please switch networks in MetaMask.`;\n setMessage(`❌ ${errorMsg}`);\n setError(new Error(errorMsg));\n return;\n }\n\n if (freshWalletClient.chain.id !== networkConfig.chainId) {\n const errorMsg = `Wallet client chain mismatch. Wallet client is on chain ${freshWalletClient.chain.id}, but expected ${networkConfig.chainId}.`;\n setMessage(`❌ ${errorMsg}`);\n setError(new Error(errorMsg));\n return;\n }\n\n setMessage(\"⏳ Sending transaction...\");\n\n const txHash = await freshWalletClient.sendTransaction({\n ...txData,\n account: account,\n });\n\n setTxHash(txHash);\n setMessage(`✅ Transaction sent!`);\n \n if (onTransactionComplete) {\n onTransactionComplete({\n txHash,\n network: selectedNetwork,\n token: selectedToken?.key,\n tokenSymbol: selectedToken?.symbol,\n amount: contextTransactionDetails?.amount,\n receivingAddress: contextTransactionDetails?.receivingAddress,\n });\n }\n } catch (error) {\n setError(error);\n setMessage(`❌ Transaction failed.`);\n console.error('Transaction error:', error);\n }\n };\n\n const getExplorerUrl = () => {\n if (!txHash || !selectedNetwork) return null;\n\n const explorerBaseUrls = {\n \"ethereum-classic\": \"https://blockscout.com/etc/mainnet/tx/\",\n \"sepolia\": \"https://sepolia.etherscan.io/tx/\",\n \"milkomeda-mainnet\": \"https://explorer-mainnet-cardano-evm.c1.milkomeda.com/tx/\",\n };\n\n return explorerBaseUrls[selectedNetwork]\n ? `${explorerBaseUrls[selectedNetwork]}${txHash}`\n : null;\n };\n\n return (\n
\n
\n Network:\n {contextTransactionDetails.network}\n
\n\n
\n You Pay:\n \n {selectedToken.key === \"stablecoin\"\n ? `${contextTransactionDetails.amount} ${contextTransactionDetails.tokenSymbol}`\n : `${tradeDataBuySc ? tradeDataBuySc : \"Calculating...\"} ${\n contextTransactionDetails.tokenSymbol\n }`}\n \n
\n\n \n\n {account && !txData && (\n \n )}\n {account && txData && (\n \n)}\n\n\n {message && (\n
\n {message}\n {error && (\n setIsErrorDetailsVisible(!isErrorDetailsVisible)}\n className={styles.detailsButton}\n >\n {isErrorDetailsVisible ? \"Hide Details\" : \"Show Details\"}\n \n )}\n
\n )}\n\n {isErrorDetailsVisible && error && (\n
\n
{error.message}
\n
\n )}\n\n \n {txHash && (\n
\n ✅ Transaction Hash:{\" \"}\n {getExplorerUrl() ? (\n \n {txHash.slice(0, 6)}...{txHash.slice(-6)}\n \n ) : (\n \n {txHash}\n \n )}\n
\n)}\n\n
\n );\n};\n\nexport default TransactionReview;\n","import React, { useState } from \"react\";\nimport PayButton from \"./PayButton\";\nimport Dialog from \"./Dialog\";\nimport NetworkDropdown from \"./NetworkDropdown\";\nimport TokenDropdown from \"./TokenDropdown\";\nimport TransactionReview from \"./TransactionReview\";\nimport { NetworkProvider, useNetwork } from \"../contexts/NetworkContext\";\nimport { WalletProvider } from \"../contexts/WalletContext\";\nimport styles from \"../styles/PricingCard.css\";\n\nconst WidgetContent = ({ onClose, buttonSize, onTransactionComplete }) => {\n const { resetSelections } = useNetwork(); \n\n const handleClose = () => {\n resetSelections(); // Reset selections when closing the widget\n onClose();\n };\n\n return (\n \n \n \n \n \n );\n};\n\nconst WidgetWithProviders = ({ onClose, buttonSize, networkSelector, onTransactionComplete }) => {\n return (\n \n \n \n \n \n );\n};\n\nexport const Widget = ({ networkSelector, buttonSize = \"medium\", onTransactionComplete, onSuccess }) => {\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n\n const handleOpenDialog = () => {\n setIsDialogOpen(true);\n };\n\n const handleCloseDialog = () => {\n setIsDialogOpen(false);\n };\n\n // Support both onTransactionComplete and onSuccess for backwards compatibility\n const handleTransactionComplete = onTransactionComplete || onSuccess;\n\n return (\n
\n {!isDialogOpen && (\n \n )}\n {isDialogOpen && (\n \n )}\n
\n );\n};\n\nexport default Widget;\n","// src/index.js\nimport { NetworkSelector } from './core/NetworkSelector';\nimport { Transaction } from './core/Transaction';\nimport { Config } from './core/MerchantConfig';\nimport Widget from './widget/Widget.jsx';\nimport PayButton from './widget/PayButton.jsx';\nimport Dialog from './widget/Dialog.jsx';\nimport NetworkDropdown from './widget/NetworkDropdown.jsx';\nimport './styles/main.css';\nimport './styles/PricingCard.css';\n\nconst StablePay = {\n NetworkSelector,\n Transaction,\n Config,\n Widget,\n PayButton,\n Dialog,\n NetworkDropdown\n};\n\nexport default StablePay;","import { networksConfig } from \"../utils/config\";\n\nexport class NetworkSelector {\n constructor(merchantConfig) {\n this.merchantConfig = merchantConfig;\n this.blacklist = merchantConfig.getBlacklist();\n this.availableNetworks = this.getAvailableNetworks();\n this.selectedNetwork = null;\n }\n\n getAvailableNetworks() {\n return Object.entries(networksConfig).reduce(\n (acc, [networkKey, networkConfig]) => {\n if (!this.blacklist.includes(networkConfig.chainId)) {\n acc[networkKey] = networkConfig;\n }\n return acc;\n },\n {}\n );\n }\n\n selectNetwork(networkKey) {\n if (networkKey === null) {\n this.selectedNetwork = null;\n console.log(\"Network selection reset\");\n return true;\n }\n if (this.availableNetworks[networkKey]) {\n this.selectedNetwork = networkKey;\n console.log(`Network selected: ${networkKey}`);\n return true;\n }\n console.error(`Invalid network: ${networkKey}`);\n return false;\n }\n\n getSelectedNetworkConfig() {\n return this.selectedNetwork\n ? this.availableNetworks[this.selectedNetwork]\n : null;\n }\n\n getReceivingAddress() {\n return this.merchantConfig.getReceivingAddress();\n }\n\n getTokenAmount(token) {\n return this.merchantConfig.getTokenAmount(this.selectedNetwork, token);\n }\n}\n","import { networksConfig } from \"../utils/config\";\n\nexport class Config {\n constructor(options = {}) {\n this.receivingAddress = options.receivingAddress || \"\";\n this.blacklist = options.blacklist || [];\n this.amounts = options.Amounts || {}; // Note the capital 'A' in Amounts\n this.validateConfig();\n }\n\n validateConfig() {\n if (!this.receivingAddress) {\n throw new Error(\"Receiving address is required\");\n }\n // Validate stablecoin amounts\n for (const [network, tokens] of Object.entries(this.amounts)) {\n if (!networksConfig[network]) {\n throw new Error(`Invalid network: ${network}`);\n }\n if (\n !tokens.stablecoin ||\n typeof tokens.stablecoin !== \"number\" ||\n tokens.stablecoin <= 0\n ) {\n throw new Error(`Invalid stablecoin amount for network ${network}`);\n }\n }\n }\n\n getBlacklist() {\n return this.blacklist;\n }\n\n getReceivingAddress() {\n return this.receivingAddress;\n }\n\n // getTokenAmount(network, token) {\n // const networkConfig = networksConfig[network];\n // if (!networkConfig) return 0;\n\n // const stablecoinSymbol = networkConfig.tokens.stablecoin.symbol;\n\n // if (token === 'stablecoin') {\n // return this.amounts[network]?.stablecoin || 0;\n // }\n // // For native tokens, return 0 as it's not specified in the new structure\n // return 0;\n // }\n getTokenAmount(network) {\n console.log(\"Getting amount for network:\", network);\n console.log(\"Amounts object:\", this.amounts);\n\n // Directly return the stablecoin amount for the network\n const amount = this.amounts[network]?.stablecoin;\n console.log(\"Returning amount:\", amount);\n\n return amount || 0;\n }\n}\n\nexport default Config;\n"],"names":["networksConfig","sepolia","uri","chainId","djedAddress","tokens","stablecoin","symbol","address","decimals","isDirectTransfer","native","isNative","feeUI","Transaction","constructor","networkUri","this","init","Error","web3","getWeb3","djedContract","getDjedContract","stableCoin","reserveCoin","getCoinContracts","scDecimals","rcDecimals","getDecimals","oracleContract","getOracleAddress","then","addr","getOracleContract","_address","oracleAddress","contractError","console","error","message","includes","getNetworkInfo","name","networkName","getNetworkName","getBlockchainDetails","web3Available","djedContractAvailable","stableCoinAddress","reserveCoinAddress","stableCoinDecimals","reserveCoinDecimals","oracleContractAvailable","handleTradeDataBuySc","amountScaled","tradeDataPriceBuySc","totalBCScaled","buyStablecoins","payer","receiver","value","UI","buyScTx","PayButton","onClick","size","sizeStyles","small","width","height","fontSize","medium","large","logoSizes","buttonStyle","logoStyle","React","createElement","className","styles","style","Dialog","children","onClose","dialogOverlay","pricingCard","dialogClose","pricingCardHeader","allianceLogo","stablepayTitle","pricingCardBody","TokenSelector","networkSelector","selectedToken","selectToken","tokenKey","networkConfig","getSelectedNetworkConfig","key","getSelectedToken","getAvailableTokens","Object","entries","map","config","resetSelection","NetworkContext","createContext","NetworkProvider","tokenSelector","useState","selectedNetwork","setSelectedNetwork","setSelectedToken","transactionDetails","setTransactionDetails","resetState","useEffect","Provider","selectNetwork","networkKey","token","resetSelections","useNetwork","context","useContext","undefined","NetworkDropdown","selectField","htmlFor","id","onChange","event","target","disabled","keys","availableNetworks","TokenDropdown","loading","setLoading","setError","availableTokens","async","newValue","transaction","tokenAmount","getTokenAmount","blockchainDetails","tradeData","String","network","tokenSymbol","amount","receivingAddress","getReceivingAddress","djedContractAddress","isNativeToken","tradeAmount","err","defineChain","nativeCurrency","rpcUrls","default","http","webSocket","blockExplorers","url","testnet","milkomeda","etcMainnet","WalletContext","switchToNetwork","window","ethereum","chainConfig","toString","chainName","blockExplorerUrls","getChainConfigForWallet","request","method","params","switchError","code","addError","WalletProvider","walletClient","setWalletClient","publicClient","setPublicClient","account","setAccount","setChainId","balance","setBalance","isConnecting","setIsConnecting","selectedChain","getChainByNetworkKey","expectedChainId","handleAccountsChangedRef","useRef","handleChainChangedRef","disconnectWalletInternal","useCallback","handleChainChanged","newChainId","parseInt","chainIdHex","newWalletClient","createWalletClient","chain","transport","custom","current","handleAccountsChanged","accounts","length","accountsHandler","chainHandler","removeListener","newPublicClient","createPublicClient","getBalance","parseFloat","Math","pow","disconnectWallet","connectPublicClient","connectWallet","on","ensureCorrectNetwork","NETWORK_SWITCH_DELAY_MS","Promise","resolve","setTimeout","newChainIdHex","freshWalletClient","prevNetworkRef","TransactionReview","onTransactionComplete","contextTransactionDetails","useWallet","setTransaction","tradeDataBuySc","setTradeDataBuySc","txData","setTxData","setMessage","txHash","setTxHash","isErrorDetailsVisible","setIsErrorDetailsVisible","newTransaction","tradeError","initializeTransaction","getExplorerUrl","explorerBaseUrls","transactionReview","transactionInfo","transactionLabel","transactionValue","walletButton","builtTx","amountToSend","valueInWei","parseEther","stablecoinAddress","parseUnits","to","data","encodeFunctionData","abi","inputs","internalType","type","outputs","stateMutability","functionName","args","currentChainId","errorMsg","sendTransaction","detailsButton","errorDetails","transactionLink","href","rel","explorerLink","color","textDecoration","fontWeight","cursor","wordBreak","slice","WidgetContent","buttonSize","handleClose","WidgetWithProviders","NetworkSelector","merchantConfig","blacklist","getBlacklist","getAvailableNetworks","reduce","acc","log","Config","options","amounts","Amounts","validateConfig","Widget","onSuccess","isDialogOpen","setIsDialogOpen","handleTransactionComplete","widgetContainer","handleOpenDialog","handleCloseDialog"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"index.js","sources":["../../src/utils/config.js","../../src/core/Transaction.js","../../src/widget/PayButton.jsx","../../src/widget/Dialog.jsx","../../src/core/TokenSelector.js","../../src/contexts/NetworkContext.jsx","../../src/widget/NetworkDropdown.jsx","../../src/widget/TokenDropdown.jsx","../../src/contexts/chains.js","../../src/contexts/WalletContext.jsx","../../src/widget/TransactionReview.jsx","../../src/widget/Widget.jsx","../../src/index.js","../../src/core/NetworkSelector.js","../../src/core/MerchantConfig.js"],"sourcesContent":["/**\r\n * Final Generalized Configuration\r\n * Supports multiple Djed deployments (Osiris, Isis, Tefnut) per network.\r\n */\r\nexport const networksConfig = {\r\n 'sepolia': {\r\n uri: 'https://ethereum-sepolia.publicnode.com/',\r\n chainId: 11155111,\r\n stablecoins: [\r\n {\r\n id: 'djed-eth-sepolia',\r\n name: 'Djed (ETH Backed)',\r\n protocol: 'djed',\r\n contractAddress: '0x624FcD0a1F9B5820c950FefD48087531d38387f4',\r\n baseAsset: {\r\n symbol: 'ETH',\r\n decimals: 18,\r\n isNative: true,\r\n address: null\r\n },\r\n stableCoin: {\r\n symbol: 'SOD',\r\n address: '0x6b930182787F346F18666D167e8d32166dC5eFBD',\r\n decimals: 18,\r\n isDirectTransfer: true\r\n }\r\n }\r\n ],\r\n feeUI: 0\r\n },\r\n 'milkomeda-mainnet': {\r\n uri: 'https://rpc-mainnet-cardano-evm.c1.milkomeda.com',\r\n chainId: 2001,\r\n stablecoins: [\r\n {\r\n id: 'djed-mada',\r\n name: 'Djed (mADA Backed)',\r\n protocol: 'djed',\r\n contractAddress: '0x67A30B399F5Ed499C1a6Bc0358FA6e42Ea4BCe76',\r\n baseAsset: {\r\n symbol: 'mADA',\r\n decimals: 18,\r\n isNative: true,\r\n address: null\r\n },\r\n stableCoin: {\r\n symbol: 'MOD',\r\n address: '0xcbA90fB1003b9D1bc6a2b66257D2585011b004e9',\r\n decimals: 18,\r\n isDirectTransfer: true\r\n }\r\n },\r\n {\r\n id: 'djed-usdt-isis',\r\n name: 'Djed (USDT Backed)',\r\n protocol: 'isis', // Triggers ERC20 Approval logic\r\n contractAddress: '0x0000000000000000000000000000000000000000', // Replace with live Isis address\r\n baseAsset: { \r\n symbol: 'USDT', \r\n decimals: 6, \r\n isNative: false, \r\n address: '0x0000000000000000000000000000000000000000' // Replace with live USDT address\r\n },\r\n stableCoin: {\r\n symbol: 'iUSD',\r\n address: '0x0000000000000000000000000000000000000000',\r\n decimals: 18,\r\n isDirectTransfer: true\r\n }\r\n }\r\n ],\r\n feeUI: 0\r\n },\r\n 'ethereum-classic': {\r\n uri: 'https://etc.rivet.link',\r\n chainId: 61,\r\n stablecoins: [\r\n {\r\n id: 'djed-etc',\r\n name: 'Djed (ETC Backed)',\r\n protocol: 'djed',\r\n contractAddress: '0xCc3664d7021FD36B1Fe2b136e2324710c8442cCf',\r\n baseAsset: {\r\n symbol: 'ETC',\r\n decimals: 18,\r\n isNative: true,\r\n address: null\r\n },\r\n stableCoin: {\r\n symbol: 'ECSD',\r\n address: '0x5A7Ca94F6E969C94bef4CE5e2f90ed9d4891918A',\r\n decimals: 18,\r\n isDirectTransfer: true\r\n }\r\n }\r\n ],\r\n feeUI: 0\r\n }\r\n};","import { \r\n getWeb3, \r\n getDjedContract, \r\n getCoinContracts, \r\n getDecimals, \r\n getOracleAddress, \r\n getOracleContract, \r\n tradeDataPriceBuySc, \r\n buyScTx, \r\n buyScIsisTx, \r\n checkAllowance, \r\n approveTx \r\n} from 'djed-sdk';\r\nimport coinArtifact from 'djed-sdk/src/artifacts/CoinABI.json'; \r\n\r\nexport class Transaction {\r\n /**\r\n * @param {string} networkUri\r\n * @param {object} stablecoinConfig - The specific config object for the selected stablecoin\r\n */\r\n constructor(networkUri, stablecoinConfig) {\r\n this.networkUri = networkUri;\r\n this.config = stablecoinConfig;\r\n this.djedAddress = stablecoinConfig.contractAddress;\r\n this.baseAsset = stablecoinConfig.baseAsset;\r\n }\r\n\r\n async init() {\r\n if (!this.networkUri || !this.djedAddress) {\r\n throw new Error('Network URI and DJED address are required');\r\n }\r\n\r\n try {\r\n this.web3 = await getWeb3(this.networkUri);\r\n this.djedContract = getDjedContract(this.web3, this.djedAddress);\r\n\r\n // Initialize Base Asset contract if it is ERC20\r\n if (!this.baseAsset.isNative && this.baseAsset.address) {\r\n this.baseAssetContract = new this.web3.eth.Contract(coinArtifact.abi, this.baseAsset.address);\r\n }\r\n \r\n try {\r\n const { stableCoin, reserveCoin } = await getCoinContracts(this.djedContract, this.web3);\r\n const { scDecimals, rcDecimals } = await getDecimals(stableCoin, reserveCoin);\r\n this.stableCoin = stableCoin;\r\n this.reserveCoin = reserveCoin;\r\n this.scDecimals = scDecimals;\r\n this.rcDecimals = rcDecimals;\r\n\r\n this.oracleContract = await getOracleAddress(this.djedContract).then((addr) =>\r\n getOracleContract(this.web3, addr, this.djedContract._address)\r\n );\r\n\r\n this.oracleAddress = this.oracleContract._address;\r\n } catch (contractError) {\r\n console.error('[Transaction] Error fetching contract details:', contractError);\r\n throw contractError;\r\n }\r\n } catch (error) {\r\n console.error('[Transaction] Error initializing transaction:', error);\r\n throw error;\r\n }\r\n }\r\n\r\n getBlockchainDetails() {\r\n return {\r\n web3Available: !!this.web3,\r\n djedContractAvailable: !!this.djedContract,\r\n stableCoinAddress: this.stableCoin ? this.stableCoin._address : 'N/A',\r\n reserveCoinAddress: this.reserveCoin ? this.reserveCoin._address : 'N/A',\r\n stableCoinDecimals: this.scDecimals,\r\n reserveCoinDecimals: this.rcDecimals,\r\n oracleAddress: this.oracleAddress || 'N/A',\r\n oracleContractAvailable: !!this.oracleContract,\r\n baseAssetSymbol: this.baseAsset.symbol,\r\n baseAssetIsNative: this.baseAsset.isNative,\r\n baseAssetDecimals: this.baseAsset.decimals\r\n };\r\n }\r\n\r\n async handleTradeDataBuySc(amountScaled) {\r\n if (!this.djedContract) throw new Error(\"DJED contract is not initialized\");\r\n try {\r\n const result = await tradeDataPriceBuySc(this.djedContract, this.scDecimals, amountScaled);\r\n return result.totalBCScaled;\r\n } catch (error) {\r\n console.error(\"Error fetching trade data for buying stablecoins: \", error);\r\n throw error;\r\n }\r\n }\r\n\r\n async buyStablecoins(payer, receiver, value) {\r\n if (!this.djedContract) throw new Error(\"DJED contract is not initialized\");\r\n \r\n try {\r\n const UI = '0x0232556C83791b8291E9b23BfEa7d67405Bd9839';\r\n\r\n if (!this.baseAsset.isNative) {\r\n // Use Isis transaction builder for ERC20\r\n if (!this.baseAssetContract) throw new Error(\"Base Asset contract not initialized for ERC20 flow\");\r\n return buyScIsisTx(this.djedContract, payer, receiver, value, UI, this.djedAddress);\r\n } else {\r\n // Use Standard transaction builder for Native\r\n return buyScTx(this.djedContract, payer, receiver, value, UI, this.djedAddress);\r\n }\r\n } catch (error) {\r\n console.error(\"Error executing buyStablecoins transaction: \", error);\r\n throw error;\r\n }\r\n }\r\n\r\n async approveBaseAsset(payer, amount) {\r\n if (this.baseAsset.isNative) throw new Error(\"Cannot approve native asset\");\r\n if (!this.baseAssetContract) throw new Error(\"No Base Asset contract to approve\");\r\n \r\n return approveTx(this.baseAssetContract, payer, this.djedAddress, amount);\r\n }\r\n\r\n async checkBaseAssetAllowance(owner, amount) {\r\n if (this.baseAsset.isNative) return true;\r\n if (!this.baseAssetContract) return false;\r\n \r\n const allowance = await checkAllowance(this.baseAssetContract, owner, this.djedAddress);\r\n return BigInt(allowance) >= BigInt(amount);\r\n }\r\n}\r\n","import React from \"react\";\r\nimport styles from \"../styles/main.css\";\r\n\r\nconst PayButton = ({ onClick, size = \"medium\" }) => {\r\n const sizeStyles = {\r\n small: { width: \"200px\", height: \"50px\", fontSize: \"14px\" },\r\n medium: { width: \"250px\", height: \"60px\", fontSize: \"16px\" },\r\n large: { width: \"300px\", height: \"70px\", fontSize: \"18px\" },\r\n };\r\n\r\n const logoSizes = {\r\n small: { width: \"35px\", height: \"33px\" },\r\n medium: { width: \"40px\", height: \"38px\" },\r\n large: { width: \"45px\", height: \"43px\" },\r\n };\r\n\r\n const buttonStyle = sizeStyles[size] || sizeStyles.medium;\r\n const logoStyle = logoSizes[size] || logoSizes.medium;\r\n\r\n return (\r\n \r\n
\r\n Pay with StablePay\r\n \r\n );\r\n};\r\n\r\nexport default PayButton;\r\n","import React from 'react';\r\nimport styles from '../styles/PricingCard.css';\r\n\r\n\r\nconst Dialog = ({ children, onClose, size = 'medium' }) => {\r\n return (\r\n
\r\n
\r\n \r\n
\r\n
\r\n\r\n

StablePay

\r\n
\r\n
\r\n {children}\r\n
\r\n
\r\n
\r\n );\r\n};\r\n\r\nexport default Dialog;","export class TokenSelector {\r\n constructor(networkSelector) {\r\n this.networkSelector = networkSelector;\r\n this.selectedToken = null;\r\n }\r\n\r\n selectToken(tokenKey) {\r\n const tokens = this.getAvailableTokens();\r\n const foundToken = tokens.find(t => t.key === tokenKey);\r\n\r\n if (foundToken) {\r\n this.selectedToken = foundToken;\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n getSelectedToken() {\r\n return this.selectedToken;\r\n }\r\n\r\n getAvailableTokens() {\r\n const networkConfig = this.networkSelector.getSelectedNetworkConfig();\r\n if (!networkConfig || !networkConfig.stablecoins) return [];\r\n\r\n // Map the stablecoins array to the UI format\r\n return networkConfig.stablecoins.map(sc => ({\r\n key: sc.id,\r\n name: sc.name,\r\n symbol: sc.stableCoin.symbol,\r\n baseAsset: sc.baseAsset.symbol,\r\n isDirectTransfer: sc.stableCoin.isDirectTransfer,\r\n // Store full config for Transaction usage\r\n config: sc \r\n }));\r\n }\r\n\r\n resetSelection() {\r\n this.selectedToken = null;\r\n }\r\n}\r\n","import React, { createContext, useState, useContext, useEffect } from 'react';\r\nimport { TokenSelector } from '../core/TokenSelector';\r\n\r\nconst NetworkContext = createContext();\r\n\r\nexport const NetworkProvider = ({ children, networkSelector }) => {\r\n const [tokenSelector] = useState(() => new TokenSelector(networkSelector));\r\n const [selectedNetwork, setSelectedNetwork] = useState(null);\r\n const [selectedToken, setSelectedToken] = useState(null);\r\n const [transactionDetails, setTransactionDetails] = useState(null);\r\n\r\n const resetState = () => {\r\n setSelectedToken(null);\r\n setTransactionDetails(null);\r\n };\r\n\r\n const selectNetwork = (networkKey) => {\r\n if (networkSelector.selectNetwork(networkKey)) {\r\n setSelectedNetwork(networkKey);\r\n resetState(); \r\n return true;\r\n }\r\n return false;\r\n };\r\n\r\n const selectToken = (tokenKey) => {\r\n if (tokenSelector.selectToken(tokenKey)) {\r\n const token = tokenSelector.getSelectedToken();\r\n setSelectedToken(token);\r\n return true;\r\n }\r\n return false;\r\n };\r\n\r\n const resetSelections = () => {\r\n networkSelector.selectNetwork(null);\r\n setSelectedNetwork(null);\r\n resetState();\r\n };\r\n\r\n // Synchronize context state with NetworkSelector\r\n useEffect(() => {\r\n setSelectedNetwork(networkSelector.selectedNetwork);\r\n }, [networkSelector.selectedNetwork]);\r\n\r\n return (\r\n \r\n {children}\r\n \r\n );\r\n};\r\n\r\nexport const useNetwork = () => {\r\n const context = useContext(NetworkContext);\r\n if (context === undefined) {\r\n throw new Error('useNetwork must be used within a NetworkProvider');\r\n }\r\n return context;\r\n};\r\n\r\nexport default NetworkContext;","import React from 'react';\r\nimport { useNetwork } from '../contexts/NetworkContext';\r\nimport styles from '../styles/PricingCard.css';\r\n\r\nconst NetworkDropdown = () => {\r\n const { networkSelector, selectedNetwork, selectNetwork } = useNetwork();\r\n\r\n const handleNetworkChange = (event) => {\r\n selectNetwork(event.target.value);\r\n };\r\n\r\n return (\r\n
\r\n \r\n \r\n
\r\n );\r\n};\r\n\r\nexport default NetworkDropdown;","import React, { useState } from \"react\";\r\nimport { useNetwork } from \"../contexts/NetworkContext\";\r\nimport { Transaction } from \"../core/Transaction\";\r\nimport styles from \"../styles/PricingCard.css\";\r\n\r\nconst TokenDropdown = () => {\r\n const {\r\n networkSelector,\r\n tokenSelector,\r\n selectedNetwork,\r\n selectedToken,\r\n selectToken,\r\n setTransactionDetails,\r\n } = useNetwork();\r\n\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState(null);\r\n\r\n const handleTokenChange = async (event) => {\r\n const newValue = event.target.value; // e.g., 'djed-eth'\r\n setError(null);\r\n setLoading(true);\r\n\r\n try {\r\n if (selectToken(newValue)) {\r\n const networkConfig = networkSelector.getSelectedNetworkConfig();\r\n \r\n // Find the specific stablecoin object matching the selected ID\r\n const selectedStablecoin = networkConfig.stablecoins.find(\r\n (sc) => sc.id === newValue\r\n );\r\n\r\n if (!selectedStablecoin) {\r\n throw new Error(\"Stablecoin configuration not found\");\r\n }\r\n\r\n // Pass the full stablecoin object (replaces the removed djedAddress)\r\n const transaction = new Transaction(\r\n networkConfig.uri,\r\n selectedStablecoin \r\n );\r\n await transaction.init();\r\n\r\n const tokenAmount = networkSelector.getTokenAmount(newValue); \r\n const blockchainDetails = transaction.getBlockchainDetails();\r\n\r\n setTransactionDetails({\r\n network: selectedNetwork,\r\n token: newValue,\r\n tokenSymbol: selectedStablecoin.stableCoin.symbol,\r\n amount: tokenAmount,\r\n receivingAddress: networkSelector.getReceivingAddress(),\r\n \r\n // Use properties from the selected stablecoin object\r\n djedContractAddress: selectedStablecoin.contractAddress,\r\n isDirectTransfer: selectedStablecoin.stableCoin.isDirectTransfer || false,\r\n baseAssetSymbol: selectedStablecoin.baseAsset.symbol,\r\n baseAssetDecimals: selectedStablecoin.baseAsset.decimals,\r\n baseAssetIsNative: selectedStablecoin.baseAsset.isNative,\r\n ...blockchainDetails,\r\n });\r\n }\r\n } catch (err) {\r\n console.error(\"Error updating transaction details:\", err);\r\n setError(\"Failed to initialize transaction.\");\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n const availableTokens = selectedNetwork\r\n ? tokenSelector.getAvailableTokens()\r\n : [];\r\n\r\n return (\r\n
\r\n \r\n \r\n \r\n {availableTokens.map((token) => (\r\n \r\n ))}\r\n \r\n {error &&
{error}
}\r\n
\r\n );\r\n};\r\n\r\nexport default TokenDropdown;\r\n","import { defineChain } from 'viem';\r\nimport { sepolia } from 'viem/chains';\r\n\r\nexport const mordor = defineChain({\r\n id: 63,\r\n name: 'Mordor Testnet',\r\n network: 'mordor',\r\n nativeCurrency: {\r\n decimals: 18,\r\n name: 'Mordor Ether',\r\n symbol: 'METC',\r\n },\r\n rpcUrls: {\r\n default: {\r\n http: ['https://rpc.mordor.etccooperative.org'],\r\n webSocket: ['wss://rpc.mordor.etccooperative.org/ws'],\r\n },\r\n },\r\n blockExplorers: {\r\n default: { name: 'BlockScout', url: 'https://blockscout.com/etc/mordor' },\r\n },\r\n testnet: true,\r\n});\r\n\r\nexport const milkomeda = defineChain({\r\n id: 2001,\r\n name: 'Milkomeda C1 Mainnet',\r\n network: 'milkomeda',\r\n nativeCurrency: {\r\n decimals: 18,\r\n name: 'Milkomeda ADA',\r\n symbol: 'mADA',\r\n },\r\n rpcUrls: {\r\n default: {\r\n http: ['https://rpc-mainnet-cardano-evm.c1.milkomeda.com'],\r\n },\r\n },\r\n blockExplorers: {\r\n default: { name: 'Milkomeda Explorer', url: 'https://explorer-mainnet-cardano-evm.c1.milkomeda.com' },\r\n },\r\n testnet: false,\r\n});\r\n\r\nexport const etcMainnet = defineChain({\r\n id: 61,\r\n name: 'Ethereum Classic',\r\n network: 'etc',\r\n nativeCurrency: {\r\n decimals: 18,\r\n name: 'Ethereum Classic',\r\n symbol: 'ETC',\r\n },\r\n rpcUrls: {\r\n default: {\r\n http: ['https://etc.rivet.link'],\r\n },\r\n },\r\n blockExplorers: {\r\n default: { name: 'Blockscout', url: 'https://blockscout.com/etc/mainnet' },\r\n },\r\n testnet: false,\r\n});\r\n\r\nexport const getChainByNetworkKey = (networkKey) => {\r\n switch (networkKey) {\r\n case 'sepolia':\r\n return sepolia;\r\n case 'ethereum-classic':\r\n return etcMainnet;\r\n case 'milkomeda-mainnet':\r\n return milkomeda;\r\n default:\r\n return null;\r\n }\r\n};\r\n\r\nexport const getChainConfigForWallet = (networkKey) => {\r\n switch (networkKey) {\r\n case 'sepolia':\r\n return {\r\n chainId: `0x${sepolia.id.toString(16)}`,\r\n chainName: 'Sepolia',\r\n nativeCurrency: {\r\n name: 'Ether',\r\n symbol: 'ETH',\r\n decimals: 18,\r\n },\r\n rpcUrls: sepolia.rpcUrls.default.http,\r\n blockExplorerUrls: sepolia.blockExplorers?.default?.url ? [sepolia.blockExplorers.default.url] : [],\r\n };\r\n case 'ethereum-classic':\r\n return {\r\n chainId: `0x${etcMainnet.id.toString(16)}`,\r\n chainName: 'Ethereum Classic',\r\n nativeCurrency: {\r\n name: 'Ethereum Classic',\r\n symbol: 'ETC',\r\n decimals: 18,\r\n },\r\n rpcUrls: ['https://etc.rivet.link'],\r\n blockExplorerUrls: ['https://blockscout.com/etc/mainnet'],\r\n };\r\n case 'milkomeda-mainnet':\r\n return {\r\n chainId: `0x${milkomeda.id.toString(16)}`,\r\n chainName: 'Milkomeda C1 Mainnet',\r\n nativeCurrency: {\r\n name: 'Milkomeda ADA',\r\n symbol: 'mADA',\r\n decimals: 18,\r\n },\r\n rpcUrls: ['https://rpc-mainnet-cardano-evm.c1.milkomeda.com'],\r\n blockExplorerUrls: ['https://explorer-mainnet-cardano-evm.c1.milkomeda.com'],\r\n };\r\n default:\r\n return null;\r\n }\r\n};\r\n","import React, { createContext, useContext, useState, useCallback, useEffect, useRef } from 'react';\r\nimport { createWalletClient, createPublicClient, custom, http } from 'viem';\r\nimport { useNetwork } from './NetworkContext';\r\nimport { getChainByNetworkKey, getChainConfigForWallet } from './chains';\r\n\r\nconst WalletContext = createContext(null);\r\n\r\nexport const useWallet = () => {\r\n const context = useContext(WalletContext);\r\n if (!context) {\r\n throw new Error('useWallet must be used within a WalletProvider');\r\n }\r\n return context;\r\n};\r\n\r\nconst switchToNetwork = async (networkKey) => {\r\n if (!window.ethereum) {\r\n throw new Error('MetaMask not installed');\r\n }\r\n\r\n const chainConfig = getChainConfigForWallet(networkKey);\r\n if (!chainConfig) {\r\n throw new Error(`Unsupported network: ${networkKey}`);\r\n }\r\n\r\n try {\r\n await window.ethereum.request({\r\n method: 'wallet_switchEthereumChain',\r\n params: [{ chainId: chainConfig.chainId }],\r\n });\r\n } catch (switchError) {\r\n if (switchError.code === 4902) {\r\n try {\r\n await window.ethereum.request({\r\n method: 'wallet_addEthereumChain',\r\n params: [chainConfig],\r\n });\r\n } catch (addError) {\r\n if (addError.code === 4001) {\r\n throw new Error(`User rejected adding ${chainConfig.chainName} to MetaMask. Please add it manually.`);\r\n }\r\n throw new Error(`Failed to add ${chainConfig.chainName} to MetaMask: ${addError.message}`);\r\n }\r\n } else if (switchError.code === 4001) {\r\n throw new Error(`User rejected switching to ${chainConfig.chainName}. Please switch manually in MetaMask.`);\r\n } else {\r\n throw new Error(`Failed to switch to ${chainConfig.chainName}: ${switchError.message}`);\r\n }\r\n }\r\n};\r\n\r\nexport const WalletProvider = ({ children }) => {\r\n const { selectedNetwork } = useNetwork();\r\n const [walletClient, setWalletClient] = useState(null);\r\n const [publicClient, setPublicClient] = useState(null);\r\n const [account, setAccount] = useState(null);\r\n const [chainId, setChainId] = useState(null);\r\n const [balance, setBalance] = useState(null);\r\n const [error, setError] = useState(null);\r\n const [isConnecting, setIsConnecting] = useState(false);\r\n\r\n const selectedChain = selectedNetwork ? getChainByNetworkKey(selectedNetwork) : null;\r\n const expectedChainId = selectedChain ? selectedChain.id : null;\r\n\r\n const handleAccountsChangedRef = useRef(null);\r\n const handleChainChangedRef = useRef(null);\r\n\r\n const disconnectWalletInternal = useCallback(() => {\r\n setWalletClient(null);\r\n setPublicClient(null);\r\n setAccount(null);\r\n setChainId(null);\r\n setBalance(null);\r\n setError(null);\r\n }, []);\r\n\r\n const handleChainChanged = useCallback(async (chainIdHex) => {\r\n const newChainId = parseInt(chainIdHex, 16);\r\n setChainId(newChainId);\r\n\r\n if (selectedChain && newChainId === expectedChainId) {\r\n setError(null);\r\n if (window.ethereum && selectedChain) {\r\n const newWalletClient = createWalletClient({ \r\n chain: selectedChain, \r\n transport: custom(window.ethereum) \r\n });\r\n setWalletClient(newWalletClient);\r\n }\r\n } else if (selectedChain && newChainId !== expectedChainId) {\r\n const chainName = selectedChain?.name || selectedNetwork || 'selected network';\r\n setError(`Wrong network detected. Please switch to ${chainName}`);\r\n }\r\n }, [selectedChain, expectedChainId, selectedNetwork]);\r\n\r\n handleChainChangedRef.current = handleChainChanged;\r\n\r\n const handleAccountsChanged = useCallback(async (accounts) => {\r\n if (accounts.length === 0) {\r\n disconnectWalletInternal();\r\n if (window.ethereum) {\r\n const accountsHandler = handleAccountsChangedRef.current;\r\n const chainHandler = handleChainChangedRef.current;\r\n if (accountsHandler) {\r\n window.ethereum.removeListener('accountsChanged', accountsHandler);\r\n }\r\n if (chainHandler) {\r\n window.ethereum.removeListener('chainChanged', chainHandler);\r\n }\r\n }\r\n } else {\r\n setAccount(accounts[0]);\r\n if (selectedChain) {\r\n try {\r\n const newPublicClient = createPublicClient({ chain: selectedChain, transport: http() });\r\n setPublicClient(newPublicClient);\r\n const balance = await newPublicClient.getBalance({ address: accounts[0] });\r\n setBalance(parseFloat(balance) / Math.pow(10, 18));\r\n } catch (error) {\r\n console.error('Error fetching balance:', error);\r\n setBalance(null);\r\n }\r\n }\r\n }\r\n }, [selectedChain, disconnectWalletInternal]);\r\n\r\n handleAccountsChangedRef.current = handleAccountsChanged;\r\n\r\n const disconnectWallet = useCallback(() => {\r\n disconnectWalletInternal();\r\n if (window.ethereum) {\r\n const accountsHandler = handleAccountsChangedRef.current;\r\n const chainHandler = handleChainChangedRef.current;\r\n if (accountsHandler) {\r\n window.ethereum.removeListener('accountsChanged', accountsHandler);\r\n }\r\n if (chainHandler) {\r\n window.ethereum.removeListener('chainChanged', chainHandler);\r\n }\r\n }\r\n }, [disconnectWalletInternal]);\r\n\r\n const connectPublicClient = useCallback(() => {\r\n if (selectedChain) {\r\n setPublicClient(createPublicClient({ chain: selectedChain, transport: http() }));\r\n }\r\n }, [selectedChain]);\r\n\r\n const connectWallet = useCallback(async () => {\r\n if (!window.ethereum) {\r\n setError('Please install MetaMask or another Web3 wallet');\r\n return false;\r\n }\r\n\r\n if (!selectedNetwork || !selectedChain) {\r\n setError('Please select a network first');\r\n return false;\r\n }\r\n\r\n setIsConnecting(true);\r\n setError(null);\r\n\r\n try {\r\n const accounts = await window.ethereum.request({ \r\n method: 'eth_requestAccounts' \r\n });\r\n\r\n if (accounts.length === 0) {\r\n throw new Error('No wallet address found. Please unlock your wallet.');\r\n }\r\n\r\n const chainIdHex = await window.ethereum.request({ method: 'eth_chainId' });\r\n const currentChainId = parseInt(chainIdHex, 16);\r\n\r\n if (currentChainId !== expectedChainId) {\r\n await switchToNetwork(selectedNetwork);\r\n }\r\n\r\n const newWalletClient = createWalletClient({\r\n chain: selectedChain,\r\n transport: custom(window.ethereum),\r\n });\r\n\r\n setWalletClient(newWalletClient);\r\n setAccount(accounts[0]);\r\n setChainId(expectedChainId);\r\n\r\n const newPublicClient = createPublicClient({ chain: selectedChain, transport: http() });\r\n setPublicClient(newPublicClient);\r\n try {\r\n const balance = await newPublicClient.getBalance({ address: accounts[0] });\r\n setBalance(parseFloat(balance) / Math.pow(10, 18));\r\n } catch (error) {\r\n console.error('Error fetching balance:', error);\r\n setBalance(null);\r\n }\r\n\r\n handleAccountsChangedRef.current = handleAccountsChanged;\r\n handleChainChangedRef.current = handleChainChanged;\r\n window.ethereum.on('accountsChanged', handleAccountsChanged);\r\n window.ethereum.on('chainChanged', handleChainChanged);\r\n\r\n return true;\r\n } catch (err) {\r\n console.error('Error connecting wallet:', err);\r\n setError(err.message);\r\n return false;\r\n } finally {\r\n setIsConnecting(false);\r\n }\r\n }, [selectedNetwork, selectedChain, expectedChainId, handleAccountsChanged, handleChainChanged]);\r\n\r\n const ensureCorrectNetwork = useCallback(async () => {\r\n if (!window.ethereum || !selectedNetwork || !selectedChain || !account) {\r\n const errorMsg = 'Wallet not connected or network not selected';\r\n setError(errorMsg);\r\n return null;\r\n }\r\n\r\n try {\r\n const chainIdHex = await window.ethereum.request({ method: 'eth_chainId' });\r\n const currentChainId = parseInt(chainIdHex, 16);\r\n\r\n if (currentChainId !== expectedChainId) {\r\n setError(null);\r\n await switchToNetwork(selectedNetwork);\r\n const NETWORK_SWITCH_DELAY_MS = 500;\r\n await new Promise(resolve => setTimeout(resolve, NETWORK_SWITCH_DELAY_MS));\r\n \r\n const newChainIdHex = await window.ethereum.request({ method: 'eth_chainId' });\r\n const newChainId = parseInt(newChainIdHex, 16);\r\n if (newChainId !== expectedChainId) {\r\n throw new Error(`Failed to switch network. MetaMask is still on chain ${newChainId}, expected ${expectedChainId}`);\r\n }\r\n }\r\n\r\n const freshWalletClient = createWalletClient({\r\n chain: selectedChain,\r\n transport: custom(window.ethereum),\r\n });\r\n\r\n setWalletClient(freshWalletClient);\r\n setChainId(expectedChainId);\r\n setError(null);\r\n return freshWalletClient;\r\n } catch (err) {\r\n setError(err.message);\r\n return null;\r\n }\r\n }, [selectedNetwork, selectedChain, expectedChainId, account]);\r\n\r\n const prevNetworkRef = useRef(selectedNetwork);\r\n\r\n useEffect(() => {\r\n if (prevNetworkRef.current !== null && \r\n prevNetworkRef.current !== selectedNetwork && \r\n account) {\r\n disconnectWalletInternal();\r\n if (window.ethereum) {\r\n const accountsHandler = handleAccountsChangedRef.current;\r\n const chainHandler = handleChainChangedRef.current;\r\n if (accountsHandler) {\r\n window.ethereum.removeListener('accountsChanged', accountsHandler);\r\n }\r\n if (chainHandler) {\r\n window.ethereum.removeListener('chainChanged', chainHandler);\r\n }\r\n }\r\n }\r\n prevNetworkRef.current = selectedNetwork;\r\n }, [selectedNetwork, account, disconnectWalletInternal]);\r\n\r\n useEffect(() => {\r\n connectPublicClient();\r\n }, [connectPublicClient]);\r\n\r\n return (\r\n \r\n {children}\r\n \r\n );\r\n};","import React, { useState, useEffect } from \"react\";\r\nimport { useNetwork } from \"../contexts/NetworkContext\";\r\nimport { useWallet } from \"../contexts/WalletContext\";\r\nimport { Transaction } from \"../core/Transaction\";\r\nimport { parseUnits } from \"viem\"; \r\nimport styles from \"../styles/PricingCard.css\"; \r\n\r\nconst TransactionReview = ({ onTransactionComplete }) => {\r\n const {\r\n networkSelector,\r\n tokenSelector,\r\n selectedNetwork,\r\n selectedToken,\r\n transactionDetails: contextTransactionDetails,\r\n setTransactionDetails,\r\n } = useNetwork();\r\n\r\n const {\r\n connectWallet,\r\n account,\r\n walletClient,\r\n ensureCorrectNetwork,\r\n isConnecting,\r\n } = useWallet();\r\n\r\n const [transaction, setTransaction] = useState(null);\r\n const [txData, setTxData] = useState(null);\r\n const [message, setMessage] = useState(\"\");\r\n const [txHash, setTxHash] = useState(null);\r\n const [error, setError] = useState(null);\r\n const [isErrorDetailsVisible, setIsErrorDetailsVisible] = useState(false);\r\n \r\n // Approval States\r\n const [isApproved, setIsApproved] = useState(false);\r\n const [isApproving, setIsApproving] = useState(false);\r\n\r\n useEffect(() => {\r\n setTxData(null);\r\n setMessage(\"\");\r\n setError(null);\r\n setTxHash(null);\r\n setIsApproved(false);\r\n }, [selectedNetwork, selectedToken]);\r\n\r\n useEffect(() => {\r\n const initializeTransaction = async () => {\r\n if (!selectedNetwork || !selectedToken) return;\r\n\r\n try {\r\n const networkConfig = networkSelector.getSelectedNetworkConfig();\r\n const selectedTokenObj = tokenSelector.getSelectedToken();\r\n const stablecoinConfig = selectedTokenObj.config;\r\n \r\n const newTransaction = new Transaction(\r\n networkConfig.uri,\r\n stablecoinConfig\r\n );\r\n \r\n await newTransaction.init();\r\n setTransaction(newTransaction);\r\n\r\n const tokenAmount = networkSelector.getTokenAmount(selectedToken.key);\r\n let tradeDataAmount = null;\r\n\r\n try {\r\n tradeDataAmount = await newTransaction.handleTradeDataBuySc(String(tokenAmount));\r\n } catch (tradeError) {\r\n console.error(\"Error fetching trade data:\", tradeError);\r\n }\r\n\r\n setTransactionDetails({\r\n network: selectedNetwork,\r\n token: selectedToken.key,\r\n tokenSymbol: selectedToken.symbol,\r\n amount: tokenAmount || \"0\",\r\n receivingAddress: networkSelector.getReceivingAddress(),\r\n tradeAmount: tradeDataAmount, \r\n ...newTransaction.getBlockchainDetails(),\r\n });\r\n \r\n if (account) {\r\n if (!stablecoinConfig.baseAsset.isNative && tradeDataAmount) {\r\n checkAllowance(newTransaction, account, tradeDataAmount, stablecoinConfig.baseAsset.decimals);\r\n } else if (stablecoinConfig.baseAsset.isNative) {\r\n setIsApproved(true);\r\n }\r\n }\r\n\r\n } catch (err) {\r\n console.error(\"Error initializing transaction:\", err);\r\n }\r\n };\r\n\r\n initializeTransaction();\r\n }, [selectedNetwork, selectedToken, networkSelector, account]); \r\n\r\n const checkAllowance = async (txInstance, userAccount, amountStr, decimals) => {\r\n try {\r\n const amountBigInt = parseUnits(String(amountStr), decimals);\r\n const hasAllowance = await txInstance.checkBaseAssetAllowance(userAccount, amountBigInt);\r\n setIsApproved(hasAllowance);\r\n } catch (e) {\r\n console.error(\"Error checking allowance\", e);\r\n }\r\n };\r\n\r\n const handleConnectWallet = async () => {\r\n await connectWallet();\r\n };\r\n \r\n const handleApprove = async () => {\r\n if (!transaction || !account) return;\r\n setIsApproving(true);\r\n setMessage(\"⏳ Approving token usage...\");\r\n try {\r\n const { tradeAmount, baseAssetDecimals } = contextTransactionDetails;\r\n const amount = parseUnits(String(tradeAmount), baseAssetDecimals);\r\n \r\n const approveData = await transaction.approveBaseAsset(account, amount);\r\n const freshWalletClient = await ensureCorrectNetwork();\r\n \r\n const hash = await freshWalletClient.sendTransaction({\r\n ...approveData,\r\n account\r\n });\r\n \r\n setMessage(`⏳ Approval Sent. Waiting...`);\r\n \r\n \r\n const receipt = await walletClient.waitForTransactionReceipt({ hash });\r\n if (receipt.status === 'success') {\r\n setIsApproved(true);\r\n setMessage(\"✅ Approved! You can now send the payment.\");\r\n } else {\r\n setMessage(\"❌ Approval transaction failed on-chain.\");\r\n }\r\n setIsApproving(false);\r\n \r\n } catch(e) {\r\n console.error(e);\r\n setError(e);\r\n setMessage(\"❌ Approval failed.\");\r\n setIsApproving(false);\r\n }\r\n };\r\n\r\n const handlePrepareTransaction = async () => {\r\n if (!account || !transaction) return;\r\n\r\n try {\r\n setTxData(null);\r\n setError(null);\r\n setMessage(\"⏳ Preparing transaction...\");\r\n\r\n const { tradeAmount, receivingAddress, baseAssetDecimals, baseAssetIsNative } = contextTransactionDetails;\r\n const valueInUnits = parseUnits(String(tradeAmount), baseAssetDecimals);\r\n\r\n const builtTx = await transaction.buyStablecoins(\r\n account,\r\n receivingAddress,\r\n valueInUnits\r\n );\r\n\r\n const finalTx = { ...builtTx, account: account };\r\n \r\n if (baseAssetIsNative) {\r\n finalTx.value = valueInUnits;\r\n } else {\r\n finalTx.value = 0n; // ERC20s send 0 value (uses data payload)\r\n }\r\n\r\n setTxData(finalTx);\r\n setMessage(\"✅ Transaction ready! Click 'Send Transaction' to proceed.\");\r\n } catch (error) {\r\n setError(error);\r\n setMessage(`❌ Transaction preparation failed.`);\r\n }\r\n };\r\n\r\n const handleSendTransaction = async () => {\r\n try {\r\n if (!account || !txData) return;\r\n \r\n const freshWalletClient = await ensureCorrectNetwork();\r\n setMessage(\"⏳ Sending transaction...\");\r\n \r\n const txHash = await freshWalletClient.sendTransaction(txData);\r\n\r\n setTxHash(txHash);\r\n setMessage(`✅ Transaction sent!`);\r\n \r\n if (onTransactionComplete) {\r\n onTransactionComplete({\r\n txHash,\r\n network: selectedNetwork,\r\n amount: contextTransactionDetails?.amount,\r\n });\r\n }\r\n } catch (error) {\r\n setError(error);\r\n setMessage(`❌ Transaction failed.`);\r\n }\r\n };\r\n\r\n return (\r\n
\r\n
\r\n Network:\r\n {contextTransactionDetails.network}\r\n
\r\n\r\n
\r\n You Pay:\r\n \r\n {contextTransactionDetails.tradeAmount \r\n ? `${contextTransactionDetails.tradeAmount} ${contextTransactionDetails.baseAssetSymbol}`\r\n : \"Calculating...\"}\r\n \r\n
\r\n\r\n \r\n\r\n {account && !contextTransactionDetails.baseAssetIsNative && !isApproved && (\r\n \r\n )}\r\n\r\n {account && (contextTransactionDetails.baseAssetIsNative || isApproved) && !txData && (\r\n \r\n )}\r\n \r\n {account && txData && (\r\n \r\n )}\r\n\r\n {message &&
{message}
}\r\n \r\n {txHash && (\r\n
\r\n ✅ Transaction Hash: {txHash.slice(0, 10)}...\r\n
\r\n )}\r\n
\r\n );\r\n};\r\n\r\nexport default TransactionReview;\r\n","import React, { useState } from \"react\";\r\nimport PayButton from \"./PayButton\";\r\nimport Dialog from \"./Dialog\";\r\nimport NetworkDropdown from \"./NetworkDropdown\";\r\nimport TokenDropdown from \"./TokenDropdown\";\r\nimport TransactionReview from \"./TransactionReview\";\r\nimport { NetworkProvider, useNetwork } from \"../contexts/NetworkContext\";\r\nimport { WalletProvider } from \"../contexts/WalletContext\";\r\nimport styles from \"../styles/PricingCard.css\";\r\n\r\nconst WidgetContent = ({ onClose, buttonSize, onTransactionComplete }) => {\r\n const { resetSelections } = useNetwork(); \r\n\r\n const handleClose = () => {\r\n resetSelections(); // Reset selections when closing the widget\r\n onClose();\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nconst WidgetWithProviders = ({ onClose, buttonSize, networkSelector, onTransactionComplete }) => {\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport const Widget = ({ networkSelector, buttonSize = \"medium\", onTransactionComplete, onSuccess }) => {\r\n const [isDialogOpen, setIsDialogOpen] = useState(false);\r\n\r\n const handleOpenDialog = () => {\r\n setIsDialogOpen(true);\r\n };\r\n\r\n const handleCloseDialog = () => {\r\n setIsDialogOpen(false);\r\n };\r\n\r\n // Support both onTransactionComplete and onSuccess for backwards compatibility\r\n const handleTransactionComplete = onTransactionComplete || onSuccess;\r\n\r\n return (\r\n
\r\n {!isDialogOpen && (\r\n \r\n )}\r\n {isDialogOpen && (\r\n \r\n )}\r\n
\r\n );\r\n};\r\n\r\nexport default Widget;\r\n","// src/index.js\r\nimport { NetworkSelector } from './core/NetworkSelector';\r\nimport { Transaction } from './core/Transaction';\r\nimport { Config } from './core/MerchantConfig';\r\nimport Widget from './widget/Widget.jsx';\r\nimport PayButton from './widget/PayButton.jsx';\r\nimport Dialog from './widget/Dialog.jsx';\r\nimport NetworkDropdown from './widget/NetworkDropdown.jsx';\r\nimport './styles/main.css';\r\nimport './styles/PricingCard.css';\r\n\r\nconst StablePay = {\r\n NetworkSelector,\r\n Transaction,\r\n Config,\r\n Widget,\r\n PayButton,\r\n Dialog,\r\n NetworkDropdown\r\n};\r\n\r\nexport default StablePay;","import { networksConfig } from \"../utils/config\";\r\n\r\nexport class NetworkSelector {\r\n constructor(merchantConfig) {\r\n this.merchantConfig = merchantConfig;\r\n this.blacklist = merchantConfig.getBlacklist();\r\n this.availableNetworks = this.getAvailableNetworks();\r\n this.selectedNetwork = null;\r\n }\r\n\r\n getAvailableNetworks() {\r\n return Object.entries(networksConfig).reduce(\r\n (acc, [networkKey, networkConfig]) => {\r\n if (!this.blacklist.includes(networkConfig.chainId)) {\r\n acc[networkKey] = networkConfig;\r\n }\r\n return acc;\r\n },\r\n {}\r\n );\r\n }\r\n\r\n selectNetwork(networkKey) {\r\n if (networkKey === null) {\r\n this.selectedNetwork = null;\r\n console.log(\"Network selection reset\");\r\n return true;\r\n }\r\n if (this.availableNetworks[networkKey]) {\r\n this.selectedNetwork = networkKey;\r\n console.log(`Network selected: ${networkKey}`);\r\n return true;\r\n }\r\n console.error(`Invalid network: ${networkKey}`);\r\n return false;\r\n }\r\n\r\n getSelectedNetworkConfig() {\r\n return this.selectedNetwork\r\n ? this.availableNetworks[this.selectedNetwork]\r\n : null;\r\n }\r\n\r\n getReceivingAddress() {\r\n return this.merchantConfig.getReceivingAddress();\r\n }\r\n\r\n // Updated to accept a specific token ID\r\n getTokenAmount(network, tokenKey) {\r\n console.log(\"Getting amount for network:\", network);\r\n console.log(\"Amounts object:\", this.amounts);\r\n const amount = this.amounts[network]?.[tokenKey] ?? this.amounts[network]?.stablecoin;\r\n return amount || 0;\r\n }\r\n}\r\n","import { networksConfig } from \"../utils/config\";\r\n\r\nexport class Config {\r\n constructor(options = {}) {\r\n this.receivingAddress = options.receivingAddress || \"\";\r\n this.blacklist = options.blacklist || [];\r\n this.amounts = options.Amounts || {}; // Note the capital 'A' in Amounts\r\n this.validateConfig();\r\n }\r\n\r\n validateConfig() {\r\n if (!this.receivingAddress) {\r\n throw new Error(\"Receiving address is required\");\r\n }\r\n // Validate stablecoin amounts\r\n for (const [network, tokens] of Object.entries(this.amounts)) {\r\n if (!networksConfig[network]) {\r\n throw new Error(`Invalid network: ${network}`);\r\n }\r\n if (\r\n !tokens.stablecoin ||\r\n typeof tokens.stablecoin !== \"number\" ||\r\n tokens.stablecoin <= 0\r\n ) {\r\n throw new Error(`Invalid stablecoin amount for network ${network}`);\r\n }\r\n }\r\n }\r\n\r\n getBlacklist() {\r\n return this.blacklist;\r\n }\r\n\r\n getReceivingAddress() {\r\n return this.receivingAddress;\r\n }\r\n\r\n // getTokenAmount(network, token) {\r\n // const networkConfig = networksConfig[network];\r\n // if (!networkConfig) return 0;\r\n\r\n // const stablecoinSymbol = networkConfig.tokens.stablecoin.symbol;\r\n\r\n // if (token === 'stablecoin') {\r\n // return this.amounts[network]?.stablecoin || 0;\r\n // }\r\n // // For native tokens, return 0 as it's not specified in the new structure\r\n // return 0;\r\n // }\r\n getTokenAmount(network) {\r\n console.log(\"Getting amount for network:\", network);\r\n console.log(\"Amounts object:\", this.amounts);\r\n\r\n // Directly return the stablecoin amount for the network\r\n const amount = this.amounts[network]?.stablecoin;\r\n console.log(\"Returning amount:\", amount);\r\n\r\n return amount || 0;\r\n }\r\n}\r\n\r\nexport default Config;\r\n"],"names":["networksConfig","sepolia","uri","chainId","stablecoins","id","name","protocol","contractAddress","baseAsset","symbol","decimals","isNative","address","stableCoin","isDirectTransfer","feeUI","Transaction","constructor","networkUri","stablecoinConfig","this","config","djedAddress","init","Error","web3","getWeb3","djedContract","getDjedContract","baseAssetContract","eth","Contract","coinArtifact","abi","reserveCoin","getCoinContracts","scDecimals","rcDecimals","getDecimals","oracleContract","getOracleAddress","then","addr","getOracleContract","_address","oracleAddress","contractError","console","error","getBlockchainDetails","web3Available","djedContractAvailable","stableCoinAddress","reserveCoinAddress","stableCoinDecimals","reserveCoinDecimals","oracleContractAvailable","baseAssetSymbol","baseAssetIsNative","baseAssetDecimals","handleTradeDataBuySc","amountScaled","tradeDataPriceBuySc","totalBCScaled","buyStablecoins","payer","receiver","value","UI","buyScTx","buyScIsisTx","approveBaseAsset","amount","approveTx","checkBaseAssetAllowance","owner","allowance","checkAllowance","BigInt","PayButton","onClick","size","sizeStyles","small","width","height","fontSize","medium","large","logoSizes","buttonStyle","logoStyle","React","createElement","className","styles","style","Dialog","children","onClose","dialogOverlay","pricingCard","dialogClose","pricingCardHeader","allianceLogo","stablepayTitle","pricingCardBody","TokenSelector","networkSelector","selectedToken","selectToken","tokenKey","foundToken","getAvailableTokens","find","t","key","getSelectedToken","networkConfig","getSelectedNetworkConfig","map","sc","resetSelection","NetworkContext","createContext","NetworkProvider","tokenSelector","useState","selectedNetwork","setSelectedNetwork","setSelectedToken","transactionDetails","setTransactionDetails","resetState","useEffect","Provider","selectNetwork","networkKey","token","resetSelections","useNetwork","context","useContext","undefined","NetworkDropdown","selectField","htmlFor","onChange","event","target","disabled","Object","keys","availableNetworks","TokenDropdown","loading","setLoading","setError","availableTokens","async","newValue","selectedStablecoin","transaction","tokenAmount","getTokenAmount","blockchainDetails","network","tokenSymbol","receivingAddress","getReceivingAddress","djedContractAddress","err","defineChain","nativeCurrency","rpcUrls","default","http","webSocket","blockExplorers","url","testnet","milkomeda","etcMainnet","WalletContext","switchToNetwork","window","ethereum","chainConfig","toString","chainName","blockExplorerUrls","getChainConfigForWallet","request","method","params","switchError","code","message","addError","WalletProvider","walletClient","setWalletClient","publicClient","setPublicClient","account","setAccount","setChainId","balance","setBalance","isConnecting","setIsConnecting","selectedChain","getChainByNetworkKey","expectedChainId","handleAccountsChangedRef","useRef","handleChainChangedRef","disconnectWalletInternal","useCallback","handleChainChanged","newChainId","parseInt","chainIdHex","newWalletClient","createWalletClient","chain","transport","custom","current","handleAccountsChanged","accounts","length","accountsHandler","chainHandler","removeListener","newPublicClient","createPublicClient","getBalance","parseFloat","Math","pow","disconnectWallet","connectPublicClient","connectWallet","on","ensureCorrectNetwork","NETWORK_SWITCH_DELAY_MS","Promise","resolve","setTimeout","newChainIdHex","freshWalletClient","prevNetworkRef","TransactionReview","onTransactionComplete","contextTransactionDetails","useWallet","setTransaction","txData","setTxData","setMessage","txHash","setTxHash","isApproved","setIsApproved","isApproving","setIsApproving","newTransaction","tradeDataAmount","String","tradeError","tradeAmount","initializeTransaction","txInstance","userAccount","amountStr","amountBigInt","parseUnits","hasAllowance","e","transactionReview","transactionInfo","transactionLabel","transactionValue","walletButton","slice","approveData","hash","sendTransaction","waitForTransactionReceipt","status","valueInUnits","finalTx","transactionLink","WidgetContent","buttonSize","handleClose","WidgetWithProviders","NetworkSelector","merchantConfig","blacklist","getBlacklist","getAvailableNetworks","entries","reduce","acc","includes","log","amounts","stablecoin","Config","options","Amounts","validateConfig","tokens","Widget","onSuccess","isDialogOpen","setIsDialogOpen","handleTransactionComplete","widgetContainer","handleOpenDialog","handleCloseDialog"],"mappings":"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"} \ No newline at end of file diff --git a/stablepay-sdk/package.json b/stablepay-sdk/package.json index 6b4f884..8a3d91b 100644 --- a/stablepay-sdk/package.json +++ b/stablepay-sdk/package.json @@ -6,7 +6,8 @@ "module": "dist/esm/index.js", "style": "dist/styles.css", "scripts": { - "build": "rollup -c" + "build": "rollup -c", + "prepare": "npm run build" }, "keywords": [], "author": "", diff --git a/stablepay-sdk/rollup.config.mjs b/stablepay-sdk/rollup.config.mjs index 57aa2ca..8214cd4 100644 --- a/stablepay-sdk/rollup.config.mjs +++ b/stablepay-sdk/rollup.config.mjs @@ -5,6 +5,7 @@ import terser from '@rollup/plugin-terser'; import babel from "@rollup/plugin-babel"; import postcss from "rollup-plugin-postcss"; import url from "@rollup/plugin-url"; + export default { input: "src/index.js", output: [ @@ -19,6 +20,7 @@ export default { file: "dist/umd/index.js", globals: { "djed-sdk": "DjedSdk", + "djed-sdk/src/artifacts/CoinABI.json": "coinArtifact", web3: "Web3", react: "React", "react-dom": "ReactDOM", @@ -29,7 +31,15 @@ export default { assetFileNames: "assets/[name][extname]", }, ], - external: ["djed-sdk", "web3", "react", "react-dom", "viem", "viem/chains"], + external: [ + "djed-sdk", + "djed-sdk/src/artifacts/CoinABI.json", + "web3", + "react", + "react-dom", + "viem", + "viem/chains" + ], plugins: [ resolve({ extensions: [".js", ".jsx"], @@ -43,7 +53,7 @@ export default { limit: 0, fileName: "[name][extname]", destDir: "dist/assets", - publicPath: "../assets/", //note:use relative path here + publicPath: "../assets/", emitFiles: true, }), postcss({ @@ -51,9 +61,7 @@ export default { extract: "styles.css", minimize: true, modules: true, - use: ["sass"], - url: { url: "rebase", }, diff --git a/stablepay-sdk/src/core/NetworkSelector.js b/stablepay-sdk/src/core/NetworkSelector.js index 171695d..638b31b 100644 --- a/stablepay-sdk/src/core/NetworkSelector.js +++ b/stablepay-sdk/src/core/NetworkSelector.js @@ -45,7 +45,11 @@ export class NetworkSelector { return this.merchantConfig.getReceivingAddress(); } - getTokenAmount(token) { - return this.merchantConfig.getTokenAmount(this.selectedNetwork, token); + // Updated to accept a specific token ID + getTokenAmount(network, tokenKey) { + console.log("Getting amount for network:", network); + console.log("Amounts object:", this.amounts); + const amount = this.amounts[network]?.[tokenKey] ?? this.amounts[network]?.stablecoin; + return amount || 0; } } diff --git a/stablepay-sdk/src/core/TokenSelector.js b/stablepay-sdk/src/core/TokenSelector.js index 6075f08..b2bf17c 100644 --- a/stablepay-sdk/src/core/TokenSelector.js +++ b/stablepay-sdk/src/core/TokenSelector.js @@ -1,5 +1,3 @@ -// TokenSelector.js - export class TokenSelector { constructor(networkSelector) { this.networkSelector = networkSelector; @@ -7,12 +5,11 @@ export class TokenSelector { } selectToken(tokenKey) { - const networkConfig = this.networkSelector.getSelectedNetworkConfig(); - if (networkConfig && networkConfig.tokens[tokenKey]) { - this.selectedToken = { - key: tokenKey, - ...networkConfig.tokens[tokenKey] - }; + const tokens = this.getAvailableTokens(); + const foundToken = tokens.find(t => t.key === tokenKey); + + if (foundToken) { + this.selectedToken = foundToken; return true; } return false; @@ -24,15 +21,21 @@ export class TokenSelector { getAvailableTokens() { const networkConfig = this.networkSelector.getSelectedNetworkConfig(); - if (!networkConfig) return []; + if (!networkConfig || !networkConfig.stablecoins) return []; - return Object.entries(networkConfig.tokens).map(([key, config]) => ({ - key, - ...config + // Map the stablecoins array to the UI format + return networkConfig.stablecoins.map(sc => ({ + key: sc.id, + name: sc.name, + symbol: sc.stableCoin.symbol, + baseAsset: sc.baseAsset.symbol, + isDirectTransfer: sc.stableCoin.isDirectTransfer, + // Store full config for Transaction usage + config: sc })); } resetSelection() { this.selectedToken = null; } -} \ No newline at end of file +} diff --git a/stablepay-sdk/src/core/Transaction.js b/stablepay-sdk/src/core/Transaction.js index eb72988..a93e769 100644 --- a/stablepay-sdk/src/core/Transaction.js +++ b/stablepay-sdk/src/core/Transaction.js @@ -1,9 +1,28 @@ -import { getWeb3, getDjedContract, getCoinContracts, getDecimals, getOracleAddress, getOracleContract, tradeDataPriceBuySc, buyScTx } from 'djed-sdk'; +import { + getWeb3, + getDjedContract, + getCoinContracts, + getDecimals, + getOracleAddress, + getOracleContract, + tradeDataPriceBuySc, + buyScTx, + buyScIsisTx, + checkAllowance, + approveTx +} from 'djed-sdk'; +import coinArtifact from 'djed-sdk/src/artifacts/CoinABI.json'; export class Transaction { - constructor(networkUri, djedAddress) { + /** + * @param {string} networkUri + * @param {object} stablecoinConfig - The specific config object for the selected stablecoin + */ + constructor(networkUri, stablecoinConfig) { this.networkUri = networkUri; - this.djedAddress = djedAddress; + this.config = stablecoinConfig; + this.djedAddress = stablecoinConfig.contractAddress; + this.baseAsset = stablecoinConfig.baseAsset; } async init() { @@ -14,61 +33,31 @@ export class Transaction { try { this.web3 = await getWeb3(this.networkUri); this.djedContract = getDjedContract(this.web3, this.djedAddress); + + // Initialize Base Asset contract if it is ERC20 + if (!this.baseAsset.isNative && this.baseAsset.address) { + this.baseAssetContract = new this.web3.eth.Contract(coinArtifact.abi, this.baseAsset.address); + } try { - const { stableCoin, reserveCoin } = await getCoinContracts(this.djedContract, this.web3); - const { scDecimals, rcDecimals } = await getDecimals(stableCoin, reserveCoin); - this.stableCoin = stableCoin; - this.reserveCoin = reserveCoin; - this.scDecimals = scDecimals; - this.rcDecimals = rcDecimals; + const { stableCoin, reserveCoin } = await getCoinContracts(this.djedContract, this.web3); + const { scDecimals, rcDecimals } = await getDecimals(stableCoin, reserveCoin); + this.stableCoin = stableCoin; + this.reserveCoin = reserveCoin; + this.scDecimals = scDecimals; + this.rcDecimals = rcDecimals; - this.oracleContract = await getOracleAddress(this.djedContract).then((addr) => - getOracleContract(this.web3, addr, this.djedContract._address) - ); + this.oracleContract = await getOracleAddress(this.djedContract).then((addr) => + getOracleContract(this.web3, addr, this.djedContract._address) + ); - this.oracleAddress = this.oracleContract._address; + this.oracleAddress = this.oracleContract._address; } catch (contractError) { console.error('[Transaction] Error fetching contract details:', contractError); - if (contractError.message && contractError.message.includes('execution reverted')) { - const getNetworkInfo = (uri) => { - if (uri.includes('milkomeda')) return { name: 'Milkomeda', chainId: '2001' }; - if (uri.includes('mordor')) return { name: 'Mordor Testnet', chainId: '63' }; - if (uri.includes('sepolia')) return { name: 'Sepolia', chainId: '11155111' }; - if (uri.includes('etc.rivet.link')) return { name: 'Ethereum Classic', chainId: '61' }; - return { name: 'the selected network', chainId: 'unknown' }; - }; - const { name: networkName, chainId } = getNetworkInfo(this.networkUri); - throw new Error( - `Failed to interact with Djed contract at ${this.djedAddress} on ${networkName}.\n\n` + - `Possible causes:\n` + - `- The contract address may be incorrect\n` + - `- The contract may not be deployed on ${networkName}\n` + - `- The contract may not be a valid Djed contract\n\n` + - `Please verify the contract address is correct for ${networkName} (Chain ID: ${chainId}).` - ); - } throw contractError; } } catch (error) { console.error('[Transaction] Error initializing transaction:', error); - if (error.message && (error.message.includes('CONNECTION ERROR') || error.message.includes('ERR_NAME_NOT_RESOLVED'))) { - const getNetworkName = (uri) => { - if (uri.includes('milkomeda')) return 'Milkomeda'; - if (uri.includes('mordor')) return 'Mordor'; - if (uri.includes('sepolia')) return 'Sepolia'; - return 'the selected network'; - }; - const networkName = getNetworkName(this.networkUri); - throw new Error( - `Failed to connect to ${networkName} RPC endpoint: ${this.networkUri}\n\n` + - `Possible causes:\n` + - `- The RPC endpoint may be temporarily unavailable\n` + - `- DNS resolution issue (check your internet connection)\n` + - `- Network firewall blocking the connection\n\n` + - `Please try again in a few moments or check the network status.` - ); - } throw error; } } @@ -83,16 +72,14 @@ export class Transaction { reserveCoinDecimals: this.rcDecimals, oracleAddress: this.oracleAddress || 'N/A', oracleContractAvailable: !!this.oracleContract, + baseAssetSymbol: this.baseAsset.symbol, + baseAssetIsNative: this.baseAsset.isNative, + baseAssetDecimals: this.baseAsset.decimals }; } async handleTradeDataBuySc(amountScaled) { - if (!this.djedContract) { - throw new Error("DJED contract is not initialized"); - } - if (typeof amountScaled !== 'string') { - throw new Error("Amount must be a string"); - } + if (!this.djedContract) throw new Error("DJED contract is not initialized"); try { const result = await tradeDataPriceBuySc(this.djedContract, this.scDecimals, amountScaled); return result.totalBCScaled; @@ -103,18 +90,37 @@ export class Transaction { } async buyStablecoins(payer, receiver, value) { - if (!this.djedContract) { - throw new Error("DJED contract is not initialized"); - } + if (!this.djedContract) throw new Error("DJED contract is not initialized"); + try { const UI = '0x0232556C83791b8291E9b23BfEa7d67405Bd9839'; - const txData = await buyScTx(this.djedContract, payer, receiver, value, UI, this.djedAddress); - - return txData; + if (!this.baseAsset.isNative) { + // Use Isis transaction builder for ERC20 + if (!this.baseAssetContract) throw new Error("Base Asset contract not initialized for ERC20 flow"); + return buyScIsisTx(this.djedContract, payer, receiver, value, UI, this.djedAddress); + } else { + // Use Standard transaction builder for Native + return buyScTx(this.djedContract, payer, receiver, value, UI, this.djedAddress); + } } catch (error) { console.error("Error executing buyStablecoins transaction: ", error); throw error; } } + + async approveBaseAsset(payer, amount) { + if (this.baseAsset.isNative) throw new Error("Cannot approve native asset"); + if (!this.baseAssetContract) throw new Error("No Base Asset contract to approve"); + + return approveTx(this.baseAssetContract, payer, this.djedAddress, amount); + } + + async checkBaseAssetAllowance(owner, amount) { + if (this.baseAsset.isNative) return true; + if (!this.baseAssetContract) return false; + + const allowance = await checkAllowance(this.baseAssetContract, owner, this.djedAddress); + return BigInt(allowance) >= BigInt(amount); + } } diff --git a/stablepay-sdk/src/utils/config.js b/stablepay-sdk/src/utils/config.js index 241ce96..7644f7f 100644 --- a/stablepay-sdk/src/utils/config.js +++ b/stablepay-sdk/src/utils/config.js @@ -1,60 +1,99 @@ -// src/utils/config.js +/** + * Final Generalized Configuration + * Supports multiple Djed deployments (Osiris, Isis, Tefnut) per network. + */ export const networksConfig = { 'sepolia': { uri: 'https://ethereum-sepolia.publicnode.com/', chainId: 11155111, - djedAddress: '0x624FcD0a1F9B5820c950FefD48087531d38387f4', - tokens: { - stablecoin: { - symbol: 'SOD', - address: '0x6b930182787F346F18666D167e8d32166dC5eFBD', - decimals: 18, - isDirectTransfer: true - }, - native: { - symbol: 'ETH', - decimals: 18, - isNative: true + stablecoins: [ + { + id: 'djed-eth-sepolia', + name: 'Djed (ETH Backed)', + protocol: 'djed', + contractAddress: '0x624FcD0a1F9B5820c950FefD48087531d38387f4', + baseAsset: { + symbol: 'ETH', + decimals: 18, + isNative: true, + address: null + }, + stableCoin: { + symbol: 'SOD', + address: '0x6b930182787F346F18666D167e8d32166dC5eFBD', + decimals: 18, + isDirectTransfer: true + } } - }, + ], feeUI: 0 }, 'milkomeda-mainnet': { uri: 'https://rpc-mainnet-cardano-evm.c1.milkomeda.com', chainId: 2001, - djedAddress: '0x67A30B399F5Ed499C1a6Bc0358FA6e42Ea4BCe76', - tokens: { - stablecoin: { - symbol: 'MOD', - address: '0xcbA90fB1003b9D1bc6a2b66257D2585011b004e9', - decimals: 18, - isDirectTransfer: true + stablecoins: [ + { + id: 'djed-mada', + name: 'Djed (mADA Backed)', + protocol: 'djed', + contractAddress: '0x67A30B399F5Ed499C1a6Bc0358FA6e42Ea4BCe76', + baseAsset: { + symbol: 'mADA', + decimals: 18, + isNative: true, + address: null + }, + stableCoin: { + symbol: 'MOD', + address: '0xcbA90fB1003b9D1bc6a2b66257D2585011b004e9', + decimals: 18, + isDirectTransfer: true + } }, - native: { - symbol: 'mADA', - decimals: 18, - isNative: true + { + id: 'djed-usdt-isis', + name: 'Djed (USDT Backed)', + protocol: 'isis', // Triggers ERC20 Approval logic + contractAddress: '0x0000000000000000000000000000000000000000', // Replace with live Isis address + baseAsset: { + symbol: 'USDT', + decimals: 6, + isNative: false, + address: '0x0000000000000000000000000000000000000000' // Replace with live USDT address + }, + stableCoin: { + symbol: 'iUSD', + address: '0x0000000000000000000000000000000000000000', + decimals: 18, + isDirectTransfer: true + } } - }, + ], feeUI: 0 }, 'ethereum-classic': { uri: 'https://etc.rivet.link', chainId: 61, - djedAddress: '0xCc3664d7021FD36B1Fe2b136e2324710c8442cCf', - tokens: { - stablecoin: { - symbol: 'ECSD', - address: '0x5A7Ca94F6E969C94bef4CE5e2f90ed9d4891918A', - decimals: 18, - isDirectTransfer: true - }, - native: { - symbol: 'ETC', - decimals: 18, - isNative: true + stablecoins: [ + { + id: 'djed-etc', + name: 'Djed (ETC Backed)', + protocol: 'djed', + contractAddress: '0xCc3664d7021FD36B1Fe2b136e2324710c8442cCf', + baseAsset: { + symbol: 'ETC', + decimals: 18, + isNative: true, + address: null + }, + stableCoin: { + symbol: 'ECSD', + address: '0x5A7Ca94F6E969C94bef4CE5e2f90ed9d4891918A', + decimals: 18, + isDirectTransfer: true + } } - }, + ], feeUI: 0 } }; \ No newline at end of file diff --git a/stablepay-sdk/src/widget/TokenDropdown.jsx b/stablepay-sdk/src/widget/TokenDropdown.jsx index d906c6f..646aa71 100644 --- a/stablepay-sdk/src/widget/TokenDropdown.jsx +++ b/stablepay-sdk/src/widget/TokenDropdown.jsx @@ -16,47 +16,53 @@ const TokenDropdown = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const handleTokenChange = async (event) => { - const newValue = event.target.value; + const handleTokenChange = async (event) => { + const newValue = event.target.value; // e.g., 'djed-eth' setError(null); setLoading(true); try { if (selectToken(newValue)) { const networkConfig = networkSelector.getSelectedNetworkConfig(); + + // Find the specific stablecoin object matching the selected ID + const selectedStablecoin = networkConfig.stablecoins.find( + (sc) => sc.id === newValue + ); + + if (!selectedStablecoin) { + throw new Error("Stablecoin configuration not found"); + } + + // Pass the full stablecoin object (replaces the removed djedAddress) const transaction = new Transaction( networkConfig.uri, - networkConfig.djedAddress + selectedStablecoin ); await transaction.init(); - const tokenAmount = networkSelector.getTokenAmount(newValue); + const tokenAmount = networkSelector.getTokenAmount(newValue); const blockchainDetails = transaction.getBlockchainDetails(); - let tradeData = null; - if (newValue === "native") { - tradeData = await transaction.handleTradeDataBuySc( - String(tokenAmount) - ); - } - setTransactionDetails({ network: selectedNetwork, token: newValue, - tokenSymbol: tokenSelector.getSelectedToken().symbol, + tokenSymbol: selectedStablecoin.stableCoin.symbol, amount: tokenAmount, receivingAddress: networkSelector.getReceivingAddress(), - djedContractAddress: networkConfig.djedAddress, - isDirectTransfer: - tokenSelector.getSelectedToken().isDirectTransfer || false, - isNativeToken: tokenSelector.getSelectedToken().isNative || false, - tradeAmount: tradeData ? tradeData.amount : null, + + // Use properties from the selected stablecoin object + djedContractAddress: selectedStablecoin.contractAddress, + isDirectTransfer: selectedStablecoin.stableCoin.isDirectTransfer || false, + baseAssetSymbol: selectedStablecoin.baseAsset.symbol, + baseAssetDecimals: selectedStablecoin.baseAsset.decimals, + baseAssetIsNative: selectedStablecoin.baseAsset.isNative, ...blockchainDetails, }); } } catch (err) { - console.error("Error fetching transaction details:", err); - setError("Failed to fetch transaction details. Please try again."); + console.error("Error updating transaction details:", err); + setError("Failed to initialize transaction."); } finally { setLoading(false); } diff --git a/stablepay-sdk/src/widget/TransactionReview.jsx b/stablepay-sdk/src/widget/TransactionReview.jsx index 23805f3..7950611 100644 --- a/stablepay-sdk/src/widget/TransactionReview.jsx +++ b/stablepay-sdk/src/widget/TransactionReview.jsx @@ -2,12 +2,13 @@ import React, { useState, useEffect } from "react"; import { useNetwork } from "../contexts/NetworkContext"; import { useWallet } from "../contexts/WalletContext"; import { Transaction } from "../core/Transaction"; -import { parseEther, encodeFunctionData, parseUnits } from "viem"; +import { parseUnits } from "viem"; import styles from "../styles/PricingCard.css"; const TransactionReview = ({ onTransactionComplete }) => { const { networkSelector, + tokenSelector, selectedNetwork, selectedToken, transactionDetails: contextTransactionDetails, @@ -18,26 +19,27 @@ const TransactionReview = ({ onTransactionComplete }) => { connectWallet, account, walletClient, - publicClient, - isConnecting, ensureCorrectNetwork, - expectedChainId, + isConnecting, } = useWallet(); const [transaction, setTransaction] = useState(null); - const [tradeDataBuySc, setTradeDataBuySc] = useState(null); const [txData, setTxData] = useState(null); const [message, setMessage] = useState(""); const [txHash, setTxHash] = useState(null); const [error, setError] = useState(null); const [isErrorDetailsVisible, setIsErrorDetailsVisible] = useState(false); + + // Approval States + const [isApproved, setIsApproved] = useState(false); + const [isApproving, setIsApproving] = useState(false); useEffect(() => { setTxData(null); - setTradeDataBuySc(null); setMessage(""); setError(null); setTxHash(null); + setIsApproved(false); }, [selectedNetwork, selectedToken]); useEffect(() => { @@ -46,24 +48,24 @@ const TransactionReview = ({ onTransactionComplete }) => { try { const networkConfig = networkSelector.getSelectedNetworkConfig(); - const receivingAddress = networkSelector.getReceivingAddress(); - const tokenAmount = networkSelector.getTokenAmount(selectedToken.key); - + const selectedTokenObj = tokenSelector.getSelectedToken(); + const stablecoinConfig = selectedTokenObj.config; + const newTransaction = new Transaction( networkConfig.uri, - networkConfig.djedAddress + stablecoinConfig ); + await newTransaction.init(); setTransaction(newTransaction); - let tradeData = null; - if (selectedToken.key === "native") { - try { - tradeData = await newTransaction.handleTradeDataBuySc(String(tokenAmount)); - setTradeDataBuySc(tradeData); - } catch (tradeError) { + const tokenAmount = networkSelector.getTokenAmount(selectedToken.key); + let tradeDataAmount = null; + + try { + tradeDataAmount = await newTransaction.handleTradeDataBuySc(String(tokenAmount)); + } catch (tradeError) { console.error("Error fetching trade data:", tradeError); - } } setTransactionDetails({ @@ -71,99 +73,103 @@ const TransactionReview = ({ onTransactionComplete }) => { token: selectedToken.key, tokenSymbol: selectedToken.symbol, amount: tokenAmount || "0", - receivingAddress, - djedContractAddress: networkConfig.djedAddress, - isDirectTransfer: selectedToken.isDirectTransfer || false, - isNativeToken: selectedToken.isNative || false, - tradeAmount: tradeData ? tradeData.amount : null, + receivingAddress: networkSelector.getReceivingAddress(), + tradeAmount: tradeDataAmount, ...newTransaction.getBlockchainDetails(), }); + + if (account) { + if (!stablecoinConfig.baseAsset.isNative && tradeDataAmount) { + checkAllowance(newTransaction, account, tradeDataAmount, stablecoinConfig.baseAsset.decimals); + } else if (stablecoinConfig.baseAsset.isNative) { + setIsApproved(true); + } + } + } catch (err) { console.error("Error initializing transaction:", err); } }; initializeTransaction(); - }, [selectedNetwork, selectedToken, networkSelector, setTransactionDetails]); + }, [selectedNetwork, selectedToken, networkSelector, account]); - if (!contextTransactionDetails) { - return
Initializing transaction...
; - } + const checkAllowance = async (txInstance, userAccount, amountStr, decimals) => { + try { + const amountBigInt = parseUnits(String(amountStr), decimals); + const hasAllowance = await txInstance.checkBaseAssetAllowance(userAccount, amountBigInt); + setIsApproved(hasAllowance); + } catch (e) { + console.error("Error checking allowance", e); + } + }; const handleConnectWallet = async () => { await connectWallet(); }; + + const handleApprove = async () => { + if (!transaction || !account) return; + setIsApproving(true); + setMessage("⏳ Approving token usage..."); + try { + const { tradeAmount, baseAssetDecimals } = contextTransactionDetails; + const amount = parseUnits(String(tradeAmount), baseAssetDecimals); + + const approveData = await transaction.approveBaseAsset(account, amount); + const freshWalletClient = await ensureCorrectNetwork(); + + const hash = await freshWalletClient.sendTransaction({ + ...approveData, + account + }); + + setMessage(`⏳ Approval Sent. Waiting...`); + + + const receipt = await walletClient.waitForTransactionReceipt({ hash }); + if (receipt.status === 'success') { + setIsApproved(true); + setMessage("✅ Approved! You can now send the payment."); + } else { + setMessage("❌ Approval transaction failed on-chain."); + } + setIsApproving(false); + + } catch(e) { + console.error(e); + setError(e); + setMessage("❌ Approval failed."); + setIsApproving(false); + } + }; - const handleSendTransaction = async () => { - if (!account || !contextTransactionDetails || !transaction) { - setMessage("❌ Wallet not connected or transaction details missing"); - return; - } + const handlePrepareTransaction = async () => { + if (!account || !transaction) return; try { setTxData(null); setError(null); setMessage("⏳ Preparing transaction..."); - const receiver = contextTransactionDetails.receivingAddress; - let builtTx; + const { tradeAmount, receivingAddress, baseAssetDecimals, baseAssetIsNative } = contextTransactionDetails; + const valueInUnits = parseUnits(String(tradeAmount), baseAssetDecimals); - if (selectedToken.key === "native") { - const UI = "0x0232556C83791b8291E9b23BfEa7d67405Bd9839"; - const amountToSend = tradeDataBuySc || "0"; - const valueInWei = parseEther(String(amountToSend)); - - builtTx = await transaction.buyStablecoins( - account, - receiver, - valueInWei, - UI - ); + const builtTx = await transaction.buyStablecoins( + account, + receivingAddress, + valueInUnits + ); - builtTx = { - ...builtTx, - value: valueInWei, - account: account, - }; + const finalTx = { ...builtTx, account: account }; + + if (baseAssetIsNative) { + finalTx.value = valueInUnits; } else { - const networkConfig = networkSelector.getSelectedNetworkConfig(); - const stablecoinAddress = networkConfig?.tokens?.stablecoin?.address; - - if (!stablecoinAddress) { - throw new Error('Stablecoin address not found in network configuration'); - } - - const amountToSend = contextTransactionDetails.amount - ? parseUnits( - String(contextTransactionDetails.amount), - contextTransactionDetails.stableCoinDecimals - ) - : "0"; - - builtTx = { - to: stablecoinAddress, - value: 0n, - data: encodeFunctionData({ - abi: [ - { - inputs: [ - { internalType: "address", name: "to", type: "address" }, - { internalType: "uint256", name: "amount", type: "uint256" }, - ], - name: "transfer", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "nonpayable", - type: "function", - }, - ], - functionName: "transfer", - args: [receiver, amountToSend], - }), - account: account, - }; + finalTx.value = 0n; // ERC20s send 0 value (uses data payload) } - setTxData(builtTx); + setTxData(finalTx); setMessage("✅ Transaction ready! Click 'Send Transaction' to proceed."); } catch (error) { setError(error); @@ -171,62 +177,14 @@ const TransactionReview = ({ onTransactionComplete }) => { } }; - const handleBuySc = async () => { - setError(null); - + const handleSendTransaction = async () => { try { - if (!account || !txData) { - setMessage("❌ Wallet account or transaction data is missing"); - return; - } - - if (!selectedNetwork) { - setMessage("❌ Network not selected"); - return; - } - - const networkConfig = networkSelector.getSelectedNetworkConfig(); - if (!networkConfig) { - setMessage("❌ Network configuration not found"); - return; - } - - setMessage("⏳ Verifying network..."); - + if (!account || !txData) return; + const freshWalletClient = await ensureCorrectNetwork(); - if (!freshWalletClient) { - setMessage("❌ Failed to switch to correct network. Please approve the network switch in MetaMask and try again."); - return; - } - - if (!window.ethereum) { - setMessage("❌ MetaMask not available"); - return; - } - - const chainIdHex = await window.ethereum.request({ method: 'eth_chainId' }); - const currentChainId = parseInt(chainIdHex, 16); - - if (currentChainId !== networkConfig.chainId) { - const errorMsg = `Network mismatch. MetaMask is on chain ${currentChainId}, but ${selectedNetwork} requires chain ${networkConfig.chainId}. Please switch networks in MetaMask.`; - setMessage(`❌ ${errorMsg}`); - setError(new Error(errorMsg)); - return; - } - - if (freshWalletClient.chain.id !== networkConfig.chainId) { - const errorMsg = `Wallet client chain mismatch. Wallet client is on chain ${freshWalletClient.chain.id}, but expected ${networkConfig.chainId}.`; - setMessage(`❌ ${errorMsg}`); - setError(new Error(errorMsg)); - return; - } - setMessage("⏳ Sending transaction..."); - - const txHash = await freshWalletClient.sendTransaction({ - ...txData, - account: account, - }); + + const txHash = await freshWalletClient.sendTransaction(txData); setTxHash(txHash); setMessage(`✅ Transaction sent!`); @@ -235,33 +193,15 @@ const TransactionReview = ({ onTransactionComplete }) => { onTransactionComplete({ txHash, network: selectedNetwork, - token: selectedToken?.key, - tokenSymbol: selectedToken?.symbol, amount: contextTransactionDetails?.amount, - receivingAddress: contextTransactionDetails?.receivingAddress, }); } } catch (error) { setError(error); setMessage(`❌ Transaction failed.`); - console.error('Transaction error:', error); } }; - const getExplorerUrl = () => { - if (!txHash || !selectedNetwork) return null; - - const explorerBaseUrls = { - "ethereum-classic": "https://blockscout.com/etc/mainnet/tx/", - "sepolia": "https://sepolia.etherscan.io/tx/", - "milkomeda-mainnet": "https://explorer-mainnet-cardano-evm.c1.milkomeda.com/tx/", - }; - - return explorerBaseUrls[selectedNetwork] - ? `${explorerBaseUrls[selectedNetwork]}${txHash}` - : null; - }; - return (
@@ -272,82 +212,41 @@ const TransactionReview = ({ onTransactionComplete }) => {
You Pay: - {selectedToken.key === "stablecoin" - ? `${contextTransactionDetails.amount} ${contextTransactionDetails.tokenSymbol}` - : `${tradeDataBuySc ? tradeDataBuySc : "Calculating..."} ${ - contextTransactionDetails.tokenSymbol - }`} + {contextTransactionDetails.tradeAmount + ? `${contextTransactionDetails.tradeAmount} ${contextTransactionDetails.baseAssetSymbol}` + : "Calculating..."}
- {account && !txData && ( - + )} + + {account && (contextTransactionDetails.baseAssetIsNative || isApproved) && !txData && ( + )} + {account && txData && ( - -)} - - - {message && ( -
- {message} - {error && ( - - )} -
- )} - - {isErrorDetailsVisible && error && ( -
-
{error.message}
-
+ )} + {message &&
{message}
} {txHash && ( -
- ✅ Transaction Hash:{" "} - {getExplorerUrl() ? ( - - {txHash.slice(0, 6)}...{txHash.slice(-6)} - - ) : ( - - {txHash} - - )} -
-)} - +
+ ✅ Transaction Hash: {txHash.slice(0, 10)}... +
+ )}
); };