diff --git a/src/components/CamOfferCard.vue b/src/components/CamOfferCard.vue index 09350cbfc..62a19683f 100644 --- a/src/components/CamOfferCard.vue +++ b/src/components/CamOfferCard.vue @@ -104,6 +104,23 @@ + @@ -113,7 +130,7 @@ import { cleanAvaxBN, formatDuration } from '@/helpers/helper' import { WalletHelper } from '@/helpers/wallet_helper' import AvaAsset from '@/js/AvaAsset' import { WalletType } from '@/js/wallets/types' -import { PlatformRewardDeposit } from '@/store/modules/platform/types' +import { PlatformRewardDeposit, PlatformRewardTreasury } from '@/store/modules/platform/types' import { MultisigTx as SignavaultTx } from '@/store/modules/signavault/types' import { BN, Buffer } from '@c4tplatform/caminojs/dist' import { ClaimTx, UnsignedTx } from '@c4tplatform/caminojs/dist/apis/platformvm' @@ -126,9 +143,10 @@ import CamCard from './CamCard.vue' }) export default class CamOfferCard extends Vue { @Prop() readonly title!: string - @Prop() readonly type!: 'offer' | 'reward' + @Prop() readonly type!: 'offer' | 'reward' | 'treasuryRewards' @Prop() readonly offer!: DepositOffer @Prop() readonly reward!: PlatformRewardDeposit + @Prop() readonly treasuryRewards!: PlatformRewardTreasury get isOffer() { return this.type === 'offer' @@ -138,6 +156,10 @@ export default class CamOfferCard extends Vue { return this.type === 'reward' } + get isTreasuryRewards() { + return this.type === 'treasuryRewards' + } + get activeWallet(): WalletType { return this.$store.state.activeWallet } @@ -326,6 +348,7 @@ label { display: grid; grid-template-columns: 1fr 1fr; grid-gap: 1rem; + height: 100%; .offer_detail_left { border-right: 2px solid var(--border-color); } diff --git a/src/components/wallet/earn/TreasuryRewardCard.vue b/src/components/wallet/earn/TreasuryRewardCard.vue new file mode 100644 index 000000000..c2d5fce20 --- /dev/null +++ b/src/components/wallet/earn/TreasuryRewardCard.vue @@ -0,0 +1,320 @@ + + + diff --git a/src/components/wallet/earn/UserRewards.vue b/src/components/wallet/earn/UserRewards.vue index 359094ca3..e5a115495 100644 --- a/src/components/wallet/earn/UserRewards.vue +++ b/src/components/wallet/earn/UserRewards.vue @@ -1,26 +1,33 @@ + + diff --git a/src/components/wallet/transfer/BulkTransfer.vue b/src/components/wallet/transfer/BulkTransfer.vue new file mode 100644 index 000000000..1238d9937 --- /dev/null +++ b/src/components/wallet/transfer/BulkTransfer.vue @@ -0,0 +1,231 @@ + + + + + diff --git a/src/components/wallet/transfer/Transfer.vue b/src/components/wallet/transfer/Transfer.vue new file mode 100644 index 000000000..8cf0c7e65 --- /dev/null +++ b/src/components/wallet/transfer/Transfer.vue @@ -0,0 +1,953 @@ + + + + + diff --git a/src/components/wallet/transfer/types.ts b/src/components/wallet/transfer/types.ts index b6ec01224..e3bd9bd7f 100644 --- a/src/components/wallet/transfer/types.ts +++ b/src/components/wallet/transfer/types.ts @@ -17,3 +17,8 @@ export interface ICurrencyInputDropdownValue { asset: AvaAsset | null amount: BN } + +export interface BulkOrder { + address: string + amount: BN +} diff --git a/src/helpers/wallet_helper.ts b/src/helpers/wallet_helper.ts index 1fd7bd00b..d2f5e9a61 100644 --- a/src/helpers/wallet_helper.ts +++ b/src/helpers/wallet_helper.ts @@ -1,5 +1,6 @@ import { ava, bintools } from '@/AVA' import { + buildBulkTransfer, buildCreateNftFamilyTx, buildEvmTransferERCNftTx, buildEvmTransferErc20Tx, @@ -10,7 +11,7 @@ import { WalletType } from '@/js/wallets/types' import { AmountOutput } from '@c4tplatform/caminojs/dist/apis/avm' import { UnsignedTx } from '@c4tplatform/caminojs/dist/apis/platformvm' -import { ITransaction } from '@/components/wallet/transfer/types' +import { BulkOrder, ITransaction } from '@/components/wallet/transfer/types' import { BN, Buffer } from '@c4tplatform/caminojs/dist' import { UTXO as AVMUTXO } from '@c4tplatform/caminojs/dist/apis/avm/utxos' import { @@ -36,6 +37,7 @@ import { GetValidatorsResponse } from '@/store/modules/platform/types' import { MultisigAliasParams } from '@c4tplatform/caminojs/dist/apis/platformvm' import { OutputOwners, SignatureError } from '@c4tplatform/caminojs/dist/common' import { ModelDepositOfferSig } from '@c4tplatform/signavaultjs' +import AvaAsset from '@/js/AvaAsset' class WalletHelper { static async getStake(wallet: WalletType): Promise { let addrs = wallet.getAllAddressesP() @@ -116,6 +118,27 @@ class WalletHelper { return txId } + static async issueBulkTx( + wallet: WalletType, + asset: AvaAsset, + orders: BulkOrder[], + memo: Buffer | undefined + ): Promise { + const fromAddresses = wallet.getAllAddressesX() + const changeAddress = wallet.getChangeAddressAvm() + + let unsignedTx = buildBulkTransfer( + fromAddresses, + [changeAddress], + wallet.getUTXOSet(), + orders, + asset, + memo + ) + const tx = await wallet.signX(unsignedTx) + return await ava.XChain().issueTx(tx) + } + static async validate( wallet: WalletType, nodeID: string, diff --git a/src/js/TxHelper.ts b/src/js/TxHelper.ts index 3c6d22dba..928576ecd 100644 --- a/src/js/TxHelper.ts +++ b/src/js/TxHelper.ts @@ -1,5 +1,5 @@ import { ava, bintools } from '@/AVA' -import { ITransaction } from '@/components/wallet/transfer/types' +import { BulkOrder, ITransaction } from '@/components/wallet/transfer/types' import { BN, Buffer } from '@c4tplatform/caminojs/dist' import { AssetAmountDestination, @@ -13,6 +13,7 @@ import { UTXOSet, UTXOSet as AVMUTXOSet, AVMConstants, + SECPTransferOutput, } from '@c4tplatform/caminojs/dist/apis/avm' import { PayloadBase } from '@c4tplatform/caminojs/dist/utils' @@ -26,6 +27,8 @@ import ERCNftToken from '@/js/ERCNftToken' import { Transaction } from '@ethereumjs/tx' import { Chain, Common, Hardfork } from '@ethereumjs/common' import Erc20Token from '@/js/Erc20Token' +import AvaAsset from './AvaAsset' +import { ZeroBN } from '@/constants' export async function buildUnsignedTransaction( orders: (ITransaction | AVMUTXO)[], @@ -153,6 +156,59 @@ export async function buildUnsignedTransaction( return unsignedTx } +export function buildBulkTransfer( + from: string[], + change: string[], + utxoset: AVMUTXOSet, + orders: BulkOrder[], + asset: AvaAsset, + memo?: Buffer +): AVMUnsignedTx { + const fromAddrs: Buffer[] = from.map((val) => bintools.parseAddress(val, 'X')) + const changeAddrs: Buffer[] = change.map((val) => bintools.parseAddress(val, 'X')) + + const aad: AssetAmountDestination = new AssetAmountDestination([], 0, fromAddrs, changeAddrs, 1) + const assetId = bintools.cb58Decode(asset.id) + const avaAsset = ava.getNetwork().X.avaxAssetID + + // Collect outputs + let amount = ZeroBN + orders.forEach((o) => (amount = amount.add(o.amount))) + const fee = ava.XChain().getTxFee() + + if (asset.id === avaAsset) { + aad.addAssetAmount(assetId, amount, fee) + } else { + aad.addAssetAmount(assetId, amount, ZeroBN) + aad.addAssetAmount(bintools.cb58Decode(avaAsset), ZeroBN, fee) + } + + const success: Error = utxoset.getMinimumSpendable(aad) + + let ins: TransferableInput[] = [] + let outs: TransferableOutput[] = [] + if (typeof success === 'undefined') { + ins = aad.getInputs() + outs = aad.getChangeOutputs() + } else { + throw success + } + + // Build the other outputs + orders.forEach((v) => { + const addr = bintools.parseAddress(v.address, 'X') + outs.push( + new TransferableOutput(assetId, new SECPTransferOutput(v.amount, [addr], ZeroBN, 1)) + ) + }) + + let networkId: number = ava.getNetworkID() + let chainId: Buffer = bintools.cb58Decode(ava.XChain().getBlockchainID()) + + const baseTx = new BaseTx(networkId, chainId, outs, ins, memo) + + return new AVMUnsignedTx(baseTx) +} export async function buildCreateNftFamilyTx( name: string, symbol: string, diff --git a/src/locales/en.json b/src/locales/en.json index ca24eb29e..03d73bb00 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -189,7 +189,10 @@ "label1": "Transaction Hash" }, "reset": "New Transaction" - } + }, + "transfer": "Transfer", + "receivers": "Receivers and Amounts", + "airdrop": "Airdrop" }, "cross_chain": { "title": "Cross Chain", @@ -579,6 +582,11 @@ "deposit_amount": "Deposit Amount", "deposited_amount": "Deposited Amount" }, + "pending_rewards": { + "title": "Expired deposit rewards", + "description": "At least one deposit has already expired without claiming the full amount of rewards. You can still claim the rewards here.", + "pending_reward": "Pending Reward" + }, "claim_modal": { "are_you_sure": "Are you sure you want to claim {amount} {symbol} rewards?", "note_message": "Transaction fee: {fee} {symbol}.", diff --git a/src/views/wallet/Earn.vue b/src/views/wallet/Earn.vue index 0a36435f7..c38ec80ef 100644 --- a/src/views/wallet/Earn.vue +++ b/src/views/wallet/Earn.vue @@ -10,7 +10,7 @@ {{ $t('earn.rewards.active_earning.title') }} -
+