@firefly-exchange/library-sui
Version:
Sui library housing helper methods, classes to interact with Bluefin protocol(s) deployed on Sui
905 lines (904 loc) • 41.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Interactor = void 0;
const transactions_1 = require("@mysten/sui/transactions");
const utils_1 = require("@mysten/sui/utils");
const library_1 = require("../library");
const utils_2 = require("../utils");
const utils_3 = require("./utils");
const enums_1 = require("./enums");
const utils_4 = require("@mysten/sui/utils");
const signer_1 = require("./signer");
const classes_1 = require("../classes");
class Interactor {
constructor(_suiClient, _deployment, _signer, isWalletExtension = false, _isZKLogin = false, zkPayload, _walletAddress) {
this.executeWalletTransaction = async ({ caller, tx }) => {
const { transactionBlockBytes, signature } = await caller.signTransactionBlock({
transactionBlock: tx
});
return await this.suiClient.executeTransactionBlock({
transactionBlock: transactionBlockBytes,
signature: signature,
options: {
showObjectChanges: true,
showEffects: true,
showEvents: true,
showInput: true
}
});
};
this.executeZkTransaction = async ({ tx, caller }) => {
tx.setSender(this.walletAddress);
const { bytes, signature: userSignature } = await tx.sign({
client: this.suiClient,
signer: caller
});
const zkSignature = (0, utils_2.createZkSignature)({
userSignature,
zkPayload: this.getZkPayload()
});
return this.suiClient.executeTransactionBlock({
transactionBlock: bytes,
signature: zkSignature,
options: {
showObjectChanges: true,
showEffects: true,
showEvents: true,
showInput: true
}
});
};
this.getZkPayload = () => {
return {
decodedJWT: this.decodedJWT,
proof: this.proof,
salt: this.salt,
maxEpoch: this.maxEpoch
};
};
this.suiClient = _suiClient;
this.deployment = _deployment;
// could be undefined, if initializing the interactor for only get calls
this.signer = _signer;
this.isWalletExtension = isWalletExtension;
this.isZKLogin = _isZKLogin;
if (_isZKLogin && zkPayload) {
this.maxEpoch = zkPayload.maxEpoch;
this.proof = zkPayload.proof;
this.decodedJWT = zkPayload.decodedJWT;
this.salt = zkPayload.salt;
}
this.walletAddress = _walletAddress || _signer?.toSuiAddress();
this.poolIdToCoin = {};
for (const poolName of Object.keys(this?.deployment?.RewardsPool || [])) {
this.poolIdToCoin[this?.deployment?.RewardsPool?.[poolName]?.id] =
this?.deployment?.RewardsPool?.[poolName]?.coin;
}
}
/// signs and executes the provided sui transaction block
async signAndExecuteTxBlock(transactionBlock, signer) {
signer = signer || this.signer;
transactionBlock.setSenderIfNotSet(this.walletAddress ?? signer.toSuiAddress());
if (this.isZKLogin) {
return this.executeZkTransaction({ caller: signer, tx: transactionBlock });
}
else if (this.isWalletExtension) {
return this.executeWalletTransaction({
caller: this.signer,
tx: transactionBlock
});
}
else {
return this.executeTxBlock(transactionBlock, signer);
}
}
async executeTxBlock(transactionBlock, signer, options = {
showObjectChanges: true,
showEffects: true,
showEvents: true,
showInput: true
}) {
const caller = signer || this.signer;
transactionBlock.setSenderIfNotSet(caller.toSuiAddress());
const builtTransactionBlock = await transactionBlock.build({
client: this.suiClient
});
const transactionSignature = await caller.signTransaction(builtTransactionBlock);
return this.suiClient.executeTransactionBlock({
transactionBlock: builtTransactionBlock,
signature: transactionSignature.signature,
options
});
}
async postCall(txb, multiSig) {
if (multiSig) {
txb.setSender(multiSig);
return (0, utils_4.toB64)(await txb.build({ client: this.suiClient, onlyTransactionKind: false }));
}
else {
txb.setSender(this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
}
/**
* Allows the caller to create the vault
* @param vaultName name of the vault (the market maker) for which the vault is being created
* @param operator the address of the trading account that will trade using vaults funds
* @param holdingAccount the address of mm's holding account that can receive profits from the vault
* @param claimsManager the address of account that can generate funds claim signature
* @param maxCapacity max amount that can be locked in the vault
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async createVault(vaultName, operator, holdingAccount, claimsManager, maxCapacity, vaultType, multiSig) {
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.AdminCap),
txb.object(this.deployment.BluefinSubAccounts),
txb.object(this.deployment.BluefinBank),
txb.object(this.deployment.BluefinVaultStore),
txb.pure.string(vaultName),
txb.pure.address(operator),
txb.pure.address(holdingAccount),
txb.pure.address(claimsManager),
txb.pure.u64((0, library_1.toBigNumberStr)(maxCapacity, utils_3.USDC_DECIMALS)),
txb.pure.u8(vaultType)
],
typeArguments: [this.getSupportedCoin()],
target: `${this.deployment.Package}::bluefin_vault::create_vault`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows the caller to create a non-trading vault
* @param vaultName name of the vault (the market maker) for which the vault is being created
* @param claimsManager the address of account that can generate funds claim signature
* @param maxCapacity max amount that can be locked in the vault - This should be in 1eX format. Where `X` being the decimals supported by the vault coin
* @param supportedCoinType the supported coin of the vault
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async createNonTradingVault(vaultName, claimsManager, maxCapacity, supportedCoin, multiSig) {
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.AdminCap),
txb.pure.string(vaultName),
txb.pure.address(claimsManager),
txb.pure.u64((0, library_1.bigNumber)(maxCapacity).toFixed(0))
],
typeArguments: [supportedCoin],
target: `${this.deployment.Package}::bluefin_vault::create_non_trading_vault`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows admin of the protocol to create reward pools
* @param rewardCoin The reward coin that will be funded into the pool and then claimed by users
* @param controller The operator that will be creating reward signatures
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async createRewardPool(rewardCoin, controller, multiSig) {
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.AdminCap),
txb.pure.address(controller || this.signer.toSuiAddress())
],
typeArguments: [rewardCoin],
target: `${this.deployment.Package}::distributor::create_reward_pool`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows the caller to fund a rewards pool
* @param pool the reward pool to which the amount will be deposited
* @param amount the amount to be deposited into the pool (must be in base number, the method adds 9 decimal places)
* @returns SuiTransactionBlockResponse
*/
async fundRewardPool(pool, amount) {
const txb = new transactions_1.Transaction();
// BLUE and SUI both rewards are in 9 decimals
const amountEN = (0, library_1.toBigNumber)(amount, 9);
const account = this.walletAddress || this.signer.toSuiAddress();
const [splitCoin, mergeCoin] = await classes_1.CoinUtils.createCoinWithBalance(this.suiClient, txb, amountEN, pool.coin, account);
const args = [txb.object(pool.id), txb.object(splitCoin)];
txb.moveCall({
arguments: args,
typeArguments: [pool.coin],
target: `${this.deployment.Package}::distributor::fund_rewards_pool`
});
if (mergeCoin) {
txb.transferObjects([mergeCoin], account);
}
txb.setSender(this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows caller to change the admin of the vault store
* The caller must be the admin of provided vault store
* @param newAdmin address of new vault store admin
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async setVaultStoreAdmin(newAdmin, multiSig) {
const txb = new transactions_1.Transaction();
txb.moveCall({
target: `${this.deployment.Package}::vaults::set_admin`,
arguments: [
txb.object(this.deployment.BluefinVaultStore),
txb.pure.address(newAdmin)
]
});
return await this.postCall(txb, multiSig);
}
/**
* Allows caller to set the vault bank manager on the bluefin vault store
* The caller must be the admin of provided vault store
* @param manager address of new bank manager
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async setVaultBankManager(manager, multiSig) {
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.BluefinVaultStore),
txb.pure.address(manager)
],
target: `${this.deployment.BluefinPackage}::vaults::set_vaults_bank_manger`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows caller to set the vault claims manager for the provided vault
* @param vaultName the name of the vault for which to update the manager
* @param manager address of new claims manager
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async setVaultClaimsManager(vaultName, manager, multiSig) {
const vault = this.getVaultID(vaultName);
const vaultCoin = this.getVaultCoin(vaultName);
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.AdminCap),
txb.object(vault),
txb.pure.address(manager)
],
typeArguments: [vaultCoin],
target: `${this.deployment.Package}::bluefin_vault::update_vault_claims_manager_account`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows caller to set the vault bank manager on the bluefin vault store
* The caller must be the admin of provided vault store
* @param manager address of new bank manager
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async setVaultOperator(vaultName, operator, multiSig) {
const vault = this.getVaultID(vaultName);
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.AdminCap),
txb.object(vault),
txb.object(this.deployment.BluefinSubAccounts),
txb.object(this.deployment.BluefinVaultStore),
txb.pure.address(operator)
],
typeArguments: [this.getSupportedCoin()],
target: `${this.deployment.Package}::bluefin_vault::update_vault_operator`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows caller to set the admin of vault store
* The caller must be the admin of provided vault store
* @param admin address of new admin
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async setAdmin(admin, multiSig) {
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.BluefinVaultStore),
txb.pure.address(admin)
],
target: `${this.deployment.BluefinPackage}::vaults::set_admin`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows caller to set the controller of a reward pool
* The caller must be the admin of package
* @param controller address of new controller
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async setController(pool, controller, multiSig) {
const txb = new transactions_1.Transaction();
const rewardsPool = this.deployment.RewardsPool[pool];
txb.moveCall({
arguments: [
txb.object(this.deployment.AdminCap),
txb.object(rewardsPool.id),
txb.pure.address(controller)
],
typeArguments: [rewardsPool.coin],
target: `${this.deployment.Package}::distributor::update_rewards_controller`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows caller to mark the provided nonce as claimed on-chain, effectively invalidating the
* signature created using that nonce.
* @param pool The name of the pool
* @param nonce the nonce to be marked as claimed
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async markNonceAsClaimed(pool, nonce, multiSig) {
const txb = new transactions_1.Transaction();
const rewardsPool = this.deployment.RewardsPool[pool];
txb.moveCall({
arguments: [txb.object(rewardsPool.id), txb.pure.u128(nonce)],
typeArguments: [rewardsPool.coin],
target: `${this.deployment.Package}::distributor::mark_nonce_as_claimed`
});
return await this.postCall(txb, multiSig);
}
/**
* Returns the balance of the pool
*/
async getPoolBalance(pool) {
const rewardsPool = this.deployment.RewardsPool[pool];
const obj = await this.suiClient.getObject({
id: rewardsPool.id,
options: {
showContent: true
}
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (0, library_1.bigNumber)((obj.data?.content).fields.reward_balance);
}
/**
* Allows caller to pause a vault
* The caller must be the admin of provided vault store
* @param vaultName name of the vault(Nexus etc..)
* @param pause boolean value to pause/unpause the vault
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async pauseVault(vaultName, pauseDeposit, pauseWithdraw, pauseClaim, multiSig) {
const vault = this.getVaultID(vaultName);
const vaultCoin = this.getVaultCoin(vaultName);
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.AdminCap),
txb.object(vault),
txb.pure.bool(pauseDeposit),
txb.pure.bool(pauseWithdraw),
txb.pure.bool(pauseClaim)
],
typeArguments: [vaultCoin],
target: `${this.deployment.Package}::bluefin_vault::pause_vault`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows caller(holding account of vault) to request profit withdraw from vault
* @param vaultName name of the vault(Nexus etc..)
* @param amount the amount of profit to withdraw
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async requestProfitWithdraw(vaultName, amount) {
const vault = this.getVaultID(vaultName);
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.BluefinBank),
txb.object(vault),
txb.pure.u64((0, library_1.toBigNumberStr)(amount, utils_3.USDC_DECIMALS))
],
typeArguments: [this.getSupportedCoin()],
target: `${this.deployment.Package}::bluefin_vault::request_profit_withdraw_from_vault`
});
txb.setSender(this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows users to deposit funds into a vault
* @param vaultName name of the vault(Nexus etc..)
* @param amount the amount of tokens to deposit - should be in base number with NO EXTRA DECIMALS
* @param options optional arguments
* - receiver: the address of the user that will receive deposited amount rewards on elixir
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async depositToVault(vaultName, amount, options) {
const receiver = options?.receiver || this.walletAddress || this.signer.toSuiAddress();
const txb = new transactions_1.Transaction();
const account = this.walletAddress ?? this.signer.toSuiAddress();
const vaultID = this.getVaultID(vaultName);
const vaultType = this.getVaultType(vaultName);
const vaultCoin = this.getVaultCoin(vaultName);
const vaultDecimals = this.getVaultDecimals(vaultName);
const amountEN = (0, library_1.toBigNumber)(amount, vaultDecimals);
console.log(amountEN.toFixed(0));
const [splitCoin, mergeCoin] = await classes_1.CoinUtils.createCoinWithBalance(this.suiClient, txb, amountEN, vaultCoin, account);
if (vaultType != enums_1.VaultType.NON_TRADING) {
txb.moveCall({
arguments: [
txb.object(this.deployment.BluefinBank),
txb.object(this.deployment.BluefinSequencer),
txb.object(vaultID),
txb.object(splitCoin),
txb.pure.u64((0, library_1.bigNumber)(amountEN).toFixed(0)),
txb.pure.address(receiver)
],
typeArguments: [vaultCoin],
target: `${this.deployment.Package}::bluefin_vault::deposit_to_vault`
});
}
else {
txb.moveCall({
arguments: [
txb.object(vaultID),
txb.object(splitCoin),
txb.pure.u64((0, library_1.bigNumber)(amountEN).toFixed(0)),
txb.pure.address(receiver)
],
typeArguments: [vaultCoin],
target: `${this.deployment.Package}::bluefin_vault::deposit_to_non_trading_vault`
});
}
if (mergeCoin) {
txb.transferObjects([mergeCoin], account);
}
if (splitCoin) {
txb.transferObjects([splitCoin], account);
}
txb.setSender(account);
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows caller to move funds the bank account of a vault inside bluefin's margin bank
* into the vault to hold for people to come in and withdraw
* @param vaultName The name of the vault
* @param amount The amount of funds to be moved
*/
async moveWithdrawalFundsToVault(vaultName, amount) {
const vault = this.getVaultID(vaultName);
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.BluefinBank),
txb.object(this.deployment.BluefinVaultStore),
txb.object(this.deployment.BluefinSequencer),
txb.object(vault),
txb.pure.u64((0, library_1.toBigNumberStr)(amount, utils_3.USDC_DECIMALS))
],
typeArguments: [this.getSupportedCoin()],
target: `${this.deployment.Package}::bluefin_vault::move_user_withdrawal_funds_to_vault`
});
txb.setSender(this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows caller to move profit amount from the bank account of a vault inside bluefin's margin bank
* to vault's holding account.
* @param vaultName The name of the vault
* @param amount The amount of funds to be moved
*/
async moveProfitWithdrawalFundsToHoldingAccount(vaultName, amount) {
const vault = this.getVaultID(vaultName);
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.BluefinBank),
txb.object(this.deployment.BluefinVaultStore),
txb.object(this.deployment.BluefinSequencer),
txb.object(vault),
txb.pure.u64((0, library_1.toBigNumberStr)(amount, utils_3.USDC_DECIMALS))
],
typeArguments: [this.getSupportedCoin()],
target: `${this.deployment.Package}::bluefin_vault::move_profit_withdrawal_funds_to_holding_account`
});
txb.setSender(this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows caller to request withdraw their locked funds from provided vault
* @param vaultName the name of the vault (partner name)
* @param amount the amount of tokens to deposit - should be in base number with NO EXTRA DECIMALS
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async requestWithdrawFromVault(vaultName, amount) {
const vaultID = this.getVaultID(vaultName);
const vaultDecimals = this.getVaultDecimals(vaultName);
const vaultCoin = this.getVaultCoin(vaultName);
const amountEN = (0, library_1.toBigNumber)(amount, vaultDecimals);
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(vaultID),
txb.pure.u64((0, library_1.bigNumber)(amountEN).toFixed(0))
],
typeArguments: [vaultCoin],
target: `${this.deployment.Package}::bluefin_vault::request_withdraw_from_vault`
});
txb.setSender(this.walletAddress ?? this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows caller to claim withdrawn funds on a user's behalf
* @param vaultName the name of the vault from which funds are to be claimed
* @param payload The signature payload consisting of
* - target The address of reward pool from which to claim rewards
* - receiver The address for which to claim rewards
* - amount The amount of rewards to be claimed (Must be in 1e9 Format that the signer signed on)
* - nonce The unique nonce used by pool's operator to generate signature
* - type The type of signature payload (RewardsClaim | FundsClaim)
* @param signature The signature created by pool's operator for provided payload
*/
async claimFunds(vaultName, payload, signature) {
// serialize the payload data
const serPayload = signer_1.SignaturePayloadStruct.serialize(payload).toBytes();
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(utils_1.SUI_CLOCK_OBJECT_ID),
txb.object(this.getVaultID(vaultName)),
txb.pure.vector("u8", Array.from(serPayload)),
txb.pure.vector("u8", Array.from((0, library_1.hexStrToUint8)(signature)))
],
typeArguments: [this.getVaultCoin(vaultName)],
target: `${this.deployment.Package}::bluefin_vault::claim_withdrawn_funds`
});
txb.setSender(this.walletAddress ?? this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows caller to batch claim
* Input is array of
* @param batch
* payload: The signature payload consisting of
* - target The address of reward pool from which to claim rewards
* - receiver The address for which to claim rewards
* - amount The amount of rewards to be claimed (Must be in 1e9 Format that the signer signed on)
* - nonce The unique nonce used by pool's operator to generate signature
* - type The type of signature payload (RewardsClaim | FundsClaim)
* signature: The signature created by pool's operator for provided payload
*/
async claimFundsBatch(batch) {
const txb = new transactions_1.Transaction();
for (const tx of batch) {
// serialize the payload data
const serPayload = signer_1.SignaturePayloadStruct.serialize(tx.payload).toBytes();
txb.moveCall({
arguments: [
txb.object(utils_1.SUI_CLOCK_OBJECT_ID),
txb.object(tx.payload.target),
txb.pure.vector("u8", Array.from(serPayload)),
txb.pure.vector("u8", Array.from((0, library_1.hexStrToUint8)(tx.signature)))
],
typeArguments: [this.getVaultCoin(tx.vaultName)],
target: `${this.deployment.Package}::bluefin_vault::claim_withdrawn_funds`
});
}
txb.setSender(this.walletAddress ?? this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows caller to claim rewards for the provided receiver from provided rewards pool
* @param payload The signature payload consisting of
* - target The address of reward pool from which to claim rewards
* - receiver The address for which to claim rewards
* - amount The amount of rewards to be claimed (Must be in 1e9 Format that the signer signed on)
* - nonce The unique nonce used by pool's operator to generate signature
* - type The type of signature payload (RewardsClaim | FundsClaim)
* @param signature The signature created by pool's operator for provided payload
*/
async claimRewards(poolName, payload, signature) {
const pool = this.getRewardsPool(poolName);
// serialize the payload data
const serPayload = signer_1.SignaturePayloadStruct.serialize(payload).toBytes();
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(utils_1.SUI_CLOCK_OBJECT_ID),
txb.object(pool.id),
txb.pure.vector("u8", Array.from(serPayload)),
txb.pure.vector("u8", Array.from((0, library_1.hexStrToUint8)(signature)))
],
typeArguments: [pool.coin],
target: `${this.deployment.Package}::distributor::claim_rewards`
});
txb.setSender(this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows caller to claim rewards for the provided receiver from provided rewards pool
* @param batch consisting of
* payload: The signature payload consisting of
* - target The address of reward pool from which to claim rewards
* - receiver The address for which to claim rewards
* - amount The amount of rewards to be claimed (Must be in 1e9 Format that the signer signed on)
* - nonce The unique nonce used by pool's operator to generate signature
* - type The type of signature payload (RewardsClaim | FundsClaim)
* signature: The signature created by pool's operator for provided payload
*/
async claimRewardsBatch(batch) {
const txb = new transactions_1.Transaction();
for (const tx of batch) {
const coin = this.poolIdToCoin[tx.payload.target];
// serialize the payload data
const serPayload = signer_1.SignaturePayloadStruct.serialize(tx.payload).toBytes();
txb.moveCall({
arguments: [
txb.object(utils_1.SUI_CLOCK_OBJECT_ID),
txb.object(tx.payload.target),
txb.pure.vector("u8", Array.from(serPayload)),
txb.pure.vector("u8", Array.from((0, library_1.hexStrToUint8)(tx.signature)))
],
typeArguments: [coin],
target: `${this.deployment.Package}::distributor::claim_rewards`
});
}
txb.setSender(this.walletAddress || this.signer.toSuiAddress());
return this.signAndExecuteTxBlock(txb, this.signer);
}
/**
* Allows admin to update vault version
* @param vaultName the name of the vault
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async updateVaultVersion(vaultName, multiSig) {
const vault = this.getVaultID(vaultName);
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [txb.object(this.deployment.AdminCap), txb.object(vault)],
typeArguments: [this.getSupportedCoin()],
target: `${this.deployment.Package}::bluefin_vault::update_version_for_vault`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows admin to update rewards pool version
* @param poolName the name of the rewards pool
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async updateRewardsPoolVersion(poolName, multiSig) {
const pool = this.getRewardsPool(poolName);
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [txb.object(this.deployment.AdminCap), txb.object(pool.id)],
typeArguments: [pool.coin],
target: `${this.deployment.Package}::distributor::update_version_for_reward_pool`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows holder of the admin cap to transfer it to new user
* @param newAdmin the address of new admin
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async transferAdminCap(newAdmin, multiSig) {
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.AdminCap),
txb.object(this.deployment.BluefinVaultStore),
txb.pure.address(newAdmin)
],
target: `${this.deployment.Package}::roles::transfer_admin_cap`
});
return await this.postCall(txb, multiSig);
}
/**
* Allows admin to move locked funds from one vault to another
* @param srcVault the name of source vault
* @param destVault the name of destination vault
* @param accounts the addresses of accounts that will be moved
* @param multiSig optional multisig wallet address. if provided the method will return tx bytes of the tx instead of executing it
* @returns SuiTransactionBlockResponse or transaction bytes
*/
async moveFundsAcrossVaults(srcVault, destVault, accounts, multiSig) {
const txb = new transactions_1.Transaction();
txb.moveCall({
arguments: [
txb.object(this.deployment.AdminCap),
txb.object(this.deployment.BluefinBank),
txb.object(this.deployment.BluefinSequencer),
txb.object(this.getVaultID(srcVault)),
txb.object(this.getVaultID(destVault)),
txb.pure.vector("address", accounts)
],
typeArguments: [this.getSupportedCoin()],
target: `${this.deployment.Package}::bluefin_vault::move_funds_across_vaults`
});
return await this.postCall(txb, multiSig);
}
/// Returns addresses of all users having any locked amount in provided vault
async getVaultTVLProviders(vaultName) {
const vaultID = this.getVaultID(vaultName);
const obj = await this.suiClient.getObject({
id: vaultID,
options: {
showContent: true
}
});
const usersMapID = (obj.data?.content).fields.users.fields.id.id;
let hasNextPage = true;
let cursor = undefined;
const users = [];
while (hasNextPage) {
const resp = await this.suiClient.getDynamicFields({
parentId: usersMapID,
cursor
});
users.push(...resp.data);
hasNextPage = resp.hasNextPage;
cursor = resp.nextCursor;
}
return users.map(u => u.name.value);
}
/// Returns user record for the provided vault
async getUserRecordForVault(vaultName, user) {
const address = user || this.signer.toSuiAddress();
const vaultID = this.getVaultID(vaultName);
const userData = {
amountLocked: "0",
pendingWithdrawal: "0",
shares: "0"
};
const obj = await this.suiClient.getObject({
id: vaultID,
options: {
showContent: true
}
});
const mapID = (obj.data?.content).fields.users.fields.id.id;
try {
const user = await this.suiClient.getDynamicFieldObject({
parentId: mapID,
name: {
type: "address",
value: address
}
});
const fields = (user.data?.content).fields.value.fields;
return {
amountLocked: fields.amount_locked,
pendingWithdrawal: fields.amount_locked,
shares: fields.shares
};
}
catch (e) {
console.log(`User does not exist for the vault`);
}
return userData;
}
/// Returns a user's current locked amount in provided vault
async getUserLockedAmount(vaultName, user) {
const userData = await this.getUserRecordForVault(vaultName, user);
return (0, library_1.toBaseNumber)(userData.amountLocked, 3, this.getVaultDecimals(vaultName));
}
/// Returns the pending amount for claim that user had requested for withdraw
async getUserPendingWithdrawals(vaultName, user) {
const userData = await this.getUserRecordForVault(vaultName, user);
return (0, library_1.toBaseNumber)(userData.pendingWithdrawal, 3, this.getVaultDecimals(vaultName));
}
/// Returns the total requested pending withdrawal amount yet to be moved to vault for user claims
async getTotalPendingWithdrawalAmount(vaultName) {
const vaultID = this.getVaultID(vaultName);
const vaultDecimals = this.getVaultDecimals(vaultName);
const obj = await this.suiClient.getObject({
id: vaultID,
options: {
showContent: true
}
});
return (0, library_1.toBaseNumber)((obj.data?.content).fields.total_amount_to_be_withdrawn, 3, vaultDecimals);
}
/// Returns current total amount locked in a given vault
async getVaultLockedAmount(vaultName) {
const vaultID = this.getVaultID(vaultName);
const obj = await this.suiClient.getObject({
id: vaultID,
options: {
showContent: true
}
});
return (0, library_1.toBaseNumber)((obj.data?.content).fields.total_locked_amount, 3, this.getVaultDecimals(vaultName));
}
/// Returns current total amount locked in a given vault
async getVaultCoinBalance(vaultName) {
const vaultID = this.getVaultID(vaultName);
const obj = await this.suiClient.getObject({
id: vaultID,
options: {
showContent: true
}
});
return (0, library_1.toBaseNumber)((obj.data?.content).fields.coin_balance, 3, this.getVaultDecimals(vaultName));
}
/// Returns the available withdrawable balance in the bluefin bank
async getBluefinBankBalance(address) {
address = address || this.signer.toSuiAddress();
const bankObj = await this.suiClient.getObject({
id: this.deployment.BluefinBank,
options: { showContent: true }
});
const bankTableID = (bankObj.data?.content).fields.accounts.fields.id.id;
try {
const availableBalance = await this.suiClient.getDynamicFieldObject({
parentId: bankTableID,
name: {
type: "address",
value: address
}
});
return (0, library_1.toBaseNumber)((availableBalance.data?.content).fields.value.fields.balance, 3, utils_3.TOKEN_DECIMALS);
}
catch (e) {
return 0;
}
}
/// Returns the balance of user (in base number, removes extra decimals)
/// of the coin supported by provided vault
async getCoinBalance(coinType, coinDecimals, address) {
const account = address || this.walletAddress || this.signer.toSuiAddress();
const balance = await classes_1.CoinUtils.getCoinBalance(this.suiClient, account, coinType);
return (0, library_1.toBaseNumber)(balance, 3, coinDecimals);
}
/// formulates the supported coin type, to be passed as `typeArguments` to contract calls
getSupportedCoin() {
return `${this.deployment.SupportedCoin}::coin::COIN`;
}
/// Returns the coin supported by the vault
getVaultCoin(vaultName) {
return this.deployment.Vaults[vaultName]["coin"];
}
/// Returns the vault id
getVaultID(vaultName) {
return this.deployment.Vaults[vaultName]["id"];
}
/// Returns the type of vault
getVaultType(vaultName) {
const type = Number(this.deployment.Vaults[vaultName]["type"]);
switch (type) {
case 1:
return enums_1.VaultType.NO_PNL_SHARING;
case 2:
return enums_1.VaultType.PNL_SHARING;
case 3:
return enums_1.VaultType.NON_TRADING;
case 4:
throw `Unknown vault type ${type} for ${vaultName}`;
}
}
/// Returns the vault bank account
getVaultAccount(vaultName) {
return this.deployment.Vaults[vaultName]["account"];
}
/// Returns the vault decimals
getVaultDecimals(vaultName) {
return this.deployment.Vaults[vaultName]["decimals"];
}
/// Returns the reward pools data
getRewardsPool(poolName) {
return this.deployment.RewardsPool[poolName];
}
}
exports.Interactor = Interactor;