From 2f745f2216231b81e6c51f8696bdf74da0d991df Mon Sep 17 00:00:00 2001 From: CounterFire2023 <136581895+CounterFire2023@users.noreply.github.com> Date: Tue, 1 Aug 2023 18:45:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9senderc20token=E7=9A=84gas?= =?UTF-8?q?=E8=B4=B9=EF=BC=8C=20=E5=A2=9E=E5=8A=A0crypto=E8=B4=AD=E4=B9=B0?= =?UTF-8?q?token=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/abis/abiTokenMall.ts | 390 +++++++++++++++++++++++++++++++ src/api/PayApi.ts | 8 + src/config/cfg_421613_dev.js | 1 + src/services/PaySvr.ts | 11 + src/standards/ERC1155Standard.ts | 3 +- src/standards/ERC20Standard.ts | 16 +- src/standards/ERC721Standard.ts | 3 +- src/standards/JCStandard.ts | 72 ++++++ 8 files changed, 498 insertions(+), 6 deletions(-) create mode 100644 src/abis/abiTokenMall.ts diff --git a/src/abis/abiTokenMall.ts b/src/abis/abiTokenMall.ts new file mode 100644 index 0000000..b97719e --- /dev/null +++ b/src/abis/abiTokenMall.ts @@ -0,0 +1,390 @@ +import { AbiItem } from 'web3-utils'; + +export let abiTokenMall: AbiItem[] = [ + { + inputs: [ + { + internalType: 'address', + name: '_tokenAddress', + type: 'address', + }, + { + internalType: 'address', + name: '_seller', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'erc20', + type: 'address', + }, + ], + name: 'AddERC20Suppout', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'buyer', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'tokenAddress', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'currency', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'tokenAmount', + type: 'uint256', + }, + ], + name: 'BuyTransaction', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnershipTransferred', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'erc20', + type: 'address', + }, + ], + name: 'RemoveERC20Suppout', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'seller', + type: 'address', + }, + ], + name: 'SellerUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'tokenAddress', + type: 'address', + }, + ], + name: 'TokenAddressUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'currency', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'price', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'pricePre', + type: 'uint256', + }, + ], + name: 'UpdateTokenPrice', + type: 'event', + }, + { + inputs: [ + { + internalType: 'address', + name: 'erc20', + type: 'address', + }, + ], + name: 'addERC20Support', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'erc20Supported', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'executor', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'feeToAddress', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'prices', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'erc20', + type: 'address', + }, + ], + name: 'removeERC20Support', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'renounceOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'seller', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_feeToAddress', + type: 'address', + }, + ], + name: 'setFeeToAddress', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'tokenAddress', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'updateExecutor', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_tokenAddress', + type: 'address', + }, + ], + name: 'updateTokenAddress', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_seller', + type: 'address', + }, + ], + name: 'updateSeller', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'currency', + type: 'address', + }, + { + internalType: 'uint256', + name: 'price', + type: 'uint256', + }, + ], + name: 'updateTokenPrice', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'currency', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'buyToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +]; diff --git a/src/api/PayApi.ts b/src/api/PayApi.ts index 104de18..8d02ede 100644 --- a/src/api/PayApi.ts +++ b/src/api/PayApi.ts @@ -10,6 +10,14 @@ export function queryTokenUsdPrice(eth: string, chain: string, env: string) { const url = `${PAY_API_HOST}/pay/alchemy/crypto_price`; return POST_JSON(url, { token: eth, chain, env }); } +/** + * check if user can buy eth from treasury + * @returns + */ +export function checkBuyTreasury() { + const url = `${PAY_API_HOST}/pay/alchemy/can_i_buy_treasury`; + return GET_JSON(url); +} export function queryFiatList() { const url = `${PAY_API_HOST}/pay/alchemy/fait_list`; diff --git a/src/config/cfg_421613_dev.js b/src/config/cfg_421613_dev.js index 1339de2..b6ddb66 100644 --- a/src/config/cfg_421613_dev.js +++ b/src/config/cfg_421613_dev.js @@ -54,6 +54,7 @@ module.exports = { nftmall: '0x4d71dE0428328112A78D402A6fdd7ceD1C96B71e', gamemarket: '0x46e2C612756b702b3d68d89F97c88FFa725F6fab', gamemall: '0x1D058c7c7451c34BbfF9c0dF1C16b95C5d171d64', + tokenmall: '0x22557f3fe7fbA53C66a35a3D70527D014704fd4D', }, gasInfo: { nftApprove: 49340, diff --git a/src/services/PaySvr.ts b/src/services/PaySvr.ts index acb57d4..0d44ae2 100644 --- a/src/services/PaySvr.ts +++ b/src/services/PaySvr.ts @@ -119,4 +119,15 @@ export class PaySvr { } // end of google pay + + // begin of iOS purchase + public async queryIOSProducts(productIds: string[]) { + let str = await new NativeSvr().queryProducts(productIds.join(',')); + return str; + } + + public async beginIOSPurchase(productId: string, orderId: string) { + let result = await new NativeSvr().buyProduct(productId, orderId); + } + // end of iOS purchase } diff --git a/src/standards/ERC1155Standard.ts b/src/standards/ERC1155Standard.ts index 820e9c9..9387f90 100644 --- a/src/standards/ERC1155Standard.ts +++ b/src/standards/ERC1155Standard.ts @@ -3,6 +3,7 @@ import { abiERC1155 } from '../abis/abiERC1155'; import { universalChainCb } from '../util/chain.util'; import { timeoutFetch } from '../util/net.util'; import { getFormattedIpfsUrl } from '../util/wallet.util'; +import { GAS_BOOST } from '../config/constants'; export const ERC1155 = 'ERC1155'; export const ERC1155_INTERFACE_ID = '0xd9b67a26'; @@ -181,7 +182,7 @@ export class ERC1155Standard { if (!gas) { gas = await contract.methods.safeBatchTransferFrom(from, to, tokenIds, amounts, []).estimateGas(); } - gas = (gas * 1.1) | 1; + gas = (gas * GAS_BOOST) | 1; if (estimate) { return jc.wallet.generateGasShow(gas); } diff --git a/src/standards/ERC20Standard.ts b/src/standards/ERC20Standard.ts index 2a59315..3ddbec2 100644 --- a/src/standards/ERC20Standard.ts +++ b/src/standards/ERC20Standard.ts @@ -3,6 +3,7 @@ import { abiERC20 } from '../abis/abiERC20'; import { BN, toUtf8 } from 'ethereumjs-util'; import { universalChainCb } from '../util/chain.util'; import { toWeiBn } from '../util/number.util'; +import { GAS_BOOST } from '../config/constants'; export class ERC20Standard { private web3: Web3; @@ -142,7 +143,7 @@ export class ERC20Standard { from, to, amount, - decimal = 18, + decimal, gas, estimate, }: { @@ -150,16 +151,23 @@ export class ERC20Standard { from: string; to: string; amount: number | string; - decimal?: number; + decimal?: string; gas?: number; estimate: number; }) { const contract = new this.web3.eth.Contract(abiERC20, address); - let amountBN = toWeiBn(amount, decimal); + if (!decimal) { + decimal = await this.getTokenDecimals(address); + } + let amountBN = toWeiBn(amount, +decimal); if (!gas) { gas = await contract.methods.transfer(to, '0').estimateGas(); + let toBalance = await this.getBalanceOf(address, to); + if (toBalance.eq(new BN(0))) { + gas += 12000; // SSTORE new value is more expensive + } } - gas = (gas * 1.1) | 1; + gas = (gas * GAS_BOOST) | 1; if (estimate) { return jc.wallet.generateGasShow(gas); } diff --git a/src/standards/ERC721Standard.ts b/src/standards/ERC721Standard.ts index a1e2fe4..03e64b4 100644 --- a/src/standards/ERC721Standard.ts +++ b/src/standards/ERC721Standard.ts @@ -3,6 +3,7 @@ import { abiERC721 } from '../abis/abiERC721'; import { universalChainCb } from '../util/chain.util'; import { timeoutFetch } from '../util/net.util'; import { getFormattedIpfsUrl } from '../util/wallet.util'; +import { GAS_BOOST } from '../config/constants'; export const ERC721 = 'ERC721'; export const ERC721_INTERFACE_ID = '0x80ac58cd'; @@ -278,7 +279,7 @@ export class ERC721Standard { if (!gas) { gas = await contract.methods.safeTransferFrom(from, to, tokenId).estimateGas(); } - gas = (gas * 1.1) | 1; + gas = (gas * GAS_BOOST) | 1; if (estimate) { return jc.wallet.generateGasShow(gas); } diff --git a/src/standards/JCStandard.ts b/src/standards/JCStandard.ts index 39f60d1..3918fa3 100644 --- a/src/standards/JCStandard.ts +++ b/src/standards/JCStandard.ts @@ -10,6 +10,7 @@ import { ZError } from '../common/ZError'; import { abiERC20 } from '../abis/abiERC20'; import { abiERC721 } from '../abis/abiERC721'; import { toBN } from '../util/number.util'; +import { abiTokenMall } from '../abis/abiTokenMall'; export class JCStandard { private web3: Web3; @@ -479,6 +480,77 @@ export class JCStandard { ); } // end of game item market + // begin of token Mall + /** + * + * @param values: [orderId, price, startTime, saltNonce] + * @returns + */ + async buyTokenWithErc20({ currency, amount, estimate }: { currency: string; amount: string; estimate: number }) { + const cfg = jc.wallet.currentChainCfg; + estimate = parseInt(estimate + ''); + const addressMall = cfg.contracts.tokenmall; + console.log('addressMall:: ', addressMall); + const contract = new this.web3.eth.Contract(abiTokenMall, addressMall, { + //@ts-ignore + from: jc.wallet.currentAccAddr, + }); + const tokenInstance = new this.web3.eth.Contract(abiERC20, currency, { + //@ts-ignore + from: jc.wallet.currentAccAddr, + }); + let approved = await tokenInstance.methods.allowance(jc.wallet.currentAccAddr, addressMall).call(); + console.log('approved:: ', approved); + let gasPrice = await jc.wallet.fetchGasPrice(); + console.log('gas price: ' + gasPrice); + let gasApprove = 0; + if (toBN(approved).lt(toBN(amount))) { + gasApprove = await tokenInstance.methods.approve(addressMall, amount).estimateGas(); + gasApprove = (gasApprove * GAS_BOOST) | 1; + await tokenInstance.methods.approve(addressMall, amount).send({ gas: gasApprove, gasPrice }); + } + let gas = await contract.methods.buyToken(currency, amount).estimateGas(); + gas = (gas * GAS_BOOST) | 1; + if (estimate) { + return jc.wallet.generateGasShow(gas + gasApprove); + } + + let details: any = [ + { + address: addressMall, + from: jc.wallet.currentAccAddr, + to: addressMall, + }, + { + address: currency, + from: jc.wallet.currentAccAddr, + to: addressMall, + value: amount, + }, + ]; + let tokenAddress = await contract.methods.tokenAddress().call(); + let currencyDecimals = await tokenInstance.methods.decimals().call(); + let price = await contract.methods.prices(currency).call(); + let tokenAmount = toBN(price) + .mul(toBN(amount)) + .mul(toBN(10).pow(toBN(10).sub(toBN(currencyDecimals)))) + .div(toBN('1000000')); + console.log(`buyTokenWithErc20: currency: ${currency}, amount: ${amount}, tokenAmount: ${tokenAmount}`); + details.push({ + address: tokenAddress, + from: addressMall, + to: jc.wallet.currentAccAddr, + value: tokenAmount, + }); + const logData = { + gas, + title: 'mall_buy_token', + details: details, + }; + return universalChainCb(logData, contract.methods.buyToken(currency, amount).send({ gas, gasPrice })); + } + // end of token Mall + // begin of NFT Mall /** *