import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { isAddress } from '@ethersproject/address'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { AbiItem } from 'web3-utils'; import { environment } from '../../../environments/environment'; import { BalanceContract, RecipientCommitment, SchnorrSignatureSet } from '../interfaces/balance-contract'; import { MethodCallback } from '../interfaces/callback'; import { InitData, InputParamsBalance, InputParamsBurnCost, InputParamsExchange, InputParamsPrice, OutputParamsBalance, } from '../interfaces/contract-service-methods'; import { PriceContract } from '../interfaces/price-contract'; import { TokenContract } from '../interfaces/token-contract'; import { ShifterContract } from '../interfaces/shifter-contract'; import { StorageContract } from '../interfaces/storage-contract' import { SwapRouterContract } from '../interfaces/swaprouter-contract' import { Weth9Contract } from '../interfaces/weth9-contract' import { ChainlinkContract } from '../interfaces/chainlink-contract' import { TokenSwapContract } from '../interfaces/tokenswap-contract' import { RewardContract } from '../interfaces/reward-contract'; import { QuoterV2Contract } from '../interfaces/quoterv2-contract' import { OracleContract } from '../interfaces/oracle-contract'; import { RewardUnlockedContract } from '../interfaces/reward-unlocked-contract' import { BaseService } from './base.service'; import { BALANCE_CONTRACT_ABI, PRICE_CONTRACT_ABI, TOKEN_CONTRACT_ABI, SHIFTER_CONTRACT_ABI, STORAGE_CONTRACT_ABI, AUSD_CONTRACT_ABI, SWAPROUTER_CONTRACT_ABI, WETH9_CONTRACT_ABI, CHAINLINK_CONTRACT_ABI, TOKENSWAP_CONTRACT_ABI, REWARDER_contract_abi, REWARDERUNLOCKED_CONTRACT_ABI, QUOTERV2_CONTRACT_ABI, V3STAKER_CONTRACT_ABI, ORACLE_CONTRACT_ABI } from './contracts-abi'; import { WalletService } from './wallet.service'; import { Contract, EventData } from 'web3-eth-contract'; import { BigNumberValue, valueToBigNumber } from '../helpers'; import { TokensName } from '../enums'; import { buffPedersenHash, randomBN, toFixedHex, parseNote, parseSaveNote } from './crypto'; import { Dictionary } from '@ngrx/entity'; import { Buffer } from 'buffer'; import { TOKENS_MAP } from '../helpers/index' import { local } from 'web3modal'; import { V3StakerContract } from '../interfaces/v3staker-contract'; const websnarkUtils = require('websnark-backup/src/utils') const buildGroth16 = require('websnark-backup/src/groth16') const stringifyBigInts = require('websnark-backup/tools/stringifybigint').stringifyBigInts const snarkjs = require('snarkjs-backup') const bigInt = snarkjs.bigInt const MerkleTree = require('fixed-merkle-tree-backup') const crypto = require('crypto') const subtle = globalThis.crypto.subtle const circomlib = require('circomlib-backup') const { toBN, keccak256, fromWei, randomHex, randomBytes } = require('web3-utils') const Web3 = require('web3') const CUT_LENGTH = 31 const CONTRACT_MAP: Map<string, Contract> = new Map<string, Contract>([]); const circuit = require("./withdraw.json") const req = new XMLHttpRequest(); const rbigint = (nbytes: number) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes)); let provingKey: any let groth16: any const loadKey = () => { return new Promise((resolve, reject) => { req.open("GET", "./assets/withdraw_proving_key.bin", true); req.responseType = "arraybuffer"; req.onload = (event) => { resolve(req.response); }; req.send(null); }) } @Injectable({ providedIn: 'root' }) export class ContractService extends BaseService { relayerConfig: any; constructor( store: Store, private readonly _http: HttpClient, private readonly _walletService: WalletService ) { super(store); } public isAddress = (address: string) => { return Web3.utils.isAddress(address); } public async relayWithdrawTransaction( saveNote: string, _recipient: string, shifterAddress: string, cb: MethodCallback = null ): Promise<any> { const { args, proof } = await this.prepareWithdraw(saveNote, _recipient, shifterAddress) // Post to the relay server const receipt = await this._http.post<any>(`${environment.relayerEndpoint}/relay`, { proof: proof, args: args, shifter: shifterAddress, callback: cb }).toPromise(); return receipt; } // Populates relayerConfig with data from config file public async getRelayerConfig() { return this._http.get(environment.relayerEndpoint + "/config") .toPromise() .then( data => this.relayerConfig = data ); } public getPrice(payload: InputParamsPrice): Observable<any> { return this._http.post<any>(this.apiUrl('getPrice'), payload); } public async getETHBalance(address: string): Promise<any> { return this._walletService.web3.eth.getBalance(address) } public async getDenomination(shifter: string): Promise<string> { const { methods } = this._getShifterContract(shifter); return await this.call(methods.denomination()) } public async getBurnCost(shifter: string, amount: BigInt, cb: MethodCallback = null): Promise<any> { const { methods } = this._getShifterContract(shifter); return await this.call(methods.getCost(amount), cb); } public async getCost(shifter: string, amount: BigInt, user: string, cb: MethodCallback = null): Promise<any> { const shifterMethods = this._getShifterContract(shifter).methods const chainlinkFeed = await shifterMethods.chainlinkFeed().call({ from: user }) const xftPool = await shifterMethods.xftPool().call({ from: user }) const { methods } = this._getOracleContract(); return methods.getCost(amount, chainlinkFeed, xftPool).call({from: user}, cb); } public async getCostSimpleShift(shifter: string, amount: BigInt, user: string, cb: MethodCallback = null): Promise<any> { const shifterMethods = this._getShifterContract(shifter).methods const chainlinkFeed = await shifterMethods.chainlinkFeed().call({ from: user }) const xftPool = await shifterMethods.xftPool().call({ from: user }) const tokenPool = await shifterMethods.tokenPool().call({ from: user }) const { methods } = this._getOracleContract(); return methods.getCostSimpleShift(amount, chainlinkFeed, xftPool, tokenPool).call({ from: user }, cb); } public async getBurnCostOracle(amount: BigInt, chainlinkFeed: string, xftPool: string, cb: MethodCallback = null): Promise<any> { const { methods } = this._getOracleContract(); return await this.call(methods.getCost(amount, chainlinkFeed, xftPool), cb); } public exchange(payload: InputParamsExchange): Observable<any> { return this._http.post<any>(this.apiUrl('exchange'), payload); } public balances(payload: InputParamsBalance): Observable<OutputParamsBalance[]> { return new Observable(); // stub the call return this._http.post<OutputParamsBalance[]>(this.apiUrl('balances'), payload); } public init(payload: InitData): Observable<any> { return new Observable(); // stub the call return this._http.post<any>(this.apiUrl('init'), payload); } public async approve( amount: string, address: string, user: string, cb: MethodCallback = null ): Promise<any> { const { methods } = this._getWETH9Contract() return methods.approve(address, amount).send({ from: user }, cb) } public async approveXFT( amount: string, address: string, tokenAddress: string, user: string, cb: MethodCallback = null ): Promise<any> { const { methods } = this._getTokenContract(tokenAddress) return methods.approve(address, amount).send({ from: user }, cb) } public async allowance( token: string, owner: string, spender: string, cb: MethodCallback = null, ): Promise<any> { const { methods } = this._getTokenContract(token); return this.call(methods.allowance(owner, spender), cb); } public async safeApprove( amount: string, token: string, address: string, user: string, cb: MethodCallback = null ): Promise<any> { const { methods } = this._getTokenContract(token) return methods.safeApprove(address, amount).send({ from: user }, cb) } public xcrypt = async (data: string, password: string, shifter: string, address: string, nonce: number = null) => { const { methods } = this._getStorageContract(); if (!nonce && nonce !== 0) nonce = (await methods.getDepositsLength(shifter, address, keccak256(password)).call()); const iv = (await subtle.digest('SHA-256', new Uint8Array(Buffer.from((password + nonce))))).slice(0, 16); const dataBuffer = new Uint8Array(Buffer.from(data.slice(2), 'hex')); const keyBuffer = new Uint8Array(Buffer.from(password.slice(2), 'hex')); const key = await subtle.importKey('raw', keyBuffer, { name: 'AES-GCM' }, false, ['encrypt']); try { let encrypted = await subtle.encrypt({ name: 'AES-GCM', iv }, key, dataBuffer); return "0x" + Buffer.from(encrypted).toString('hex').slice(0, 124); } catch (err) { throw new Error("The data provided couldn't be encrypted or decrypted, please check the inputs"); } } public async deposit( address: string, amount: BigNumberValue, currency: TokensName, cb: MethodCallback = null, ): Promise<any> { const { methods } = this._getBalanceContract(); const { amountIn, assetEnum: asset, commitment, message, aggregatePubKey: publicKey, aggregaterR: ecR, aggregateS: s, } = await this._http.post<any>(this.apiUrl('deposit'), { address, amount: valueToBigNumber(amount).toNumber(), currency }).toPromise(); const recipientCommitment: RecipientCommitment = { commitment, asset }; const schnorrSignatureSet: SchnorrSignatureSet = { message, publicKey, ecR, s }; return this.send(methods.deposit(amountIn, amount, recipientCommitment, schnorrSignatureSet), cb); } public async withdraw( address: string, amount: BigNumberValue, commitmentId: BigNumberValue, cb: MethodCallback = null, ): Promise<any> { const { message, aggregatePubKey: publicKey, aggregaterR: ecR, aggregateS: s, } = await this._http.post<any>(this.apiUrl('withdraw'), { address, amount: valueToBigNumber(amount).toNumber(), commitmentId: Number(commitmentId), }).toPromise(); const schnorrSignatureSet: SchnorrSignatureSet = { message, publicKey, ecR, s }; const { methods } = this._getBalanceContract(); return this.send(methods.withdraw(amount, Number(commitmentId), schnorrSignatureSet), cb); } // Get all notes for a given address public async getAggregateDeposits(shifters: string[], address: string, decrypted: boolean): Promise<any> { const { methods } = this._getStorageContract(); const password = this.getOrSetPasswordLocally(); // Should never be null let encryptedNotes: any[] = []; let decryptedNotes: any[] = []; let spentNotes: any[] = []; for (let shifter in shifters) { shifter = shifters[shifter]; if (password) { // There should always be a password set by the time we reach this let passwordHash = keccak256(password); let shifterNotes = await this.call(methods.getDeposits(shifter, address, passwordHash)); shifterNotes = shifterNotes.map((note: string, index: number) => { return { "note": note, "shifter": shifter, "index": index } }); try { encryptedNotes = encryptedNotes.concat(shifterNotes); } catch (e) { console.log(e) }; } else { throw ("No password set"); } } let nullHashArray; let boolSpentArray: string[]; let decryptedUnspentNotes; if (decrypted) { for (let index in encryptedNotes) { let thisNote = encryptedNotes[index]; let thisDecryptedNote = (await this.xcrypt(thisNote.note, password, thisNote.shifter, address, parseInt(thisNote.index))); let deposited = await this._getShifterContract(thisNote.shifter).methods.commitments(parseSaveNote(thisDecryptedNote).commitmentHex).call() let spent = await this._getShifterContract(thisNote.shifter).methods.isSpent(parseSaveNote(thisDecryptedNote).nullifierHex).call(); // Make sure the note is unspent if (!spent && deposited) decryptedNotes.push({ "note": thisDecryptedNote, "shifter": thisNote.shifter }) // Aggregates spent notes else if (spent && deposited) spentNotes.push({ "note": thisDecryptedNote, "shifter": thisNote.shifter }) } decryptedNotes = decryptedNotes .map((note, idx: number) => { return { "note": note.note, "shifter": note.shifter, "commitment": parseSaveNote(note.note).commitmentHex } }); spentNotes = spentNotes .map((note, idx: number) => { return { "note": note.note, "shifter": note.shifter, "commitment": parseSaveNote(note.note).commitmentHex } }); } return (decrypted ? { decryptedNotes, spentNotes } : encryptedNotes); } public async getLatestDeposit(shifter: string, address: string): Promise<any> { const { methods } = this._getStorageContract(); const password = this.getOrSetPasswordLocally() let encryptedNote; if (password) { try { let passwordHash = keccak256(password) encryptedNote = await this.call(methods.getLatestDeposit(shifter, address, passwordHash)) } catch (e) { console.log(e) }; } if (!encryptedNote) { console.log("No note found") return Promise.resolve(null); // No note found } console.log(`Found note: ${encryptedNote}`) console.log(`Decrypted note: ${await this.xcrypt(encryptedNote, password, shifter, address)}`) return this.xcrypt(encryptedNote, password, shifter, address) } public async depositAnon( /*amount: BigNumberValue, currency: TokensName,*/ address: string, shifterAddress: string, cb: MethodCallback = null, ): Promise<any> { let config = JSON.parse(localStorage.getItem('config')) let noStorage = config[localStorage.getItem('account')].noStorage const { methods } = this._getShifterContract(shifterAddress); const { saveNote, hexNote, commitmentHex, password } = this.prepareDeposit(); let passwordHash = keccak256(noStorage ? crypto.randomBytes(32) : password); const encryptedNote = await this.xcrypt(noStorage ? crypto.randomBytes(62) : saveNote, password, shifterAddress, address); let tx = await methods.deposit(commitmentHex, encryptedNote, passwordHash).send({ from: address, gas: 1300000, value: "50000000000000000" }, cb) // Download saveNote if (noStorage) this.download(saveNote, commitmentHex, shifterAddress); return tx; } public async withdrawAnon( saveNote: string, _recipient: string, shifterAddress: string, cb: MethodCallback = null ): Promise<any> { console.log("Save note: " + saveNote) const { methods } = this._getShifterContract(shifterAddress) const { args, proof } = await this.prepareWithdraw(saveNote, _recipient, shifterAddress) const { root, nullifierHash, recipient, relayer, fee, refund } = args return this.send(methods.withdraw( proof, root, nullifierHash, recipient, relayer, fee, refund ), cb) } public generatePassword = () => randomHex(32); public getOrSetPasswordLocally = () => { // add account object let config = JSON.parse(localStorage.getItem('config')) let acc = config[localStorage.getItem('account')] if (!acc.password) { acc.password = this.generatePassword(); } config[localStorage.getItem('account')] = acc; localStorage.setItem('config', JSON.stringify(config)) return acc.password } public prepareDeposit() { const secret = rbigint(31) const nullifier = rbigint(31) const preimage = Buffer.concat([nullifier.leInt2Buff(31), secret.leInt2Buff(31)]) const commitment = buffPedersenHash(preimage) const commitmentHex = toFixedHex(commitment) const saveNote = `0x${nullifier.toString(16, 'hex').padStart(62, '0')}${secret.toString(16, 'hex').padStart(62, '0')}`; const hexNote = `0x${preimage.toString('hex')}` const password = this.getOrSetPasswordLocally(); return { saveNote, hexNote, commitmentHex, password } } async prepareWithdraw( saveNote: string, recipient: string, shifterAddress: string, ): Promise<any> { const { note, tree, root } = await this.buildTree(saveNote, 20, shifterAddress) const { args, proof } = await this.createSnarkProof( root, note, tree, recipient, tree.indexOf(note.commitmentHex) ) return { args, proof } } async buildTree( saveNote: string, levels: number, shifterAddress: string, cb: MethodCallback = null ): Promise<any> { const note = parseSaveNote(saveNote) const { methods } = this._getShifterContract(shifterAddress); let leaves = await this.call(methods.commitmentList(), cb) const tree = new MerkleTree(levels, leaves) return { note, tree, root: tree.root() } } async createSnarkProof( root: string, note: any, tree: any, recipient: string, leafIndex: number, ): Promise<any> { const { pathElements, pathIndices } = tree.path(leafIndex) let relayer, fee, refund, chainId if (environment.useRelayer) { await this.getRelayerConfig(); relayer = this.relayerConfig.address fee = this.relayerConfig.fee refund = this.relayerConfig.refund chainId = this.relayerConfig.chainId // Not used yet } else { relayer = recipient fee = 0 refund = 0 chainId = 1 } const input = stringifyBigInts({ // public root: tree.root(), nullifierHash: note.nullifierHash, relayer, recipient: BigInt(recipient), fee, refund, // private nullifier: note.nullifier, secret: note.secret, pathElements: pathElements, pathIndices: pathIndices, }) if (!provingKey) provingKey = await loadKey(); if (!groth16) groth16 = await buildGroth16() let proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, provingKey) const { proof } = await websnarkUtils.toSolidityInput(proofData) const args = { root: toFixedHex(input.root), nullifierHash: toFixedHex(input.nullifierHash), recipient: toFixedHex(input.recipient, 20), relayer: toFixedHex(input.relayer, 20), fee: toFixedHex(input.fee), refund: toFixedHex(input.refund) } return { args, proof } } public async simpleShift( amount: string, recipient: string, shifterAddress: string, cb: MethodCallback = null ): Promise<any> { const { methods } = this._getShifterContract(shifterAddress) return methods.simpleShift(amount, recipient).send({ from: recipient, gas: 350000 }, cb) } public async isDeposited(saveNote: string, shifterAddress: string): Promise<string> { const { methods } = this._getShifterContract(shifterAddress) const depositObject = parseSaveNote(saveNote) console.log("Commitment hex: " + depositObject.commitmentHex) console.log("Is commitment deposited: " + await this.call(methods.commitments(depositObject.commitmentHex))) return this.call(methods.commitments(depositObject.commitmentHex)) } public async isSpent(saveNote: string, shifterAddress: string): Promise<string> { const { methods } = this._getShifterContract(shifterAddress); console.log(`The note being checked is: ${saveNote}`) console.log(`Note spent: ${await this.call(methods.isSpent(parseSaveNote(saveNote).nullifierHex))}`) return this.call(methods.isSpent(parseSaveNote(saveNote).nullifierHex)); } public async exactOutputSingle(params: string[], recipient: string, cb: MethodCallback = null): Promise<any> { const { methods } = this._getSwapRouterContract() let gas = await methods.exactOutputSingle(params).estimateGas({ from: recipient }, cb) const out = methods.exactOutputSingle(params).send({ from: recipient, gas: gas }, cb) return out } public async exactOutputSingleCall(params: string[], recipient: string, cb: MethodCallback = null): Promise<any> { const { methods } = this._getSwapRouterContract() const out = methods.exactOutputSingle(params).call({ from: recipient }, cb) return out } /* struct QuoteExactOutputSingleParams { address tokenIn; address tokenOut; uint256 amount; uint24 fee; uint160 sqrtPriceLimitX96; } */ public async quoteExactOutputSingle(params: string[], user: string, cb: MethodCallback = null): Promise<any> { const { methods } = this._getQuoterV2Contract(); return methods.quoteExactOutputSingle(params).call({ from: user }); } public async quoteExactInputSingle(params: string[], user: string, cb: MethodCallback = null): Promise<any> { const { methods } = this._getQuoterV2Contract(); return methods.quoteExactInputSingle(params).call({ from: user }); } public async wethDeposit(amount: string, user: string, cb: MethodCallback = null): Promise<any> { const { methods } = this._getWETH9Contract() return methods.deposit().send({ from: user, value: amount }, cb) } public async getChainlinkPriceETHUSD(amount: string, user: string, slippage: number, cb: MethodCallback = null): Promise<any> { const { methods } = this._getChainlinkContract("0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419") //Mainnet ETHUSD Chainlink feed let chainlinkPrice = await methods.latestAnswer().call({ from: user }, cb) const chainlinkDecimals = await methods.decimals().call({ from: user }, cb) let amountBigInt = BigInt(amount) // Putting the rate in the denominator so we can get ETH in the numerator let exchangeRate = (amountBigInt * BigInt(10 ** chainlinkDecimals)) / BigInt(chainlinkPrice) exchangeRate = exchangeRate * BigInt(100 + slippage) / BigInt(100) //Adds slippage amount to calculated price return exchangeRate.toString() } public async getChainlinkPriceBTCETH(amount: string, user: string, slippage: number, cb: MethodCallback = null): Promise<any> { const { methods } = this._getChainlinkContract("0xdeb288F737066589598e9214E782fa5A8eD689e8") //Mainnet BTCETH Chainlink feed let chainlinkPrice = await methods.latestAnswer().call({ from: user }, cb) const chainlinkDecimals = await methods.decimals().call({ from: user }, cb) let amountBigInt = BigInt(amount) // Rate goes in the numerator since BTCETH is the number of ETH per BTC let exchangeRate = (amountBigInt * BigInt(chainlinkPrice)) / BigInt(10 ** chainlinkDecimals) exchangeRate = exchangeRate * BigInt(100 + slippage) / BigInt(100) //Adds slippage amount to calculated price return exchangeRate.toString() } public async tokenSwap(user: string, cb: MethodCallback = null): Promise<any> { const { methods } = this._getTokenSwapContract() const gasEstimate = await methods.upgrade().estimateGas({ from: user }, cb) return methods.upgrade().send({ from: user, gas: gasEstimate }, cb) } public async getV3IncentiveKey(user: string): Promise<any> { const { methods } = this._getRewardContract(); const incentiveKey = await methods.incentiveKey().call({from: user}); return incentiveKey; } public async getIncentiveId(user: string): Promise<any> { const { methods } = this._getRewardContract(); const incentiveKey = await this.getV3IncentiveKey(user); const incentiveId = await methods.incentiveId().call({from: user}); return incentiveId; } public async incentives(user: string): Promise<any> { const { methods } = this._getV3StakerContract(); const incentiveId = await this.getIncentiveId(user); const incentives = await methods.incentives(incentiveId).call({from: user}); return incentives; } public async getAPY(user: string): Promise<any> { const incentives = await this.incentives(user); const incentiveKey = await this.getV3IncentiveKey(user); const interval = BigInt(incentiveKey.endTime - Math.ceil(Date.now()/1000)); const yearInSeconds = BigInt(365 * 24 * 3600); //one year, in seconds const quoteXFTWETH = await this.quoteExactInputSingle([ TOKENS_MAP["XFT"].contract, TOKENS_MAP["WETH"].contract, incentives.totalRewardUnclaimed.toString(), "3000", "0"], user); const quote = BigInt(quoteXFTWETH.amountOut.toString()); const rewardsPerYearETH = quote * yearInSeconds / interval; const ethBalPool = await this.getBalance(incentiveKey.pool, TOKENS_MAP["WETH"].contract); const anonUSDBalPool = await this.getBalance(incentiveKey.pool, TOKENS_MAP["anonUSD500"].contract); const anonUSDValue = await this.getRequiredETH(anonUSDBalPool.toString(), user); const poolValue = BigInt(anonUSDValue.toString()) + BigInt(ethBalPool.toString()); const poolValueFromWei = Number(fromWei(poolValue.toString())); const rewardsFromWei = Number(fromWei(rewardsPerYearETH.toString())); const apy = rewardsFromWei * 100 / poolValueFromWei; return apy.toString(); } public async getRewardInfo(tokenId: string, user: string): Promise<any> { const { methods } = this._getV3StakerContract(); const incentiveKey = await this.getV3IncentiveKey(user); const params = [incentiveKey.rewardToken, incentiveKey.pool, incentiveKey.startTime, incentiveKey.endTime, incentiveKey.refundee]; const getRewardInfo = await methods.getRewardInfo(params, tokenId).call({from: user}); return getRewardInfo; } public async groupCommitment( address: string, commitmentsId: BigNumberValue[], cb: MethodCallback = null, ): Promise<any> { const { assetEnum, commitment, pkSum, } = await this._http.post<any>(this.apiUrl('group'), { address, commitmentsId, }).toPromise(); const { methods } = this._getBalanceContract(); return this.send(methods.groupCommitment(commitmentsId, assetEnum, commitment, pkSum), cb); } public async groupCommitmentsSpent( address: string, hashTx: string, commitmentsId: BigNumberValue[], cb: MethodCallback = null, ): Promise<any> { return await this._http.post<any>(this.apiUrl('group', 'spent'), { address, hashTx, commitmentsId, }).toPromise(); } public async xftBalance(address: string): Promise<string> { const { methods } = this._getXFTContract(); return this.call(methods.balanceOf(address)); } public async usdBalance(address: string): Promise<string> { const { methods } = this._getUSDContract(); return this.call(methods.balanceOf(address)); } public async getBalance(userAddress: string, tokenAddress: string, cb: MethodCallback = null): Promise<string> { const { methods } = this._getTokenContract(tokenAddress) return methods.balanceOf(userAddress).call({ from: userAddress }, cb) } public async getLatestPrice(aggregator: number): Promise<{ price: string, decimals: number }> { const { methods } = this._getPriceContract(); const latestPrice = 0; const [price, decimals] = Object.values(latestPrice); return { price, decimals: Number(decimals) }; } public async getRequiredETH(amount: string, account: string): Promise<string> { const { methods } = this._getRewardContract(); return methods.getETHAmount(amount).call({ from: account }); } public async shift(amount: string, ether: string, account: string, cb: MethodCallback = null): Promise<void> { const { methods } = this._getRewardContract(); await methods.shift(amount).send({ from: account, value: ether }, cb); } public async addLiquidity(amount: string, ether: string, account: string, cb: MethodCallback = null): Promise<void> { const { methods } = this._getRewardUnlockedContract(); await methods.addLiquidity(amount).send({ from: account, value: ether }, cb); } public async liquidityUnlocked(user: string, cb: MethodCallback = null): Promise<boolean> { const { methods } = this._getRewardContract(); const expiry = await methods.expiry().call({from: user}); const blockNumber = await this._walletService.web3.eth.getBlockNumber(); return (Number(blockNumber) > Number(expiry)); } //withdraw the token from the contract public async liquidityWithdraw(tokenId: string, account: string): Promise<void> { const { methods } = this._getRewardContract(); methods.withdraw(tokenId).send({ from: account }); } public async liquidityWithdrawUnlocked(tokenId: string, account: string): Promise<void> { const { methods } = this._getRewardUnlockedContract(); methods.withdraw(tokenId).send({ from: account }); } // Get all the staked tokens in the contract that belong to the account public async queryTokenIds(accountAddress: string): Promise<any[]> { const contract = this._getRewardContract(); const stakerContract = this._getV3StakerContract(); const incentiveKey = await this.getV3IncentiveKey(accountAddress); const params = [incentiveKey.rewardToken, incentiveKey.pool, incentiveKey.startTime, incentiveKey.endTime, incentiveKey.refundee]; let events: any[] = []; contract.events.NFT_LOCKED({ filter: { owner: accountAddress }, fromBlock: 0, }, async function(error : any, event: EventData){ const deposit = await contract.methods.deposits(event.returnValues['tokenId']).call(); if (deposit.liquidity != 0){ events.push({ token: event.returnValues['tokenId'], locked: true, reward: fromWei((await stakerContract.methods.getRewardInfo(params, event.returnValues['tokenId']).call({from: accountAddress})).reward) }); } }); return events; } // Get all the staked tokens in the contract that belong to the account public async queryTokenIdsUnlocked(accountAddress: string): Promise<any[]> { const contract = this._getRewardUnlockedContract(); const stakerContract = this._getV3StakerContract(); const incentiveKey = await this.getV3IncentiveKey(accountAddress); const params = [incentiveKey.rewardToken, incentiveKey.pool, incentiveKey.startTime, incentiveKey.endTime, incentiveKey.refundee]; let events: any[] = []; contract.events.NFT_LOCKED({ filter: { owner: accountAddress }, fromBlock: 0, }, async function(error : any, event: EventData){ const deposit = await contract.methods.deposits(event.returnValues['tokenId']).call(); if (deposit.liquidity != 0){ events.push({ token: event.returnValues['tokenId'], locked: false, reward: fromWei((await stakerContract.methods.getRewardInfo(params, event.returnValues['tokenId']).call({from: accountAddress})).reward) }); } }); return events; } public download(note: string, commitment: string, shifter: string): void { let receipt = { "shifter": shifter, "commitment": commitment, "note": note } var file = new Blob([JSON.stringify(receipt)], { type: '.txt' }); var a = document.createElement("a"), url = URL.createObjectURL(file); a.href = url; const timestamp = Date.now(); a.download = `xft-note-${timestamp}` document.body.appendChild(a); a.click(); setTimeout(function () { document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 0); } private _getXFTContract(): TokenContract { const XFT = Object.values(TOKENS_MAP).find(value => value["zkSymbol"] === "XFT") const address = XFT["contract"] return this._getContractInstance(AUSD_CONTRACT_ABI, address); } private _getUSDContract(): TokenContract { const anonUSD = Object.values(TOKENS_MAP).find(value => value["name"] === "anonUSD") const address = anonUSD["contract"] return this._getContractInstance(AUSD_CONTRACT_ABI, address) } private _getTokenContract(address: string): TokenContract { return this._getContractInstance(AUSD_CONTRACT_ABI, address) } private _getPriceContract(): PriceContract { return this._getContractInstance(PRICE_CONTRACT_ABI, environment.priceContractAddress); } private _getBalanceContract(): BalanceContract { return this._getContractInstance(BALANCE_CONTRACT_ABI, environment.balanceContractAddress); } private _getShifterContract(addr: string = null): ShifterContract { return this._getContractInstance(SHIFTER_CONTRACT_ABI, addr); } private _getStorageContract(): StorageContract { return this._getContractInstance(STORAGE_CONTRACT_ABI, environment.storageContractAddress); } private _getSwapRouterContract(): SwapRouterContract { return this._getContractInstance(SWAPROUTER_CONTRACT_ABI, "0xE592427A0AEce92De3Edee1F18E0157C05861564"); // Hardcoded by Uniswap } private _getV3StakerContract(): V3StakerContract { return this._getContractInstance(V3STAKER_CONTRACT_ABI, "0xe34139463bA50bD61336E0c446Bd8C0867c6fE65"); //Hardcoded by Uniswap } private _getWETH9Contract(): Weth9Contract { return this._getContractInstance(WETH9_CONTRACT_ABI, "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") } private _getChainlinkContract(address: string): ChainlinkContract { return this._getContractInstance(CHAINLINK_CONTRACT_ABI, address) } private _getTokenSwapContract(): TokenSwapContract { return this._getContractInstance(TOKENSWAP_CONTRACT_ABI, environment.tokenSwapAddress) } private _getRewardContract(): RewardContract { return this._getContractInstance(REWARDER_contract_abi, environment.rewarderAddress); } private _getRewardUnlockedContract(): RewardUnlockedContract { return this._getContractInstance(REWARDERUNLOCKED_CONTRACT_ABI, environment.rewarderUnlockedAddress) } private _getOracleContract(): OracleContract { return this._getContractInstance(ORACLE_CONTRACT_ABI, environment.oracleAddress); } private _getQuoterV2Contract(): QuoterV2Contract { return this._getContractInstance(QUOTERV2_CONTRACT_ABI, "0x61fFE014bA17989E743c5F6cB21bF9697530B21e"); //Hardcoded uniswap deployment } private _getContractInstance<T extends Contract>(abi: AbiItem[], address: string): T { if (!isAddress(address)) { throw new Error('Contract address is not valid'); } const contractInstance = new this._walletService.web3.eth.Contract(abi, address); CONTRACT_MAP.set(address, contractInstance); return contractInstance as T; } protected override apiUrl(...path: string[]): string { let url = `${super.apiUrl()} `; if (path && Array.isArray(path)) { url = path.reduce((cur, prev) => { cur += `/ ${prev} `; return cur; }, url); } return url; } }