@brightunion/sdk
Version:
Brightjs is a set of javascript tools to interact with the Bright Union's risk platform aggregator's protocol on Ethereum, Binance Smart Chain and Polygon blockchains.
589 lines (477 loc) • 24.7 kB
text/typescript
import {
_getIERC20Contract,
_getDistributorsContract,
_getBridgeV2RegistryContract,
_getBridgeV2PolicyRegistry, _getPermitContract,
} from './helpers/getContract';
import { buyCoverNexus, buyCoverInsurace, buyCoverBridge, buyCoverEase } from "./dao/Buying";
import NetConfig from './config/NetConfig';
import InsuraceApi from './distributorsApi/InsuraceApi';
import ERC20Helper from './helpers/ERC20Helper';
import GoogleEvents from './config/GoogleEvents';
import axios from "axios";
import { toWei, toBN } from 'web3-utils';
export async function buyQuote(_quoteProtocol: any): Promise<any> {
GoogleEvents.buy(_quoteProtocol);
if(_quoteProtocol.distributorName == 'bridge'){
return await buyOnBridgeV2(_quoteProtocol);
}else if(_quoteProtocol.distributorName == 'nexus'){
return await buyOnNexus(_quoteProtocol);
}else if(_quoteProtocol.distributorName == 'insurace'){
return await buyOnInsurace(_quoteProtocol);
}else if(_quoteProtocol.distributorName == 'unore'){
return await buyOnUnoRe(_quoteProtocol);
}else if(_quoteProtocol.distributorName == 'ease'){
return await buyOnEase(_quoteProtocol);
}
}
export async function buyMultipleQuotes (_quotes:any):Promise<any> {
GoogleEvents.multiBuy(_quotes);
return buyMutipleOnInsurace(_quotes);
}
export async function buyMutipleOnInsurace (_quotes:any):Promise<any> {
const chainSymbol:string = NetConfig.netById(global.user.networkId).symbol;
// Confirm insurace quoted premium & get security signature params to buy
const confirmCoverResult:any = await InsuraceApi.confirmCoverPremium(chainSymbol, _quotes.params);
// Map Quote confirmation to Insurace buying object
const buyingObj:any = setInsuraceBuyingObject(confirmCoverResult);
if(NetConfig.isNetworkCurrencyBySymbol(_quotes.currency.name)){
return callInsurace(buyingObj, true , _quotes );
}else{
const netConfig:any = NetConfig.netById(global.user.networkId);
const erc20Address:string = netConfig[_quotes.currency.name];
const erc20Instance = _getIERC20Contract(erc20Address);
let account = global.user.account;
let ercBalance = await erc20Instance.methods.balanceOf(account).call();
const insuraceAddress:any = await _getDistributorsContract(global.user.web3).methods.getDistributorAddress('insurace').call();
if (NetConfig.sixDecimalsCurrency(global.user.networkId, _quotes.currency.name) && //6 digits currency?
Number(ERC20Helper.USDTtoERCDecimals(ercBalance)) >= (Number)(buyingObj.premium)) {
buyingObj.premium = ERC20Helper.USDTtoERCDecimals(buyingObj.premium);
//proceed with USDT
global.events.emit("buy" , { status: "CONFIRMATION" , type:"approve_spending" , count:2 , current:1 } );
return ERC20Helper.approveUSDTAndCall(
erc20Instance,
insuraceAddress,
buyingObj.premium,
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"reset_usdt_allowance" , count:3 , current:2 } );
},
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"get_transaction_hash" , count:2 , current:2 } );
},
() => {
buyingObj.premium = ERC20Helper.ERCtoUSDTDecimals(buyingObj.premium)
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main", count:2 , current:2 } );
return callInsurace(buyingObj, false, _quotes);
},
() => {
GoogleEvents.buyRejected('REJECTED - ERC20Helper - approveUSDTAndCall' , _quotes );
global.events.emit("buy" , { status: "REJECTED" } );
return {error: "Confirmation rejected"}
}
)
}else if (Number(ercBalance) >= (Number)(buyingObj.premium)) {
return await ERC20Helper.approveAndCall(
erc20Instance,
insuraceAddress, // global.user.brightProtoAddress //0x7e758e0D330B9B340A7282029e73dA448fb4BdB6
buyingObj.premium,
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"get_transaction_hash" , count:2 , current:2 } );
},
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main" , count:2 , current:2 } );
return callInsurace(buyingObj , false, _quotes);
},
(err:any) => {
GoogleEvents.buyRejected('REJECTED - ERC20Helper - approveAndCall' , _quotes );
global.events.emit("buy" , { status: "REJECTED" } );
return {error: err , message: 'ERC20Helper approveAndCall Error'};
}
);
} else{
GoogleEvents.buyRejected('You have insufficient funds to continue with this transaction' , _quotes );
global.events.emit("buy" , { status: "ERROR" , message:"You have insufficient funds to continue with this transaction" } );
return {error: 'You have insufficient funds to continue with this transaction' }
}
}
}
/**
* Buy on Insurace multi-currency handler method
*
* @param _quoteProtocol Quote to buy
*/
export async function buyOnInsurace (_quoteProtocol:any):Promise<any> {
const chainSymbol:string = NetConfig.netById(global.user.networkId).symbol;
// Confirm insurace quoted premium & get security signature params to buy
const confirmCoverResult:any = await InsuraceApi.confirmCoverPremium(chainSymbol, _quoteProtocol.rawData.params);
// Map Quote confirmation to Insurace buying object
const buyingObj = setInsuraceBuyingObject(confirmCoverResult);
// Check for user ETH balance
const netBalance = await global.user.web3.eth.getBalance(global.user.account);
global.events.emit("buy" , { status: "INITIALIZED"} );
const insuraceAddress:any = await _getDistributorsContract(global.user.web3).methods.getDistributorAddress('insurace').call();
if(NetConfig.isNetworkCurrencyBySymbol(_quoteProtocol.currency)){
if (Number(netBalance) >= (Number)(_quoteProtocol.price)) {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main", count:1 , current:1 } );
return callInsurace(buyingObj , true, _quoteProtocol);
} else {
GoogleEvents.buyRejected('You have insufficient funds to continue with this transaction' , _quoteProtocol );
global.events.emit("buy" , { status: "ERROR" , message:"You have insufficient funds to continue with this transaction" } );
return { error: "You have insufficient funds to continue with this transaction..." };
}
}else{
const netConfig:any = NetConfig.netById(global.user.networkId);
const erc20Address:string = netConfig[_quoteProtocol.currency];
const erc20Instance = _getIERC20Contract(erc20Address);
let account = global.user.account;
let ercBalance = await erc20Instance.methods.balanceOf(account).call();
// balance is enough?
if (NetConfig.sixDecimalsCurrency(global.user.networkId, _quoteProtocol.currency) && //6 digits currency?
Number(ERC20Helper.USDTtoERCDecimals(ercBalance)) >= (Number)(_quoteProtocol.price)) {
buyingObj.premium = ERC20Helper.USDTtoERCDecimals(buyingObj.premium)
//proceed with USDT
global.events.emit("buy" , { status: "CONFIRMATION" , type:"approve_spending" , count:2 , current:1 } );
return ERC20Helper.approveUSDTAndCall(
erc20Instance,
insuraceAddress,
buyingObj.premium,
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"reset_usdt_allowance" , count:3 , current:2 } );
},
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"get_transaction_hash" , count:2 , current:2 } );
},
() => {
buyingObj.premium = ERC20Helper.ERCtoUSDTDecimals(buyingObj.premium)
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main", count:2 , current:2 } );
return callInsurace(buyingObj, false, _quoteProtocol);
},
() => {
GoogleEvents.buyRejected('REJECTED - ERC20Helper - approveUSDTAndCall' , _quoteProtocol );
global.events.emit("buy" , { status: "REJECTED" } );
return {error: "Confirmation rejected"}
}
);
} else if (Number(ercBalance) >= (Number)(_quoteProtocol.price)) {
//proceed with ERC
global.events.emit("buy" , { status: "CONFIRMATION" , type:"approve_spending" , count:2 , current:1 } );
return await ERC20Helper.approveAndCall(
erc20Instance,
insuraceAddress, // global.user.brightProtoAddress //0x7e758e0D330B9B340A7282029e73dA448fb4BdB6
buyingObj.premium,
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"get_transaction_hash" , count:2 , current:2 } );
},
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main" , count:2 , current:2 } );
return callInsurace(buyingObj , false, _quoteProtocol);
},
(err:any) => {
GoogleEvents.buyRejected('REJECTED - ERC20Helper - approveAndCall' , _quoteProtocol );
global.events.emit("buy" , { status: "REJECTED" } );
return {error: err , message: 'ERC20Helper approveAndCall Error'};
});
} else {
GoogleEvents.buyRejected('You have insufficient funds to continue with this transaction' , _quoteProtocol );
global.events.emit("buy" , { status: "ERROR" , message:"You have insufficient funds to continue with this transaction" } );
return {error: 'You have insufficient funds to continue with this transaction' }
}
}
}
/**
* Contract Call to buy quote
* @param buyingObj
* @returns
*/
export async function callInsurace(buyingObj:any, buyingWithNetworkCurrency: boolean, _quote:any):Promise<any>{
return await buyCoverInsurace( buyingObj, buyingWithNetworkCurrency,_quote );
}
/**
* Specific buying struct for Insurace Contract
* @param confirmCoverResult
*
* @prop _products
* @prop _durationInDays
* @prop _amounts
* @prop _currency
* @prop _premiumAmount
* @prop _helperParameters
* @prop _securityParameters
* @prop _v
* @prop _r
* @prop _s
*
* @returns {Object} insurance buying struct
*/
function setInsuraceBuyingObject(confirmCoverResult:any){
return {
products: confirmCoverResult[0],
durationInDays: confirmCoverResult[1],
amounts: confirmCoverResult[2],
currency: confirmCoverResult[3][1],
addresses: confirmCoverResult[3],
premium: confirmCoverResult[4],
refCode: confirmCoverResult[5],
helperParameters: confirmCoverResult[6],
securityParameters: confirmCoverResult[7],
freeText: confirmCoverResult[8],
v: confirmCoverResult[9],
r: confirmCoverResult[10],
s: confirmCoverResult[11]
}
}
/**
* Buy on Nexus Mutual
* @param _quoteProtocol Quote to buy
*/
export async function callNexus(_quoteProtocol:any , buyingWithNetworkCurrency: boolean){
let net:any = NetConfig.netById(global.user.networkId);
let paymentAsset = net[_quoteProtocol.paymentCurrency]
return buyCoverNexus(
global.user.account,
'nexus',
paymentAsset,
_quoteProtocol.amount.toString(), // sum assured, compliant
_quoteProtocol.priceInNXM,
_quoteProtocol.priceWithSlippage,
_quoteProtocol.period, // period
_quoteProtocol.price.toString(), // token amount to cover with FEE
buyingWithNetworkCurrency,
_quoteProtocol,
)
}
export async function buyOnNexus(_quoteProtocol:any) : Promise<any>{
let buyingCurrency = _quoteProtocol.paymentCurrency;
let buyingAsset:any;
let requiredAmount = _quoteProtocol.price;
if (NetConfig.isNetworkCurrencyBySymbol(buyingCurrency)) {
buyingAsset = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
} else if (buyingCurrency === 'DAI') {
buyingAsset = NetConfig.netById(global.user.networkId).DAI;
} else if (buyingCurrency === 'NXM') {
buyingAsset = NetConfig.netById(global.user.networkId).NXM;
requiredAmount = _quoteProtocol.priceInNXM;
} else {
//Not supported yet
throw new Error();
}
global.events.emit("buy" , { status: "INITIALIZED"} );
const nexusVersion = _quoteProtocol.uniSwapRouteData.protocol ? 'nexus2' : 'nexus';
let spenderAddress:String;
if (buyingCurrency === 'NXM') {
spenderAddress = NetConfig.netById(global.user.networkId).nexusV2TokenController;
} else {
spenderAddress = await _getDistributorsContract(global.user.web3).methods.getDistributorAddress(nexusVersion).call();
}
console.log('Buying WITH ' + buyingCurrency);
if(!NetConfig.isNetworkCurrencyBySymbol(buyingCurrency)){
const erc20Instance = await _getIERC20Contract(buyingAsset);
const ercBalance = await erc20Instance.methods.balanceOf(global.user.account).call();
if (Number(ercBalance) >= (Number)(requiredAmount)) {
const currentAllowance = await erc20Instance.methods.allowance(global.user.account, spenderAddress).call();
if (toBN(currentAllowance.toString()).gte(toBN( requiredAmount.toString() ))) {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main" , count:2 , current:2 } );
return callNexus(_quoteProtocol, false);
} else {
const onTXHash = () => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"get_transaction_hash" , count:2 , current:2 } );
// return callNexus(_quoteProtocol, false);
};
const onSuccess = () => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main" , count:2 , current:2 } );
return callNexus(_quoteProtocol, false);
};
const onError = (err:any) => {
GoogleEvents.buyRejected('REJECTED - ERC20Helper - approveAndCall' , _quoteProtocol );
global.events.emit("buy" , { status: "REJECTED" } );
return {error: err , message: 'ERC20Helper approveAndCall Error'};
}
global.events.emit("buy" , { status: "CONFIRMATION" , type:"approve_spending" , count:2 , current:1 } );
return await ERC20Helper.approveAndCall( erc20Instance, spenderAddress, requiredAmount, onTXHash, onSuccess, onError);
}
} else {
GoogleEvents.buyRejected('You have insufficient funds to continue with this transaction' , _quoteProtocol );
global.events.emit("buy" , { status: "ERROR" , message:"You have insufficient funds to continue with this transaction" } );
return{ error: "You have insufficient funds to continue with this transaction" }
}
} else{
const netBalance = await global.user.web3.eth.getBalance(global.user.account);
if (Number(netBalance) >= (Number)(requiredAmount)) {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main" , count:1 , current:1 } );
return callNexus(_quoteProtocol, true);
} else {
GoogleEvents.buyRejected('You have insufficient funds to continue with this transaction' , _quoteProtocol );
global.events.emit("buy" , { status: "ERROR" , message:"You have insufficient funds to continue with this transaction" } );
return {error: 'You have insufficient funds to continue with this transaction' }
}
}
}
export async function callBridgeV2(_quoteProtocol:any){
const data = global.user.web3.eth.abi.encodeParameters(
['uint256', 'uint256'],
[_quoteProtocol.price, _quoteProtocol.period ],
);
return buyCoverBridge(
global.user.account,
'bridge',
_quoteProtocol.protocol.bridgeProductAddress, //bridge prod address
null, // payment asset
_quoteProtocol.amount.toString(), // sum assured, compliant
_quoteProtocol.actualPeriod, // period
null, //coverType
_quoteProtocol.price, // token amount to cover
data, // random data
null,
_quoteProtocol
)
}
export async function buyOnBridgeV2(_quoteProtocol:any) : Promise<any>{
const registry:any = await _getBridgeV2RegistryContract(NetConfig.netById(global.user.networkId).bridgeV2Registry, global.user.web3 )
let asset: any = await registry.methods.getUSDTContract().call().then((stableTokenAddr:any) => {
return _getIERC20Contract(stableTokenAddr).options.address
});
const policyRegistryAddr = await _getBridgeV2RegistryContract( NetConfig.netById(global.user.ethNet.networkId).bridgeV2Registry , global.user.ethNet.web3Instance).methods.getPolicyRegistryContract().call();
const policyRegistry = await _getBridgeV2PolicyRegistry(policyRegistryAddr, global.user.ethNet.web3Instance)
const nPolicies = await policyRegistry.methods.getPoliciesLength(global.user.account).call();
const activeInfos = await policyRegistry.methods.getPoliciesInfo(global.user.account, true, 0, nPolicies).call();
for (var i = 0; i < activeInfos._policyBooksArr.length; i++) {
if(activeInfos._policyBooksArr[i] == _quoteProtocol.protocol.bridgeProductAddress){
return await new Promise((reject) => {
global.events.emit("buy" , { status: "ERROR" , message: "Bridge does not support buying multiple active covers by the same address. Please check your expiry date on the existing cover." } );
reject( {error: "Bridge cover already exists" , message: "Bridge does not support buying multiple active policies by the same address. Please check your expiry date on the existing cover."} )
})
}
}
const erc20Instance = _getIERC20Contract(asset);
const ercBalance = await erc20Instance.methods.balanceOf(global.user.account).call();
global.events.emit("buy" , { status: "INITIALIZED"} );
if (Number(ERC20Helper.USDTtoERCDecimals(ercBalance)) >= (Number)(_quoteProtocol.price)) {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"approve_spending", count:2 , current:1 } );
return ERC20Helper.approveUSDTAndCall(
erc20Instance,
_quoteProtocol.protocol.bridgeProductAddress,
_quoteProtocol.price,
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"reset_usdt_allowance" , count:3 , current:2 } );
},
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"get_transaction_hash" , count:2 , current:2 } );
},
() => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main", count:2 , current:2 } );
return callBridgeV2(_quoteProtocol);
},
() => {
GoogleEvents.buyRejected('REJECTED - ERC20Helper - approveUSDTAndCall' , _quoteProtocol );
global.events.emit("buy" , { status: "REJECTED" } );
return {error: "Rejected confirmation"};
}
)
} else {
GoogleEvents.buyRejected('You have insufficient funds to continue with this transaction' , _quoteProtocol );
global.events.emit("buy" , { status: "ERROR" , message:"You have insufficient funds to continue with this transaction" } );
return {error: "You have insufficient funds to continue with this transaction"};
}
}
export async function buyOnEase(_quoteProtocol: any) : Promise<any> {
global.events.emit("buy" , { status: "INITIALIZED"} );
const erc20Instance = await _getIERC20Contract(_quoteProtocol.asset);
const token = await _getIERC20Contract(_quoteProtocol.vault.token.address);
const ercBalance = await erc20Instance.methods.balanceOf(global.user.account).call();
if (Number(ercBalance) >= (Number)(toWei(_quoteProtocol.amount))) {
const currentAllowance = await erc20Instance.methods.allowance(global.user.account, NetConfig.NETWORK_CONFIG[0].easeDistributor).call();
if (toBN(currentAllowance.toString()).gte(toBN(toWei(_quoteProtocol.amount).toString()))) {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main" , count:2 , current:2 } );
return callEase(_quoteProtocol, false);
} else {
const onTXHash = () => {
global.events.emit("buy", {status: "CONFIRMATION", type: "get_transaction_hash", count: 2, current: 2});
};
const onSuccess = () => {
global.events.emit("buy", {status: "CONFIRMATION", type: "main", count: 2, current: 2});
return callEase(_quoteProtocol, false);
};
const onError = (err: any) => {
GoogleEvents.buyRejected('REJECTED - ERC20Helper - approveAndCall', _quoteProtocol);
global.events.emit("buy", {status: "REJECTED"});
return {error: err, message: 'ERC20Helper approveAndCall Error'};
}
global.events.emit("buy" , { status: "CONFIRMATION" , type:"approve_spending" , count:2 , current:1 } );
return await ERC20Helper.approveAndCall( token, NetConfig.NETWORK_CONFIG[0].easeDistributor, toWei(_quoteProtocol.amount), onTXHash, onSuccess, onError);
}
} else {
GoogleEvents.buyRejected('You have insufficient funds to continue with this transaction' , _quoteProtocol );
global.events.emit("buy" , { status: "ERROR" , message:"You have insufficient funds to continue with this transaction" } );
return{ error: "You have insufficient funds to continue with this transaction" }
}
}
export async function callEase(_quoteProtocol: any, buyingWithNetworkCurrency: boolean) {
const nonce = await _getPermitContract('0xEA5edEF1A7106D9e2024240299DF3D00C7D94767').methods.nonces(global.user.account).call();
const data = {
chainId: _quoteProtocol.chainId,
vault: _quoteProtocol.vault.address,
user: global.user.account,
amount: toWei(_quoteProtocol.amount),
nonce: nonce
}
return axios.post('https://api.ease.org/api/v1/permits', data)
.then((response) => {
return buyCoverEase(
global.user.account,
_quoteProtocol.vault.address,
_quoteProtocol.asset, // payment asset
_quoteProtocol.amount, // sum assured, compliant
null, // period
0, //coverType
null, // token amount to cover with FEE
response.data ,// random data
_quoteProtocol,
)
// return response.data;
}).catch(error => {
// this.errorMessage = error.message;
console.error("There was an error!", error);
});
}
/**
* Buy on Nexus Mutual
* @param _quoteProtocol Quote to buy
*/
export async function callUnoRe(_quoteProtocol:any , buyingWithNetworkCurrency: boolean){
//TODO
}
export async function buyOnUnoRe(_quoteProtocol:any) : Promise<any>{
global.events.emit("buy" , { status: "INITIALIZED"} );
const asset:any = NetConfig.netById(global.user.networkId).USDC;
const erc20Instance = await _getIERC20Contract(asset);
const ercBalance = await erc20Instance.methods.balanceOf(global.user.account).call();
const unoReAddress:any = await _getDistributorsContract(global.user.web3).methods.getDistributorAddress('unore').call();
if (Number(ercBalance) >= (Number)(_quoteProtocol.price)) {
let allowanceAmount = _quoteProtocol.price;
const currentAllowance = await erc20Instance.methods.allowance(global.user.account, unoReAddress).call();
if (toBN(currentAllowance.toString()).gte(toBN( allowanceAmount.toString() ))) {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main" , count:2 , current:2 } );
return callUnoRe(_quoteProtocol, false);
} else {
const onTXHash = () => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"get_transaction_hash" , count:2 , current:2 } );
};
const onSuccess = () => {
global.events.emit("buy" , { status: "CONFIRMATION" , type:"main" , count:2 , current:2 } );
return callUnoRe(_quoteProtocol, false);
};
const onError = (err:any) => {
GoogleEvents.buyRejected('REJECTED - ERC20Helper - approveAndCall' , _quoteProtocol );
global.events.emit("buy" , { status: "REJECTED" } );
return {error: err , message: 'ERC20Helper approveAndCall Error'};
}
global.events.emit("buy" , { status: "CONFIRMATION" , type:"approve_spending" , count:2 , current:1 } );
return await ERC20Helper.approveAndCall( erc20Instance, unoReAddress, _quoteProtocol.price, onTXHash, onSuccess, onError);
}
} else {
GoogleEvents.buyRejected('You have insufficient funds to continue with this transaction' , _quoteProtocol );
global.events.emit("buy" , { status: "ERROR" , message:"You have insufficient funds to continue with this transaction" } );
return{ error: "You have insufficient funds to continue with this transaction" }
}
}
export default {buyQuote, buyMultipleQuotes }