@crocswap-libs/sdk
Version:
🛠🐊🛠 An SDK for building applications on top of CrocSwap
215 lines • 9.54 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CrocEthView = exports.CrocTokenView = void 0;
exports.sortBaseQuoteViews = sortBaseQuoteViews;
const tslib_1 = require("tslib");
const ethers_1 = require("ethers");
const constants_1 = require("./constants");
const context_1 = require("./context");
const utils_1 = require("./utils");
const token_1 = require("./utils/token");
/* General top-level class for interacting with specific ERC20 tokens. Handles functionality for
* approval, getting token balances both in wallet and on dex, and display/decimalization. */
class CrocTokenView {
/* Creates a new CrocTokenView for specificied token address.
*
* @param context The CrocContext environment context. Specific to a given chain.
* @param tokenAddr The address of the token contract. Use zero address for native ETH token. */
constructor(context, tokenAddr) {
this.context = context;
this.tokenAddr = tokenAddr;
this.isNativeEth = tokenAddr == ethers_1.ZeroAddress;
if (this.isNativeEth) {
this.decimals = Promise.resolve(18);
}
else {
this.decimals = this.resolve().then((c) => tslib_1.__awaiter(this, void 0, void 0, function* () { return c.decimals().then(Number); }));
}
}
/* Sends a signed transaction to approve the CrocSwap contract for the ERC20 token contract.
*
* @param approveQty Optional arugment to specify the quantity to approve. Defaults to 2^120
* if unspecified. */
approve(approveQty) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return this.approveAddr(yield (yield this.context).dex.getAddress(), approveQty);
});
}
approveRouter(approveQty) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let router = (yield this.context).router;
return router && this.approveAddr(yield router.getAddress(), approveQty);
});
}
approveAddr(addr, approveQty) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (this.isNativeEth) {
return undefined;
}
const weiQty = approveQty !== undefined ? yield this.normQty(approveQty) : ethers_1.MaxUint256;
yield (0, context_1.ensureChain)(yield this.context);
// We want to hardcode the gas limit, so we can manually pad it from the estimated
// transaction. The default value is low gas calldata, but Metamask and other wallets
// will often ask users to change the approval amount. Without the padding, approval
// transactions can run out of gas.
const gasEst = (yield this.resolveWrite()).approve.estimateGas(addr, weiQty);
return (yield this.resolveWrite()).approve(addr, weiQty, { gasLimit: (yield gasEst) + BigInt(15000), chainId: ((yield this.context).chain).chainId });
});
}
approveBypassRouter() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let router = (yield this.context).router;
if (!router) {
return undefined;
}
let abiCoder = new ethers_1.ethers.AbiCoder();
const MANY_CALLS = 1000000000;
const HOT_PROXY_IDX = 1;
const COLD_PROXY_IDX = 3;
const cmd = abiCoder.encode(["uint8", "address", "uint32", "uint16[]"], [72, router.address, MANY_CALLS, [HOT_PROXY_IDX]]);
yield (0, context_1.ensureChain)(yield this.context);
return (yield this.context).dex.userCmd(COLD_PROXY_IDX, cmd, { chainId: ((yield this.context).chain).chainId });
});
}
wallet(address_1) {
return tslib_1.__awaiter(this, arguments, void 0, function* (address, block = "latest") {
if (this.isNativeEth) {
return (yield this.context).provider.getBalance(address, block);
}
else {
return (yield this.resolve()).balanceOf(address, { blockTag: block });
}
});
}
walletDisplay(address_1) {
return tslib_1.__awaiter(this, arguments, void 0, function* (address, block = "latest") {
const balance = this.wallet(address, block);
return (0, token_1.toDisplayQty)(yield balance, yield this.decimals);
});
}
balance(address_1) {
return tslib_1.__awaiter(this, arguments, void 0, function* (address, block = "latest") {
return (yield this.context).query.querySurplus(address, this.tokenAddr, { blockTag: block });
});
}
balanceDisplay(address_1) {
return tslib_1.__awaiter(this, arguments, void 0, function* (address, block = "latest") {
const balance = this.balance(address, block);
return (0, token_1.toDisplayQty)(yield balance, yield this.decimals);
});
}
allowance(address, spender) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (this.isNativeEth) {
return constants_1.MAX_LIQ;
}
return (yield this.resolve()).allowance(address, spender ? spender : yield (yield this.context).dex.getAddress());
});
}
roundQty(qty) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (typeof qty === "number" || typeof qty === "string") {
return this.normQty(this.truncFraction(qty, yield this.decimals));
}
else {
return qty;
}
});
}
truncFraction(qty, decimals) {
if (typeof (qty) === "number") {
const exp = Math.pow(10, decimals);
return Math.floor(qty * exp) / exp;
}
else {
return this.truncFraction(parseFloat(qty), decimals);
}
}
normQty(qty) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (typeof qty === "number" || typeof qty === "string") {
return (0, token_1.fromDisplayQty)(qty.toString(), yield this.decimals);
}
else {
return qty;
}
});
}
toDisplay(qty) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (typeof qty === "number" || typeof qty === "string") {
return qty.toString();
}
else {
return (0, token_1.toDisplayQty)(qty, yield this.decimals);
}
});
}
resolve() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return (yield this.context).erc20Read.attach(this.tokenAddr);
});
}
resolveWrite() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return (yield this.context).erc20Write.attach(this.tokenAddr);
});
}
deposit(qty, recv) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return this.surplusOp(73, qty, recv, this.isNativeEth);
});
}
withdraw(qty, recv) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return this.surplusOp(74, qty, recv);
});
}
transfer(qty, recv) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return this.surplusOp(75, qty, recv);
});
}
surplusOp(subCode_1, qty_1, recv_1) {
return tslib_1.__awaiter(this, arguments, void 0, function* (subCode, qty, recv, useMsgVal = false) {
const abiCoder = new ethers_1.ethers.AbiCoder();
const weiQty = this.normQty(qty);
const cmd = abiCoder.encode(["uint8", "address", "uint128", "address"], [subCode, recv, yield weiQty, this.tokenAddr]);
const txArgs = useMsgVal ? { value: yield weiQty } : {};
let cntx = yield this.context;
yield (0, context_1.ensureChain)(cntx);
const gasEst = yield cntx.dex.userCmd.estimateGas(cntx.chain.proxyPaths.cold, cmd, txArgs);
Object.assign(txArgs, { gasLimit: gasEst + utils_1.GAS_PADDING, chainId: cntx.chain.chainId });
return cntx.dex.userCmd(cntx.chain.proxyPaths.cold, cmd, txArgs);
});
}
}
exports.CrocTokenView = CrocTokenView;
class CrocEthView extends CrocTokenView {
constructor(context) {
super(context, ethers_1.ZeroAddress);
}
/* Returns the amount needed to attach to msg.value when spending
* ETH from surplus collateral. (I.e. the difference between the
* two, or 0 if surplus collateral is sufficient) */
msgValOverSurplus(ethNeeded) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const sender = (yield this.context).senderAddr;
if (!sender) {
console.warn("No sender address known, returning 0");
return BigInt(0);
}
const ethView = new CrocTokenView(this.context, ethers_1.ZeroAddress);
const surpBal = yield ethView.balance(sender);
const hasEnough = surpBal > ethNeeded;
return hasEnough ? BigInt(0) :
ethNeeded - surpBal;
});
}
}
exports.CrocEthView = CrocEthView;
function sortBaseQuoteViews(tokenA, tokenB) {
return tokenA.tokenAddr.toLowerCase() < tokenB.tokenAddr.toLowerCase() ?
[tokenA, tokenB] : [tokenB, tokenA];
}
//# sourceMappingURL=tokens.js.map