Commit bde794d5 authored by XFT's avatar XFT
Browse files

.

parent 67aa780a
Pipeline #28 failed with stages
in 0 seconds
import { Contract } from 'web3-eth-contract';
import { CallReturn } from './callback';
export interface PriceContractMethods {
getLatestPrice(aggregator: number): CallReturn<any>;
}
export interface PriceContract extends Contract {
methods: PriceContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { CallReturn, SendReturn } from './callback';
export interface QuoterV2ContractMethods {
quoteExactOutputSingle(params: string[]): CallReturn<any>
quoteExactInputSingle(params: string[]): CallReturn<any>
}
export interface QuoterV2Contract extends Contract {
methods: QuoterV2ContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { SendReturn, CallReturn } from './callback';
export interface RewardContractMethods {
shift(amount: string): SendReturn<any>;
withdraw(tokenId: string): SendReturn<any>;
harvest(tokenId: string): SendReturn<any>;
chainlinkPrice(): CallReturn<any>;
getETHAmount(amount: string): CallReturn<any>;
deposits(tokenId: string): CallReturn<any>;
incentiveKey(): CallReturn<any>;
incentiveId(): CallReturn<any>;
expiry(): CallReturn<any>;
}
export interface RewardContract extends Contract {
methods: RewardContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { SendReturn, CallReturn } from './callback';
export interface RewardUnlockedContractMethods {
addLiquidity(amount: string): SendReturn<any>;
withdraw(tokenId: string): SendReturn<any>;
chainlinkPrice(): CallReturn<any>;
getETHAmount(amount: string): CallReturn<any>;
deposits(tokenId: string): CallReturn<any>;
incentiveKey(): CallReturn<any>;
incentiveId(): CallReturn<any>;
}
export interface RewardUnlockedContract extends Contract {
methods: RewardUnlockedContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { CallReturn, SendReturn } from './callback';
export interface ShifterContractMethods {
deposit(commitment: string, encryptedNote: string, passwordHash: string): SendReturn<any>;
withdraw(
proof: string,
root: string,
nullifierHash: string,
recipient: string,
relayer: string,
fee: string,
refund: string
): SendReturn<any>;
commitmentList(): CallReturn<any>;
commitments(commitment: string): CallReturn<any>;
simpleShift(amount: string, recipient: string): SendReturn<any>
getTokensForDenomination(denom: BigInt): CallReturn<any>
getCost(denom: BigInt): CallReturn<any>
isSpent(nullifierHash: string): CallReturn<any>
isSpentArray(nullifierHashes: string[]): CallReturn<any>
denomination(): CallReturn<any>
chainlinkFeed(): CallReturn<any>;
xftPool(): CallReturn<any>;
tokenPool(): CallReturn<any>;
}
export interface ShifterContract extends Contract {
methods: ShifterContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { CallReturn, SendReturn } from './callback';
export interface StorageContractMethods {
store(address: string, _encryptedNote: string): SendReturn<any>;
getLatestDeposit(mixer: string, address: string, passwordHash: string): CallReturn<any>;
getDeposits(mixer: string, address: string, passwordHash: string): CallReturn<any>;
getDepositsLength(mixer: string, address: string, passwordHash: string): CallReturn<any>;
getDepositByIndex(mixer: string, address: string, passwordHash: string, index: string): CallReturn<any>;
xcrypt(data: string, key: string): CallReturn<any>;
decrypt(
address: string,
index: string,
key: string): CallReturn<any>;
}
export interface StorageContract extends Contract {
methods: StorageContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { CallReturn, SendReturn } from './callback';
export interface SwapRouterContractMethods {
//exactOutputSingle(params: string[]): SendReturn<any>
exactOutputSingle(params: string[]): SendReturn<any>
}
export interface SwapRouterContract extends Contract {
methods: SwapRouterContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { CallReturn, SendReturn } from './callback';
export interface TokenContractMethods {
allowance(owner: string, spender: string): CallReturn<any>;
approve(accountAddress: string, amount: string): SendReturn<any>;
balanceOf(accountAddress: string): CallReturn<string>;
safeApprove(accountAddress: string, amount: string): SendReturn<any>
}
export interface TokenContract extends Contract {
methods: TokenContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { SendReturn } from './callback';
export interface TokenSwapContractMethods {
upgrade(): SendReturn<any>;
}
export interface TokenSwapContract extends Contract {
methods: TokenSwapContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { CallReturn, SendReturn } from './callback';
export interface V3StakerContractMethods {
getRewardInfo(key: string[], tokenId: string): CallReturn<any>;
incentives(incentiveId: string): CallReturn<any>;
}
export interface V3StakerContract extends Contract {
methods: V3StakerContractMethods;
}
import { Contract } from 'web3-eth-contract';
import { SendReturn } from './callback';
export interface Weth9ContractMethods {
deposit(): SendReturn<any>;
withdraw(amount: string): SendReturn<any>;
approve(spender: string, amount: string): SendReturn<any>;
}
export interface Weth9Contract extends Contract {
methods: Weth9ContractMethods;
}
export * from './pipes.module';
export * from './short-address.pipe';
import { Pipe, PipeTransform } from '@angular/core';
import { normalizeBN, BigNumberValue } from '../helpers';
@Pipe({
name: 'normalizeBN'
})
export class NormalizeBnPipe implements PipeTransform {
public transform(value: BigNumberValue = '0', decimals: number = 18): number {
return Number(normalizeBN(value, decimals).dp(5).toString(10));
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NormalizeBnPipe } from './normalize-bn.pipe';
import { ShortAddressPipe } from './short-address.pipe';
@NgModule({
declarations: [
ShortAddressPipe,
NormalizeBnPipe,
],
exports: [
ShortAddressPipe,
NormalizeBnPipe,
],
imports: [
CommonModule,
]
})
export class PipesModule {
}
import { Pipe, PipeTransform } from '@angular/core';
import { shortAddress, isTxHash, shortTxHash } from '../helpers';
@Pipe({
name: 'shortAddress'
})
export class ShortAddressPipe implements PipeTransform {
public transform(address: string): string {
return isTxHash(address) ? shortTxHash(address) : shortAddress(address);
}
}
import { Action, createReducer, on } from '@ngrx/store';
import {
GetAllowanceSuccess,
GetBalancesSuccess,
GetLatestPriceSuccess,
GetXFTBalanceSuccess,
GetUSDBalanceSuccess,
SetAccountAddress,
SetRouterParams,
} from './actions';
import { RouterState } from './index';
import { OutputParamsBalance } from './interfaces/contract-service-methods';
import { TOKENS_MAP } from './helpers';
import { TokensName } from './enums';
import { Console } from 'console';
export const coreKey = 'context';
export interface CoreState extends RouterState {
accountAddress: string;
xftBalance: string;
usdBalance: string;
allowance: string;
balances: OutputParamsBalance[];
assetPrice: {
price: string;
decimals: number;
};
}
export const initialState: CoreState = {
accountAddress: undefined,
balances: undefined,
xftBalance: undefined,
usdBalance: undefined,
allowance: undefined,
assetPrice: undefined,
params: undefined,
queryParams: undefined,
url: undefined,
};
const _coreReducer = createReducer(
initialState,
on(SetRouterParams, (state, { url, params, queryParams }): CoreState => {
return { ...state, url, params, queryParams };
}),
on(SetAccountAddress, (state, { accountAddress }): CoreState => {
return { ...state, accountAddress };
}),
// The actual balance state modifications happen here
on(GetXFTBalanceSuccess, (state, { xftBalance }): CoreState => {
TOKENS_MAP["XFT"]["balance"] = xftBalance;
return { ...state, xftBalance };
}),
on(GetUSDBalanceSuccess, (state, { usdBalance }): CoreState => {
Object.entries(TOKENS_MAP).forEach(([key, value]) => value["zkSymbol"] === ("anonUSD")?
TOKENS_MAP[key]["balance"] = usdBalance : TOKENS_MAP[key]["balance"] = "No USD");
return { ...state, usdBalance };
}),
on(GetLatestPriceSuccess, (state, { assetPrice }): CoreState => {
return { ...state, assetPrice };
}),
on(GetAllowanceSuccess, (state, { allowance }): CoreState => {
return { ...state, allowance };
}),
on(GetBalancesSuccess, (state, { balances }): CoreState => {
balances?.forEach(({ asset }) => {
if (asset.symbol && TOKENS_MAP[asset.symbol]) {
TOKENS_MAP[asset.symbol].balance = asset.amount;
}
});
return { ...state, balances };
}),
);
export function reducer(state: CoreState, action: Action): CoreState {
return _coreReducer(state, action);
}
import { Params, RouterStateSnapshot } from '@angular/router';
import { RouterStateSerializer } from '@ngrx/router-store';
import { RouterParams, RouterState, initialRouterParamsState } from './index';
export class RouterParamsSerializer implements RouterStateSerializer<RouterState> {
public serialize({ url, root }: RouterStateSnapshot): RouterState {
let route = root;
let queryParams: Params = {};
let params: RouterParams = initialRouterParamsState;
while (route.firstChild) {
route = route.firstChild;
params = { ...params, ...parseParams(route.params) };
queryParams = { ...queryParams, ...parseParams(route.queryParams) };
}
return { url, params, queryParams };
}
}
function parseParams(params: Params): Params {
const obj: Params = {};
for (const param in params) {
if (params.hasOwnProperty(param)) {
obj[param] = !params[param].startsWith('0x') && !isNaN(+params[param]) ? +params[param] : params[param];
}
}
return obj;
}
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { coreKey, CoreState } from './reducer';
import { normalizeBN, TOKENS_MAP, valueToBigNumber } from './helpers';
import { Tokens, TokensName } from './enums';
export const selectContextState = createFeatureSelector<CoreState>(coreKey);
export const selectContext = createSelector(
selectContextState,
(context) => context,
);
export const selectAccountAddress = createSelector(
selectContext,
(state) => state.accountAddress,
);
export const selectXFTBalance = createSelector(
selectContext,
(state) => state.xftBalance,
);
export const selectUSDBalance = createSelector(
selectContext,
(state) => state.usdBalance,
);
export const selectAssetPrice = createSelector(
selectContext,
(state) => state.assetPrice,
);
export const selectAllowance = createSelector(
selectContext,
(state) => state.allowance,
);
export const selectBalances = createSelector(
selectContext,
(state) => {
return state?.balances?.map((balance) => {
return {
...TOKENS_MAP[balance.asset.symbol],
...balance,
};
});
},
);
export const selectAssets = createSelector(
selectBalances,
(balances) => {
return Object.values(TOKENS_MAP)
.filter(({ zkSymbol }) => zkSymbol !== "XFT") //TokensName.XFT)
.map((token) => {
const balance = balances?.find((b) => b.asset.symbol === token.zkSymbol);
return {
...token,
asset: {
symbol: token.zkSymbol,
amount: balance ? balance.asset.amount : '0',
USD: balance ? balance.asset.USD : '0',
XFT: balance ? balance.asset.XFT : '0',
},
commitments: balance ? balance.commitments : [],
};
});
},
);
// Used in wallet component
export const selectXFTBalancesAmount = createSelector(
selectBalances,
(balances) => {
const amount = balances?.map((balance) => balance.asset.XFT)
.reduce((prev, cur) => {
prev = prev.plus(cur);
return prev;
}, valueToBigNumber(0));
return normalizeBN(amount, 18).dp(2).toString(10);
},
);
export const selectTokenBySymbol = (symbol: string) => {
return createSelector(
selectBalances,
(balances) => balances?.find(({ asset }) => symbol === asset.symbol),
);
};
export const selectChartData = createSelector(
selectBalances,
(balances) => {
const data = balances?.map((b) => {
return normalizeBN(b.asset.amount, 18)
.dp(5)
.toNumber();
});
return {
labels: Object.values(Tokens).map((symbol) => ` ${ symbol }`),
datasets: [
{
data,
label: '',
borderWidth: 0,
backgroundColor: [
'#5800B0',
'#6B39FA',
'#A98CFF',
'#D1C1FE',
],
hoverBackgroundColor: [
'#5800B0',
'#6B39FA',
'#A98CFF',
'#D1C1FE',
],
},
],
};
},
);
import { Store } from '@ngrx/store';
import { firstValueFrom } from 'rxjs';
import { environment } from '../../../environments/environment';
import { State } from '../index';
import { CallReturn, MethodCallback, SendReturn } from '../interfaces/callback';
import { selectAccountAddress } from '../selectors';
export abstract class BaseService {
protected accountAddress!: string;
protected constructor(
protected readonly store: Store,
) {
}
protected apiUrl(): string {
return environment.apiUrl;
}
protected async call<T = any>(method: CallReturn<T>, cb: MethodCallback = null): Promise<T> {
const from = await firstValueFrom(this.store.select(selectAccountAddress));
return await method.call({ from }, cb);
}
protected async send<T = any>(method: SendReturn<T>, cb: MethodCallback = null): Promise<T> {
const from = await firstValueFrom(this.store.select(selectAccountAddress));
const gas = await method.estimateGas({ from });
return await method.send({ from, gas }, cb);
}
}
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').padEnd(62, '0')}${secret.toString(16, 'hex').padEnd(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;
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment