viem
Version:
1,471 lines • 56.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.alwaysAllowPolicyId = void 0;
exports.configureExitSafePolicy = configureExitSafePolicy;
exports.validateExitSafePolicy = validateExitSafePolicy;
exports.deposit = deposit;
exports.depositSync = depositSync;
exports.depositShares = depositShares;
exports.depositSharesSync = depositSharesSync;
exports.privateDeposit = privateDeposit;
exports.privateDepositSync = privateDepositSync;
exports.waitForPrivateDeposit = waitForPrivateDeposit;
exports.getFeeState = getFeeState;
exports.getPosition = getPosition;
exports.getRedeemQuote = getRedeemQuote;
exports.getVault = getVault;
exports.getWithdrawQuote = getWithdrawQuote;
exports.redeem = redeem;
exports.redeemSync = redeemSync;
exports.privateRedeem = privateRedeem;
exports.privateRedeemSync = privateRedeemSync;
exports.waitForPrivateRedeem = waitForPrivateRedeem;
exports.withdrawExact = withdrawExact;
exports.withdrawExactSync = withdrawExactSync;
const ox_1 = require("ox");
const tempo_1 = require("ox/tempo");
const parseAccount_js_1 = require("../../accounts/utils/parseAccount.js");
const estimateContractGas_js_1 = require("../../actions/public/estimateContractGas.js");
const getBlockNumber_js_1 = require("../../actions/public/getBlockNumber.js");
const getLogs_js_1 = require("../../actions/public/getLogs.js");
const multicall_js_1 = require("../../actions/public/multicall.js");
const readContract_js_1 = require("../../actions/public/readContract.js");
const simulateContract_js_1 = require("../../actions/public/simulateContract.js");
const internal_Token = require("../../actions/token/internal.js");
const sendTransaction_js_1 = require("../../actions/wallet/sendTransaction.js");
const sendTransactionSync_js_1 = require("../../actions/wallet/sendTransactionSync.js");
const writeContractSync_js_1 = require("../../actions/wallet/writeContractSync.js");
const account_js_1 = require("../../errors/account.js");
const encodeAbiParameters_js_1 = require("../../utils/abi/encodeAbiParameters.js");
const getAbiItem_js_1 = require("../../utils/abi/getAbiItem.js");
const parseEventLogs_js_1 = require("../../utils/abi/parseEventLogs.js");
const getAddress_js_1 = require("../../utils/address/getAddress.js");
const isAddressEqual_js_1 = require("../../utils/address/isAddressEqual.js");
const observe_js_1 = require("../../utils/observe.js");
const poll_js_1 = require("../../utils/poll.js");
const withResolvers_js_1 = require("../../utils/promise/withResolvers.js");
const stringify_js_1 = require("../../utils/stringify.js");
const Abis = require("../Abis.js");
const Addresses = require("../Addresses.js");
const errors_js_1 = require("../errors.js");
const utils_js_1 = require("../internal/utils.js");
const policyActions = require("./policy.js");
const tokenActions = require("./token.js");
const zoneActions = require("./zone.js");
__exportStar(require("./earn/deployment.js"), exports);
exports.alwaysAllowPolicyId = 1n;
async function configureExitSafePolicy(client, parameters) {
const account_ = parameters.account ?? client.account;
if (!account_)
throw new account_js_1.AccountNotFoundError();
const account = (0, parseAccount_js_1.parseAccount)(account_);
const initialMembers = [
...new Set(parameters.initialMembers.map((member) => (0, getAddress_js_1.getAddress)(member))),
];
if (initialMembers.length === 0)
throw new Error('At least one initial policy member is required.');
const eligibility = await policyActions.createSync(client, {
account,
addresses: initialMembers,
chain: client.chain,
type: 'whitelist',
});
if (eligibility.receipt.status === 'pending')
return { receipt: eligibility.receipt };
const compoundPolicy = await (0, writeContractSync_js_1.writeContractSync)(client, {
account,
abi: Abis.tip403Registry,
address: Addresses.tip403Registry,
args: [exports.alwaysAllowPolicyId, eligibility.policyId, eligibility.policyId],
chain: client.chain,
functionName: 'createCompoundPolicy',
throwOnReceiptRevert: true,
});
if (compoundPolicy.status === 'pending')
return { receipt: compoundPolicy };
const [compoundEvent] = (0, parseEventLogs_js_1.parseEventLogs)({
abi: Abis.tip403Registry,
eventName: 'CompoundPolicyCreated',
logs: compoundPolicy.logs,
strict: true,
});
if (!compoundEvent)
throw new Error('`CompoundPolicyCreated` event not found.');
const tokenPolicy = await tokenActions.changeTransferPolicySync(client, {
account,
chain: client.chain,
policyId: compoundEvent.args.policyId,
token: parameters.shareToken,
});
if (tokenPolicy.receipt.status === 'pending')
return { receipt: tokenPolicy.receipt };
const policyAdmin = (0, isAddressEqual_js_1.isAddressEqual)(parameters.accessAdministrator, account.address)
? undefined
: await policyActions.setAdminSync(client, {
account,
admin: parameters.accessAdministrator,
chain: client.chain,
policyId: eligibility.policyId,
});
if (policyAdmin &&
policyAdmin.receipt.status === 'pending')
return { receipt: policyAdmin.receipt };
return {
policy: {
transferPolicyId: compoundEvent.args.policyId,
senderPolicyId: exports.alwaysAllowPolicyId,
recipientPolicyId: eligibility.policyId,
mintRecipientPolicyId: eligibility.policyId,
},
receipts: {
eligibilityPolicy: eligibility.receipt,
compoundPolicy,
tokenPolicy: tokenPolicy.receipt,
policyAdmin: policyAdmin?.receipt,
},
};
}
async function validateExitSafePolicy(client, parameters) {
const { accessAdministrator, policy, requiredMembers, shareToken, ...rest } = parameters;
const [tokenPolicyId, compound, simplePolicy, memberResults] = await Promise.all([
(0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.tip20,
address: shareToken,
functionName: 'transferPolicyId',
}),
(0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.tip403Registry,
address: Addresses.tip403Registry,
args: [policy.transferPolicyId],
functionName: 'compoundPolicyData',
}),
(0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.tip403Registry,
address: Addresses.tip403Registry,
args: [policy.recipientPolicyId],
functionName: 'policyData',
}),
Promise.all(requiredMembers.map(async (member) => {
const [recipient, mintRecipient] = await Promise.all([
(0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.tip403Registry,
address: Addresses.tip403Registry,
args: [policy.transferPolicyId, member],
functionName: 'isAuthorizedRecipient',
}),
(0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.tip403Registry,
address: Addresses.tip403Registry,
args: [policy.transferPolicyId, member],
functionName: 'isAuthorizedMintRecipient',
}),
]);
return { member, mintRecipient, recipient };
})),
]);
if (tokenPolicyId !== policy.transferPolicyId)
throw new Error('Earn share token transfer policy mismatch.');
if (compound[0] !== policy.senderPolicyId ||
compound[1] !== policy.recipientPolicyId ||
compound[2] !== policy.mintRecipientPolicyId)
throw new Error('TIP-403 compound policy components mismatch.');
if (policy.senderPolicyId !== exports.alwaysAllowPolicyId)
throw new Error('TIP-403 sender policy is not always allow.');
if (policy.recipientPolicyId !== policy.mintRecipientPolicyId)
throw new Error('TIP-403 recipient and mint-recipient policies must match.');
if (simplePolicy[0] !== 0)
throw new Error('TIP-403 eligibility policy is not a whitelist.');
if (!(0, isAddressEqual_js_1.isAddressEqual)(simplePolicy[1], accessAdministrator))
throw new Error('TIP-403 access administrator mismatch.');
const unauthorized = memberResults.find((result) => !result.recipient || !result.mintRecipient);
if (unauthorized)
throw new Error(`Required TIP-403 member is unauthorized: ${unauthorized.member}`);
}
async function deposit(client, parameters) {
return deposit.inner(sendTransaction_js_1.sendTransaction, client, parameters);
}
(function (deposit) {
async function inner(action, client, parameters) {
const [args, assetToken] = await Promise.all([
toDepositArgs(client, parameters),
(0, readContract_js_1.readContract)(client, {
abi: Abis.earnVault,
address: parameters.vault,
functionName: 'asset',
}),
]);
return (await action(client, {
...parameters,
calls: deposit.calls({ ...args, assetToken }),
}));
}
deposit.inner = inner;
function call(...parameters) {
const [, args] = (0, utils_js_1.resolveCallParameters)(parameters);
const { recipient, vault } = args;
const shareAmountMin = (() => {
if (args.shareAmountMin !== undefined)
return args.shareAmountMin;
return tempo_1.EarnShares.minimumOutput(args.shareAmount, args.slippageBps);
})();
return (0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'deposit',
args: [
internal_Token.toBaseUnits(args.assetAmount, undefined),
recipient,
shareAmountMin,
],
});
}
deposit.call = call;
function calls(args) {
const { assetToken, vault } = args;
const assetAmount = internal_Token.toBaseUnits(args.assetAmount, undefined);
return [
(0, utils_js_1.defineCall)({
address: tempo_1.TokenId.toAddress(assetToken),
abi: Abis.tip20,
functionName: 'approve',
args: [vault, assetAmount],
}),
deposit.call({ ...args, assetAmount }),
];
}
deposit.calls = calls;
function extractEvent(logs, parameters) {
const { vault } = parameters;
const [log] = (0, parseEventLogs_js_1.parseEventLogs)({
abi: Abis.earnVault,
eventName: 'Deposited',
logs: logs.filter((log) => (0, isAddressEqual_js_1.isAddressEqual)(log.address, vault)),
});
if (!log)
throw new Error('`Deposited` event not found.');
return log;
}
deposit.extractEvent = extractEvent;
async function estimateGas(client, parameters) {
return (0, estimateContractGas_js_1.estimateContractGas)(client, {
...(0, utils_js_1.pickWriteParameters)(parameters),
...deposit.call(await toDepositArgs(client, parameters)),
});
}
deposit.estimateGas = estimateGas;
async function simulate(client, parameters) {
return (0, simulateContract_js_1.simulateContract)(client, {
...(0, utils_js_1.pickWriteParameters)(parameters),
...deposit.call(await toDepositArgs(client, parameters)),
});
}
deposit.simulate = simulate;
})(deposit || (exports.deposit = deposit = {}));
async function depositSync(client, parameters) {
const { throwOnReceiptRevert = true, vault } = parameters;
const receipt = await deposit.inner(sendTransactionSync_js_1.sendTransactionSync, client, {
...parameters,
throwOnReceiptRevert,
});
if (receipt.status === 'pending')
return { receipt };
const { args } = deposit.extractEvent(receipt.logs, { vault });
return {
assetAmount: args.assets,
caller: args.caller,
receipt,
recipient: args.receiver,
shareAmount: args.earnShares,
};
}
async function depositShares(client, parameters) {
return depositShares.inner(sendTransaction_js_1.sendTransaction, client, parameters);
}
(function (depositShares) {
async function inner(action, client, parameters) {
const engine = await (0, readContract_js_1.readContract)(client, {
abi: Abis.earnVault,
address: parameters.vault,
functionName: 'engine',
});
return (await action(client, {
...parameters,
calls: depositShares.calls({
...toDepositSharesArgs(client, parameters),
engine,
venueShareToken: parameters.venueShareToken,
}),
}));
}
depositShares.inner = inner;
function call(...parameters) {
const [, args] = (0, utils_js_1.resolveCallParameters)(parameters);
const { recipient, vault, venueShareAmount } = args;
const earnShareAmountMin = (() => {
if (args.earnShareAmountMin !== undefined)
return args.earnShareAmountMin;
return tempo_1.EarnShares.minimumOutput(args.earnShareAmount, args.slippageBps);
})();
return (0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'depositVenueShares',
args: [venueShareAmount, recipient, earnShareAmountMin],
});
}
depositShares.call = call;
function calls(args) {
const { engine, venueShareAmount, venueShareToken } = args;
return [
(0, utils_js_1.defineCall)({
address: venueShareToken,
abi: Abis.tip20,
functionName: 'approve',
args: [engine, venueShareAmount],
}),
depositShares.call(args),
];
}
depositShares.calls = calls;
function extractEvent(logs, parameters) {
const { vault } = parameters;
const [log] = (0, parseEventLogs_js_1.parseEventLogs)({
abi: Abis.earnVault,
eventName: 'VenueSharesDeposited',
logs: logs.filter((log) => (0, isAddressEqual_js_1.isAddressEqual)(log.address, vault)),
});
if (!log)
throw new Error('`VenueSharesDeposited` event not found.');
return log;
}
depositShares.extractEvent = extractEvent;
async function estimateGas(client, parameters) {
return (0, estimateContractGas_js_1.estimateContractGas)(client, {
...(0, utils_js_1.pickWriteParameters)(parameters),
...depositShares.call(toDepositSharesArgs(client, parameters)),
});
}
depositShares.estimateGas = estimateGas;
async function simulate(client, parameters) {
return (0, simulateContract_js_1.simulateContract)(client, {
...(0, utils_js_1.pickWriteParameters)(parameters),
...depositShares.call(toDepositSharesArgs(client, parameters)),
});
}
depositShares.simulate = simulate;
})(depositShares || (exports.depositShares = depositShares = {}));
async function depositSharesSync(client, parameters) {
const { throwOnReceiptRevert = true, vault } = parameters;
const receipt = await depositShares.inner(sendTransactionSync_js_1.sendTransactionSync, client, {
...parameters,
throwOnReceiptRevert,
});
if (receipt.status === 'pending')
return { receipt };
const { args } = depositShares.extractEvent(receipt.logs, { vault });
return {
caller: args.caller,
earnShareAmount: args.earnShares,
receipt,
receivedVenueShareAmount: args.receivedEngineShares,
recipient: args.receiver,
venueShareAmount: args.requestedVenueShares,
};
}
async function privateDeposit(client, parameters) {
await assertPreparedZoneRequestChain(client, parameters);
return zoneActions.requestWithdrawal(client, parameters);
}
(function (privateDeposit) {
async function prepare(client, parameters) {
const chainId = client.chain?.id;
if (!chainId)
throw new Error('`chain` is required.');
const { actionId = ox_1.Hex.random(32), assetAmount, callbackGas = zoneGatewayCallbackGas, tempoRefundRecipient, fallbackRecipient = tempoRefundRecipient, gateway, portalAddress: portalAddress_, recipient, returnMemo, vault, withdrawalMemo, zoneId, } = parameters;
const portalAddress = portalAddress_ ?? Addresses.zonePortal(zoneId);
const readParameters = pickReadParameters(parameters);
const [fromBlock, config] = await Promise.all([
(0, getBlockNumber_js_1.getBlockNumber)(client, { cacheTime: 0 }),
getZoneGatewayConfig(client, {
...readParameters,
flow: 0,
gateway,
vault,
zoneId,
zonePortal: portalAddress,
}),
]);
const assetToken = parameters.assetToken ?? config.privateAsset;
if (!(0, isAddressEqual_js_1.isAddressEqual)(assetToken, config.privateAsset))
throw new Error('`assetToken` must match the Zone gateway private asset.');
const { encrypted, keyIndex } = await zoneActions.encryptedDeposit.prepareRecipient(client, {
...readParameters,
memo: returnMemo,
portalAddress: config.zonePortal,
recipient,
sender: gateway,
zoneId: config.zoneId,
});
const shareAmountMin = resolveMinimumShareAmount(parameters);
const data = (0, encodeAbiParameters_js_1.encodeAbiParameters)(Abis.earnRouterCallbackData, [
{
actionId,
flow: 0,
minEarnShares: shareAmountMin,
minOutputAmount: 0n,
minVaultAssets: parameters.vaultAssetAmountMin ?? assetAmount,
zoneReturn: {
encrypted,
keyIndex,
refundRecipient: tempoRefundRecipient,
},
},
]);
return {
actionId,
amount: assetAmount,
callbackGas,
chainId,
data,
fallbackRecipient,
fromBlock,
memo: withdrawalMemo,
to: gateway,
token: assetToken,
zoneId: config.zoneId,
};
}
privateDeposit.prepare = prepare;
function calls(args) {
return zoneActions.requestWithdrawal.calls(args);
}
privateDeposit.calls = calls;
})(privateDeposit || (exports.privateDeposit = privateDeposit = {}));
async function privateDepositSync(client, parameters) {
await assertPreparedZoneRequestChain(client, parameters);
return zoneActions.requestWithdrawalSync(client, parameters);
}
async function waitForPrivateDeposit(client, parameters) {
const { actionId, fromBlock, gateway, pollingInterval = client.pollingInterval, timeout = 60_000, vault, } = parameters;
const event = (0, getAbiItem_js_1.getAbiItem)({
abi: Abis.earnRouter,
name: 'EarnDeposit',
});
const observerId = (0, stringify_js_1.stringify)([
'waitForPrivateDeposit',
client.uid,
gateway,
vault,
actionId,
fromBlock,
]);
const { promise, reject, resolve } = (0, withResolvers_js_1.withResolvers)();
let timer;
let unobserve;
const cleanup = () => {
clearTimeout(timer);
unobserve();
};
const resolve_ = (result) => {
cleanup();
resolve(result);
};
const reject_ = (error) => {
cleanup();
reject(error);
};
unobserve = (0, observe_js_1.observe)(observerId, { reject: reject_, resolve: resolve_ }, (emit) => {
const unpoll = (0, poll_js_1.poll)(async () => {
try {
const [log] = await (0, getLogs_js_1.getLogs)(client, {
address: gateway,
args: { actionId, earnVault: vault },
event,
fromBlock,
strict: true,
toBlock: 'latest',
});
if (!log)
return;
unpoll();
emit.resolve({
actionId: log.args.actionId,
inputAmount: log.args.inputAmount,
inputToken: log.args.inputToken,
shares: log.args.earnShares,
tempoBlockNumber: log.blockNumber,
vaultAssets: log.args.vaultAssets,
zoneDepositHash: log.args.zoneDepositHash,
});
}
catch (error) {
unpoll();
emit.reject(error);
}
}, { emitOnBegin: true, interval: pollingInterval });
return unpoll;
});
timer = timeout
? setTimeout(() => {
reject_(new errors_js_1.WaitForPrivateDepositTimeoutError({ actionId, gateway }));
}, timeout)
: undefined;
return await promise;
}
async function getFeeState(client, parameters) {
const { recipient, vault, ...rest } = parameters;
const fees = await (0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.earnVault,
address: vault,
functionName: 'earnFees',
});
const contracts = [
(0, utils_js_1.defineCall)({
address: fees,
abi: Abis.earnFees,
functionName: 'currentFeeConfigId',
}),
(0, utils_js_1.defineCall)({
address: fees,
abi: Abis.earnFees,
functionName: 'feesActive',
}),
(0, utils_js_1.defineCall)({
address: fees,
abi: Abis.earnFees,
functionName: 'highWaterMark',
}),
(0, utils_js_1.defineCall)({
address: fees,
abi: Abis.earnFees,
functionName: 'previewAccruedFees',
}),
(0, utils_js_1.defineCall)({
address: fees,
abi: Abis.earnFees,
functionName: 'targetBase',
}),
];
const feeConfig = async (configId) => toFeeConfig(await (0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.earnFees,
address: fees,
functionName: 'feeConfig',
args: [configId],
}));
if (recipient !== undefined) {
const [configId, feesActive, highWaterMark, preview, targetBase, shares] = await (0, multicall_js_1.multicall)(client, {
...rest,
allowFailure: false,
contracts: [
...contracts,
(0, utils_js_1.defineCall)({
address: fees,
abi: Abis.earnFees,
functionName: 'claimableEarnShares',
args: [recipient],
}),
],
deployless: true,
});
return {
claimableShares: shares,
config: await feeConfig(configId),
configId,
feesActive,
highWaterMark,
preview: toFeePreview(preview),
targetBase,
};
}
const [configId, feesActive, highWaterMark, preview, targetBase] = await (0, multicall_js_1.multicall)(client, {
...rest,
allowFailure: false,
contracts,
deployless: true,
});
return {
config: await feeConfig(configId),
configId,
feesActive,
highWaterMark,
preview: toFeePreview(preview),
targetBase,
};
}
async function getPosition(client, parameters) {
const { account: account_ = client.account, vault, ...rest } = parameters;
if (!account_)
throw new account_js_1.AccountNotFoundError();
const account = (0, parseAccount_js_1.parseAccount)(account_).address;
const [assetToken, shareToken] = await (0, multicall_js_1.multicall)(client, {
...rest,
allowFailure: false,
contracts: [
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'asset',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'earnShare',
}),
],
deployless: true,
});
const [assetAllowance, assetBalance, shareAllowance, shareBalance] = await (0, multicall_js_1.multicall)(client, {
...rest,
allowFailure: false,
contracts: [
(0, utils_js_1.defineCall)({
address: assetToken,
abi: Abis.tip20,
functionName: 'allowance',
args: [account, vault],
}),
(0, utils_js_1.defineCall)({
address: assetToken,
abi: Abis.tip20,
functionName: 'balanceOf',
args: [account],
}),
(0, utils_js_1.defineCall)({
address: shareToken,
abi: Abis.tip20,
functionName: 'allowance',
args: [account, vault],
}),
(0, utils_js_1.defineCall)({
address: shareToken,
abi: Abis.tip20,
functionName: 'balanceOf',
args: [account],
}),
],
deployless: true,
});
const value = await (0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.earnVault,
address: vault,
args: [shareBalance],
functionName: 'previewRedeem',
});
return {
assetAllowance,
assetBalance,
assetToken,
shareAllowance,
shareBalance,
shareToken,
value,
};
}
async function getRedeemQuote(client, parameters) {
const { shareAmount, vault, ...rest } = parameters;
return (0, readContract_js_1.readContract)(client, {
...rest,
...getRedeemQuote.call({ shareAmount, vault }),
});
}
(function (getRedeemQuote) {
function call(args) {
const { shareAmount, vault } = args;
return (0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
args: [shareAmount],
functionName: 'previewRedeem',
});
}
getRedeemQuote.call = call;
})(getRedeemQuote || (exports.getRedeemQuote = getRedeemQuote = {}));
async function getVault(client, parameters) {
const { vault, ...rest } = parameters;
const [engine, fees] = await Promise.all([
(0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.earnVault,
address: vault,
functionName: 'engine',
}),
(0, readContract_js_1.readContract)(client, {
...rest,
abi: Abis.earnVault,
address: vault,
functionName: 'earnFees',
}),
]);
const [assetToken, engine_, shareToken, operator, emergencyGuardian, asyncJanitor, engineMigrationMode, depositsPaused, engineShares, shareSupply, isSynced, pendingRedeemCount, feesActive, totalAssets, name, symbol, asyncRedeem, exactWithdraw, inKindDeposit, syncRedeem,] = await (0, multicall_js_1.multicall)(client, {
...rest,
allowFailure: false,
contracts: getVault.calls({ engine, fees, vault }),
deployless: true,
});
if (!(0, isAddressEqual_js_1.isAddressEqual)(engine, engine_))
throw new errors_js_1.GetVaultEngineChangedError({ vault });
return {
assetToken,
asyncJanitor,
capabilities: { asyncRedeem, exactWithdraw, inKindDeposit, syncRedeem },
depositsPaused,
emergencyGuardian,
engine: { address: engine_, name, symbol, totalAssets },
engineMigrationMode: engineMigrationMode === 0 ? 'userOnly' : 'operatorEnabled',
engineShares,
feesActive,
isSynced,
operator,
pendingRedeemCount,
shareSupply,
shareToken,
};
}
(function (getVault) {
function calls(args) {
const { engine, fees, vault } = args;
return [
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'asset',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'engine',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'earnShare',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'operator',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'emergencyGuardian',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'asyncJanitor',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'engineMigrationMode',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'depositsPaused',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'engineShares',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'totalEarnShares',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'isAccountingAligned',
}),
(0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'openRedeemRequestCount',
}),
(0, utils_js_1.defineCall)({
address: fees,
abi: Abis.earnFees,
functionName: 'feesActive',
}),
(0, utils_js_1.defineCall)({
address: engine,
abi: Abis.earnEngine,
functionName: 'totalAssets',
}),
(0, utils_js_1.defineCall)({
address: engine,
abi: Abis.earnEngine,
functionName: 'name',
}),
(0, utils_js_1.defineCall)({
address: engine,
abi: Abis.earnEngine,
functionName: 'symbol',
}),
(0, utils_js_1.defineCall)({
address: engine,
abi: Abis.earnEngine,
functionName: 'supportsInterface',
args: [interfaceIds.asyncRedeem],
}),
(0, utils_js_1.defineCall)({
address: engine,
abi: Abis.earnEngine,
functionName: 'supportsInterface',
args: [interfaceIds.exactWithdraw],
}),
(0, utils_js_1.defineCall)({
address: engine,
abi: Abis.earnEngine,
functionName: 'supportsInterface',
args: [interfaceIds.inKindDeposit],
}),
(0, utils_js_1.defineCall)({
address: engine,
abi: Abis.earnEngine,
functionName: 'supportsInterface',
args: [interfaceIds.syncRedeem],
}),
];
}
getVault.calls = calls;
})(getVault || (exports.getVault = getVault = {}));
async function getWithdrawQuote(client, parameters) {
const { assetAmount, vault, ...rest } = parameters;
return (0, readContract_js_1.readContract)(client, {
...rest,
...getWithdrawQuote.call({ assetAmount, vault }),
});
}
(function (getWithdrawQuote) {
function call(args) {
const { assetAmount, vault } = args;
return (0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
args: [assetAmount],
functionName: 'previewWithdraw',
});
}
getWithdrawQuote.call = call;
})(getWithdrawQuote || (exports.getWithdrawQuote = getWithdrawQuote = {}));
async function redeem(client, parameters) {
return redeem.inner(sendTransaction_js_1.sendTransaction, client, parameters);
}
(function (redeem) {
async function inner(action, client, parameters) {
const [args, shareToken] = await Promise.all([
toRedeemArgs(client, parameters),
(0, readContract_js_1.readContract)(client, {
abi: Abis.earnVault,
address: parameters.vault,
functionName: 'earnShare',
}),
]);
return (await action(client, {
...parameters,
calls: redeem.calls({ ...args, shareToken }),
}));
}
redeem.inner = inner;
function call(...parameters) {
const [, args] = (0, utils_js_1.resolveCallParameters)(parameters);
const { recipient, vault } = args;
const assetAmountMin = (() => {
if (args.assetAmountMin !== undefined)
return args.assetAmountMin;
return tempo_1.EarnShares.minimumOutput(args.assetAmount, args.slippageBps);
})();
return (0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'redeem',
args: [
internal_Token.toBaseUnits(args.shareAmount, undefined),
recipient,
assetAmountMin,
],
});
}
redeem.call = call;
function calls(args) {
const { shareToken, vault } = args;
const shareAmount = internal_Token.toBaseUnits(args.shareAmount, undefined);
return [
(0, utils_js_1.defineCall)({
address: shareToken,
abi: Abis.tip20,
functionName: 'approve',
args: [vault, shareAmount],
}),
redeem.call({ ...args, shareAmount }),
];
}
redeem.calls = calls;
function extractEvent(logs, parameters) {
const { vault } = parameters;
const [log] = (0, parseEventLogs_js_1.parseEventLogs)({
abi: Abis.earnVault,
eventName: 'Redeemed',
logs: logs.filter((log) => (0, isAddressEqual_js_1.isAddressEqual)(log.address, vault)),
});
if (!log)
throw new Error('`Redeemed` event not found.');
return log;
}
redeem.extractEvent = extractEvent;
async function estimateGas(client, parameters) {
return (0, estimateContractGas_js_1.estimateContractGas)(client, {
...(0, utils_js_1.pickWriteParameters)(parameters),
...redeem.call(await toRedeemArgs(client, parameters)),
});
}
redeem.estimateGas = estimateGas;
async function simulate(client, parameters) {
return (0, simulateContract_js_1.simulateContract)(client, {
...(0, utils_js_1.pickWriteParameters)(parameters),
...redeem.call(await toRedeemArgs(client, parameters)),
});
}
redeem.simulate = simulate;
})(redeem || (exports.redeem = redeem = {}));
async function redeemSync(client, parameters) {
const { throwOnReceiptRevert = true, vault } = parameters;
const receipt = await redeem.inner(sendTransactionSync_js_1.sendTransactionSync, client, {
...parameters,
throwOnReceiptRevert,
});
if (receipt.status === 'pending')
return { receipt };
const { args } = redeem.extractEvent(receipt.logs, { vault });
return {
assetAmount: args.assets,
caller: args.caller,
receipt,
recipient: args.receiver,
shareAmount: args.earnShares,
};
}
async function privateRedeem(client, parameters) {
await assertPreparedZoneRequestChain(client, parameters);
return zoneActions.requestWithdrawal(client, parameters);
}
(function (privateRedeem) {
async function prepare(client, parameters) {
const chainId = client.chain?.id;
if (!chainId)
throw new Error('`chain` is required.');
const { actionId = ox_1.Hex.random(32), callbackGas = zoneGatewayCallbackGas, tempoRefundRecipient, fallbackRecipient = tempoRefundRecipient, gateway, portalAddress: portalAddress_, recipient, returnMemo, shareAmount, vault, withdrawalMemo, zoneId, } = parameters;
const portalAddress = portalAddress_ ?? Addresses.zonePortal(zoneId);
const readParameters = pickReadParameters(parameters);
const [fromBlock, config] = await Promise.all([
(0, getBlockNumber_js_1.getBlockNumber)(client, { cacheTime: 0 }),
getZoneGatewayConfig(client, {
...readParameters,
flow: 1,
gateway,
vault,
zoneId,
zonePortal: portalAddress,
}),
]);
const assetToken = parameters.assetToken ?? config.privateAsset;
if (!(0, isAddressEqual_js_1.isAddressEqual)(assetToken, config.privateAsset))
throw new Error('`assetToken` must match the Zone gateway private asset.');
const [{ encrypted, keyIndex }, assetAmountMin] = await Promise.all([
zoneActions.encryptedDeposit.prepareRecipient(client, {
...readParameters,
memo: returnMemo,
portalAddress: config.zonePortal,
recipient,
sender: gateway,
zoneId: config.zoneId,
}),
(async () => {
if (parameters.assetAmountMin !== undefined)
return tempo_1.EarnShares.minimumOutput(parameters.assetAmountMin, 0);
if (parameters.assetAmount !== undefined)
return tempo_1.EarnShares.minimumOutput(parameters.assetAmount, parameters.slippageBps);
const assetAmount = await getRedeemQuote(client, {
...readParameters,
shareAmount,
vault: config.vault,
});
return tempo_1.EarnShares.minimumOutput(assetAmount, parameters.slippageBps);
})(),
]);
const data = (0, encodeAbiParameters_js_1.encodeAbiParameters)(Abis.earnRouterCallbackData, [
{
actionId,
flow: 1,
minEarnShares: 0n,
minOutputAmount: assetAmountMin,
minVaultAssets: assetAmountMin,
zoneReturn: {
encrypted,
keyIndex,
refundRecipient: tempoRefundRecipient,
},
},
]);
return {
actionId,
amount: shareAmount,
callbackGas,
chainId,
data,
fallbackRecipient,
fromBlock,
memo: withdrawalMemo,
to: gateway,
token: config.shareToken,
zoneId: config.zoneId,
};
}
privateRedeem.prepare = prepare;
function calls(args) {
return zoneActions.requestWithdrawal.calls(args);
}
privateRedeem.calls = calls;
})(privateRedeem || (exports.privateRedeem = privateRedeem = {}));
async function privateRedeemSync(client, parameters) {
await assertPreparedZoneRequestChain(client, parameters);
return zoneActions.requestWithdrawalSync(client, parameters);
}
async function waitForPrivateRedeem(client, parameters) {
const { actionId, fromBlock, gateway, pollingInterval = client.pollingInterval, timeout = 60_000, vault, } = parameters;
const event = (0, getAbiItem_js_1.getAbiItem)({
abi: Abis.earnRouter,
name: 'EarnRedeem',
});
const observerId = (0, stringify_js_1.stringify)([
'waitForPrivateRedeem',
client.uid,
gateway,
vault,
actionId,
fromBlock,
]);
const { promise, reject, resolve } = (0, withResolvers_js_1.withResolvers)();
let timer;
let unobserve;
const cleanup = () => {
clearTimeout(timer);
unobserve();
};
const resolve_ = (result) => {
cleanup();
resolve(result);
};
const reject_ = (error) => {
cleanup();
reject(error);
};
unobserve = (0, observe_js_1.observe)(observerId, { reject: reject_, resolve: resolve_ }, (emit) => {
const unpoll = (0, poll_js_1.poll)(async () => {
try {
const [log] = await (0, getLogs_js_1.getLogs)(client, {
address: gateway,
args: { actionId, earnVault: vault },
event,
fromBlock,
strict: true,
toBlock: 'latest',
});
if (!log)
return;
unpoll();
emit.resolve({
actionId: log.args.actionId,
outputAmount: log.args.outputAmount,
outputToken: log.args.outputToken,
shares: log.args.earnShares,
tempoBlockNumber: log.blockNumber,
vaultAssets: log.args.vaultAssets,
zoneDepositHash: log.args.zoneDepositHash,
});
}
catch (error) {
unpoll();
emit.reject(error);
}
}, { emitOnBegin: true, interval: pollingInterval });
return unpoll;
});
timer = timeout
? setTimeout(() => {
reject_(new errors_js_1.WaitForPrivateRedeemTimeoutError({ actionId, gateway }));
}, timeout)
: undefined;
return await promise;
}
async function withdrawExact(client, parameters) {
return withdrawExact.inner(sendTransaction_js_1.sendTransaction, client, parameters);
}
(function (withdrawExact) {
async function inner(action, client, parameters) {
const [args, shareToken] = await Promise.all([
toWithdrawExactArgs(client, parameters),
(0, readContract_js_1.readContract)(client, {
abi: Abis.earnVault,
address: parameters.vault,
functionName: 'earnShare',
}),
]);
return (await action(client, {
...parameters,
calls: withdrawExact.calls({ ...args, shareToken }),
}));
}
withdrawExact.inner = inner;
function call(...parameters) {
const [, args] = (0, utils_js_1.resolveCallParameters)(parameters);
const { recipient, vault } = args;
const shareAmountMax = (() => {
if (args.shareAmountMax !== undefined)
return args.shareAmountMax;
return maximumInput(args.shareAmount, args.slippageBps);
})();
return (0, utils_js_1.defineCall)({
address: vault,
abi: Abis.earnVault,
functionName: 'withdrawExact',
args: [
internal_Token.toBaseUnits(args.assetAmount, undefined),
recipient,
shareAmountMax,
],
});
}
withdrawExact.call = call;
function calls(args) {
const { shareToken, vault } = args;
const assetAmount = internal_Token.toBaseUnits(args.assetAmount, undefined);
const call = withdrawExact.call({ ...args, assetAmount });
const [, , shareAmountMax] = call.args;
return [
(0, utils_js_1.defineCall)({
address: shareToken,
abi: Abis.tip20,
functionName: 'approve',
args: [vault, shareAmountMax],
}),
call,
];
}
withdrawExact.calls = calls;
function extractEvent(logs, parameters) {
const { vault } = parameters;
const [log] = (0, parseEventLogs_js_1.parseEventLogs)({
abi: Abis.earnVault,
eventName: 'WithdrewExact',
logs: logs.filter((log) => (0, isAddressEqual_js_1.isAddressEqual)(log.address, vault)),
});
if (!log)
throw new Error('`WithdrewExact` event not found.');
return log;
}
withdrawExact.extractEvent = extractEvent;
async function estimateGas(client, parameters) {
return (0, estimateContractGas_js_1.estimateContractGas)(client, {
...(0, utils_js_1.pickWriteParameters)(parameters),
...withdrawExact.call(await toWithdrawExactArgs(client, parameters)),
});
}
withdrawExact.estimateGas = estimateGas;
async function simulate(client, parameters) {
return (0, simulateContract_js_1.simulateContract)(client, {
...(0, utils_js_1.pickWriteParameters)(parameters),
...withdrawExact.call(await toWithdrawExactArgs(client, parameters)),
});
}
withdrawExact.simulate = simulate;
})(withdrawExact || (exports.withdrawExact = withdrawExact = {}));
async function withdrawExactSync(client, parameters) {
const { throwOnReceiptRevert = true, vault } = parameters;
const receipt = await withdrawExact.inner(sendTransactionSync_js_1.sendTransactionSync, client, {
...parameters,
throwOnReceiptRevert,
});
if (receipt.status === 'pending')
return { receipt };
const { args } = withdrawExact.extractEvent(receipt.logs, { vault });
return {
assetAmount: args.assets,
caller: args.caller,
receipt,
recipient: args.receiver,
shareAmount: args.earnSharesBurned,
};
}
const zoneGatewayCallbackGas = 10000000n;
function resolveMinimumShareAmount(parameters) {
if (parameters.shareAmountMin !== undefined)
return tempo_1.EarnShares.minimumOutput(parameters.shareAmountMin, 0);
return tempo_1.EarnShares.minimumOutput(parameters.shareAmount, parameters.slippageBps);
}
async function assertPreparedZoneRequestChain(client, parameters) {
const chain = client.chain;
if (!chain)
throw new Error('`chain` is required.');
if (chain.sourceId !== parameters.chainId)
throw new Error('Prepared Zone request parent chain ID does not match client chain.');
const { zoneId } = await zoneActions.getZoneInfo(client);
if (zoneId !== parameters.zoneId)
throw new Error('Prepared Zone request Zone ID does not match client chain.');
}
function pickReadParameters(parameters) {
const { blockOverrides, stateOverride } = parameters;
if (parameters.blockNumber !== undefined)
return {
blockNumber: parameters.blockNumber,
blockOverrides,
stateOverride,
};
return { blockOverrides, blockTag: parameters.blockTag, stateOverride };
}
async function getZoneGatewayConfig(client, parameters) {
const { flow, gateway, vault, zoneId, zonePortal, ...rest } = parameters;
const [vaultAsset, shareToken, allowedZoneId, routerVault, privateAsset, routerVaultAsset, routerShareToken, supportsFlow,] = await (0, multicall_js_1.multicall)(client, {
...rest,
allowFailure: false,
contracts: [
{
abi: Abis.earnVault,
address: vault,
functionName: 'asset',
},
{
abi: Abis.earnVault,
address: vault,
functionName: 'earnShare',
},
{
abi: Abis.earnRouter,
address: gateway,
functionName: 'allowedZoneId',
},
{
abi: Abis.earnRouter,
address: gateway,
functionName: 'earnVault',
},
{
abi: Abis.earnRouter,
address: gateway,
functionName: 'privateAsset',
},
{
abi: Abis.earnRouter,
address: gateway,
functionName: 'vaultAsset',
},
{
abi: Abis.earnRouter,
address: gateway,
functionName: 'earnShare',
},
{
abi: Abis.earnRouter,
address: gateway,
args: [flow],
functionName: 'supportsFlow',
},
],
deployless: true,
});
if (!supportsFlow)
throw new Error('Zone gateway flow is not supported.');
if (allowedZoneId !== zoneId)
throw new Error('Zone gateway is configured for a different Zone.');
if (!(0, isAddressEqual_js_1.isAddressEqual)(routerVault, vault))
throw new Error('Zone gateway is configured for a different Earn vault.');
if (!(0, isAddressEqual_js_1.isAddressEqual)(routerVaultAsset, vaultAsset))
throw new Error('Zone gateway vault asset does not match the Earn vault.');
if (!(0, isAddressEqual_js_1.isAddressEqual)(routerShareToken, shareToken))
throw new Error('Zone gateway share token does not match the Earn vault.');
return {
privateAsset,
shareToken,
vault,
vaultAsset,
zoneId,
zonePortal,
};
}
const interfaceIds = {
asyncRedeem: '0xa1a6a1d7',
exactWithdraw: '0x0adfb0b9',
inKindDeposit: '0xce4790a9',
syncRedeem: '0x94a2d467',
};
function toFeeConfig(config) {
return {
excess: config.excess,
fixedFees: config.fixedFees.slice(0, config.fixedFeeCount),
};
}
function toFeePreview(preview) {
const { allocationCount, allocations, postFeeValuePerEarnShare, preFeeValuePerEarnShare, targetValuePerEarnShare, totalFeeEarnShares, ...rest } = preview;
return {
...rest,
allocations: allocations
.slice(0, allocationCount)
.map(({ feeEarnShares, ...allocation }) => ({
...allocation,
feeShares: feeEarnShares,
})),
postFeeValuePerShare: postFeeValuePerEarnShare,
preFeeValuePerShare: preFeeValuePerEarnShare,
targetValuePerShare: targetValuePerEarnShare,
totalFeeShares: totalFeeEarnShares,
};
}
async function toDepositArgs(client, parameters) {
const { vault } = parameters;
const assetAmount = await toBaseUnitsLive(client, {
amount: parameters.assetAmount,
token: 'asset',
vault,
});
const args = {
assetAmount,
recipient: resolveRecipient(client, parameters),
vault,
};
if (parameters.shareAmountMin !== undefined)
return { ...args, shareAmountMin: parameters.shareAmountMin };
return {
...args,
shareAmount: parameters.shareAmount,
slippageBps: parameters.slippageBps,
};
}
function toDepositSharesArgs(client, parameters) {
const { vault, venueShareAmount } = parameters;
const args = {
recipient: resolveRecipient(client, parameters),
vault,
venueShareAmount,
};
if (parameters.earnShareAmountMin !== undefined)
return { ...args, earnShareAmountMin: parameters.earnShareAmountMin };
return {
...args,
earnShareAmount: parameters.earnShareAmount,
slippageBps: parameters.slippageBps,
};
}
async function toRedeemArgs(client, parameters) {
const { vault } = parameters;
const shareAmount = await toBaseUnitsLive(client, {
amount: parameters.shareAmount,
token: 'shareToken',
vault,
});
const args = {
recipient: resolveRecipient(client, parameters),
shareAmount,
vault,
};
if (parameters.assetAmountMin !== undefined)
return { ...args, assetAmountMin: parameters.assetAmountMin };
const assetAmount = await (async () => {
if (parameters.assetAmount !== undefined)
return parameters.assetAmount;
return getRedeemQuote(client, { shareAmount, vault });
})();
return {
...args,
assetAmount,
slippageBps: parameters.slippageBps,
};
}
async function toWithdrawExactArgs(client, parameters) {
const { vault } = parameters;
const assetAmount = await toBaseUnitsLive(client, {
amount: parameters.assetAmount,
token: 'asset',
vault,
});
const args = {
assetAmount,
recipient: resolveRecipient(client, parameters),
vault,
};
if (parameters.shareAmountMax !== undefined)
return { ...args, shareAmountMax: parameters.shareAmountMax };
const shareAmount = await (async () => {
if (parameters.shareAmount !== undefined)
return parameters.shareAmount;
return getWithdrawQuote(client, { assetAmount, vault });
})();
return {
...args,
shareAmount,
slippageBps: parameters.slippageBps,
};
}
function maximumInput(shareAmount, slippageBps) {
if (shareAmount <= 0n)
throw new tempo_1.EarnShares.InvalidExpectedOutputError({
expectedAmount: shareAmount,
});
if (!Number.isInteger(slippageBps) ||
slippageBps < 0 ||
slippageBps >= tempo_1.EarnShares.basisPointScale)
throw new tempo_1.EarnShares.InvalidSlippageError({ slippageBps });
const scale = BigInt(tempo_1.EarnShares.basisPointScale);
const numerator = shareAmount * (scale + BigInt(slippageBps));
return (numerator + scale - 1n) / scale;
}
async function toBaseUnitsLive(client, options) {
const { amount, token, vault } = options;
if (typeof amount === 'bigint')
return amount;
if (amount.decimals !== undefined)
return internal_Token.toBaseUnits(amount, amount.decimals);
const address = await (0, readContract_js_1.readContract)(client, {
abi: Abis.earnVault,
address: vault,
functionName: token === 'asset' ? 'asset' : 'earnShare',
});
const { decimals } = await (0, utils_js_1.resolveTokenWithDecimals)(client, {
token: address,
});
return internal_Token.toBaseUnits(amount, decimals);
}
function resolveRecipient(client, parameters) {
if (parameters.recipient)
return parameters.recipient;
const account = parameters.account ?? client.account;
if (!account)
throw new account_js_1.AccountNotFoundError();
return (0, parseAccount_js_1.parseAccount)(account).address;
}
//# sourceMappingURL=earn.js.map