@kresuslabs/core v2.11.6
Kresus Core SDK
Name
kresus-core
Installation
install with npm:
npm install @kresuslabs/core --save
install with yarn:
yarn add @kresuslabs/core
Creating Provider with Magic Link
import { ethers } from "ethers";
import { Magic } = require("magic-sdk");
/*
Ethereum
*/
const magicEth = new Magic("your_magic_api_key", {network: {
rpcUrl: "RPC_URL_FOR_ETHEREUM",
chainId: 5 // goerli
}});
// or
const magicEth = new Magic("your_magic_api_key", {network: 'goerli'});
await magicEth.auth.loginWithMagicLink({email: "youremail@example.com"});
const provider = new ethers.providers.Web3Provider(magicEth.rpcProvider);
(await magicEth.user.getMetadata()).publicAddress; // get public address
Vault Operations
import { VaultRegistry, CoreEnv } from "@kresuslabs/core";
const configVaultRegistry = {
env: CoreEnv.dev,
orgId: "Kresus",
rpcKey: "AlchemyRpcKey"
};
const vaultRegistryObj = new VaultRegistry(configVaultRegistry);
Add Vault:
timeDelay = 1000;
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
await vaultRegistryObj.addVault(timeDelay, bioAuthToken, authToken);
Deploy Vault:
vaultId = "04fde114-ab47-4933-a764-73bc3a24f93c";
identityId?="test"
authToken = "AUTHENTICATION_TOKEN";
await vaultRegistryObj.deployVault(vaultId,authToken,identityId);
import { Vault, CoreEnv } from "@kresuslabs/core";
const configVault = {
env: CoreEnv.dev,
vaultAddr: "0x14F6559416101FFc9A32Bb35C8f90127A4E2CEF8",
vaultId: "43d4ecfb-8bb6-4a47-804b-9d5b2b884862",
chainId: 5, // goerli
orgId: "Kresus",
rpcKey: "https://polygon-mumbai.g.alchemy.com/v2/<APIKEY>"
};
const vaultObj = new Vault(configVault, provider);
Transfer ETH from Vault:
toAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
amount = "0.1";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferETHFromVault(toAddress, amount, bioAuthToken, authToken, credits);
Transfer ERC20 from Vault:
toAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
amount = "1999999999999999999";
erc20ContractAddress = "0xb5B0cDBf3b42DDEBfaA1Fe95A79d29854F325eD3";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferERC20FromVault(toAddress, amount, erc20ContractAddress, bioAuthToken, authToken, credits);
Transfer ERC721 from Vault:
toAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
amount = "1";
erc721ContractAddress = "0xa6C0cGAf1d93AAEBfaA1Fe95A79d29854F325eD3";
tokenId = "9876";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferERC721FromVault(toAddress, amount, erc721ContractAddress, tokenId, bioAuthToken, authToken, credits);
Transfer ERC1155 from Vault:
toAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
amount = "1";
erc1155ContractAddress = "0x55B0cDBf3b42DDEBfaA1Fe98B79d71298A987eD3";
tokenId = "5678";
data = "0x";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferERC1155FromVault(toAddress, amount, erc1155ContractAddress, tokenId, data, bioAuthToken, authToken, credits);
Bulk Withdraw assests from Vault:
toAddr = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
ethAmount = "100000000000000"
erc20Params = [{
contractAddress : "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
amount : "10000"
},
{
contractAddress : "0x326c977e6efc84e512bb9c30f76e30c160ed06fb",
amount : "100000000000000"
}]
erc721Params = [{
contractAddress : "0xbC40FB08C4F6AE0BD91c11965D8E37FA90ecccEf",
tokenId : "4"
},
{
contractAddress : "0xbC40FB08C4F6AE0BD91c11965D8E37FA90ecccEf",
tokenId : "5"
}]
erc1155Params = [{
contractAddress : "0x75Bd1BC1757eAe47E0Ef14CddaA8d5B08207b76C",
tokenId : "0",
amount : "1",
data : "0x"
},
{
contractAddress : "0x75Bd1BC1757eAe47E0Ef14CddaA8d5B08207b76C",
tokenId : "1",
amount : "2",
data : "0x"
}]
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.bulkWithdrawal(toAddr,ethAmount,erc20Params,erc721Params,erc1155Params,bioAuthToken,authToken, credits)
Set Time Delay:
newTimeDelay = "600";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.setTimeDelay(newTimeDelay, bioAuthToken, authToken, credits);
lock User Vault:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.lock(bioAuthToken, authToken);
Unlock User Vault:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.unlock(bioAuthToken, authToken, credits);
Transfer Ownership of Vault:
newOwner = "0x5D89f57e0184708A3530d0C8ef3dcF4614e3fcc5";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferOwnership(newOwner, bioAuthToken, authToken, credits);
Add Guardian:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.addGuardian(bioAuthToken, authToken, credits);
Add Trustee:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.addTrustee(bioAuthToken, authToken, credits);
Remove Guardian:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.removeGuardian(bioAuthToken, authToken, credits);
Remove Trustee:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.removeTrustee(bioAuthToken, authToken, credits);
Cancel Operation:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.cancelOperation(operationId,authToken);
Reject Operation:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.rejectOperation(operationId,authToken);
Increase Allowance:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
offset= 2;
isCancel=true;
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.increaseAllowance(operationId,offset,isCancel, provider, authToken);
Get Operation:
vaultId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getOperations(vaultId,authToken);
Remind Operation:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
guardianAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.remindOperation(operationId,guardianAddress,authToken);
Get Operation Details:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getOperationDetails(operationId,authToken);
getVaultList:
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getVaultList(authToken);
getVaultsAmGuarding:
status = "active";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getVaultsAmGuarding(authToken, status);
getTrustorVaults:
status = "active";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getTrustorVaults(authToken, status);
removeAllInvites:
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.removeAllInvites(authToken);
Accept Operation:
requestId = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
authToken = "AUTHENTICATION_TOKEN";
bioAuthToken? = "BIOAUTHTOKEN"
await vaultObj.acceptOperation(requestId, authToken, bioAuthToken);
Invite Guardian:
guardianAddress = ["0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5"];
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.inviteGuardians(guardianAddress, authToken);
Accept Invitation:
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.acceptInvitation(invitationId, authToken);
Reject Invitation:
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.rejectInvitation(invitationId, authToken);
Extend Invitation:
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.extendInvitation(invitationId, authToken);
Replace Invitation:
inviteAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.replaceInvitation(inviteAddress,invitationId, authToken);
isUnlockPaymentDone:
timestamp = '2023-11-25T10:16:08.616Z';
auth = "AUTHENTICATION_TOKEN";
await vaultObj.isUnlockPaymentDone(timestamp,auth);
checkTokenOwnership:
contractAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
auth = "AUTHENTICATION_TOKEN";
await vaultObj.checkTokenOwnership(contractAddress,auth);
closeVault:
vaultId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
auth = "AUTHENTICATION_TOKEN";
await vaultObj.closeVault(vaultId,auth);
Get Invitation details:
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getInvitationDetails(invitationId, authToken);
Get Heir:
await vaultObj.getHeir( provider);
Get TimeDelay:
await vaultObj.getTimeDelay( provider);
Check if Vault is Locked:
await vaultObj.isLocked( provider);
Check if Voting is Enabled:
await vaultObj.isVotingEnabled( provider);
Get Vault Details:
await vaultObj.getVaultDetails( provider);
Get Vault History:
authToken = "AUTHENTICATION_TOKEN";
ownerAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
recordCount = "10";
await vaultObj.accept(authToken, ownerAddress, recordCount, provider);
Invite Trustee
let trusteeAddress = "0xdc89dbc543f8b83f92760db79b4988f0c4433ef8";
await vaultObj.inviteTrustee([trusteeAddress],<AUTH_TOKEN>);
Cancel Invitation
let invitationId = "1dfce4d7-bd78-4c7f-aaca-30275361eef5");
await vaultObj.cancelInvitation(invitationId,<AUTH_TOKEN>)
Remind Invitation
let invitationId = "1dfce4d7-bd78-4c7f-aaca-30275361eef5");
await vaultObj.remindInvitation(invitationId,<AUTH_TOKEN>)
Get Operation Requirement
let Operation = "multiCall";
await vaultObj.getOperationRequirement(<AUTH_TOKEN>, Operation)
Usage for ENS
import {Ens} from "@kresuslabs/core";
const ens = new Ens();
Get ENS Name from Address:
const address = "0x3E0e5C49008c6Ef7CF08a7c458C50498E1455851";
await ens.getEnsName(address, provider);
Get Address from ENS name:
const ensName = "rizzy" // or "rizzy.eth";
await ens.getEnsAddress(ensName, provider);
Check ENS Name Availability:
const checkName = "rizzy" // do not send "rizzy.eth";
await ens.checkAvailable(checkName, provider);
Usage for Airdrop
import {Airdrop} from "@kresuslabs/core";
const config = {
contractAddress: "0x3E0e5C49008c6Ef7CF08a7c458C50498E1455851"
}
const airdrop = new Airdrop(config);
Check if user has already claimed or not:
const userAddress = "0x3E0e5C49008c6Ef7CF08a7c458C50498E1455851";
const tokenId = "1";
await airdrop.hasClaimed(userAddress, tokenId, provider);
Mint:
const maxAmount = "1000";
const tokenId = "1";
const data = "0x";
const tokenURI = "https://kresus.mypinata.cloud/ipfs/QmZjDaTxgrSh1G8kMuzWkygCcUgaTseFHQKjENDhkSk1x1";
await airdrop.mintAirdropNFT(maxAmount, tokenId, data, tokenURI, wallet);
Transfer:
const to = "0x3E0e5C49008c6Ef7CF08a7c458C50498E1455851";
const tokenId = "1";
const data = "0x";
await airdrop.transferAirdropNFT(to, tokenId, data, wallet);
Usage for NFT-Lens
import {NFTLens,CoreEnv} from "@kresuslabs/core";
const orgId = "Kresus"
const nftLens = new NFTLens(CoreEnv.dev,orgId);
createNFTLens:
const myAddress = "0xe9F743f2649eAD27C15F98fe669DE9653B25c763";
const toAddresses = ["0x20B6Ac97be081df9add2A2865A6b56cd2C05D664"];
const toEmail = ["email@xyz.com"]
const toPhone = ["+41-76542980"]
const tokenMetadata = "tokenMetadatUrl"
const authToken = "AUTH_TOKEN"
await nftLens.createNFTLens(myAddress,toAddresses,toEmail,toPhone,tokenMetadata,authToken);
Txn Note Operations
Import class and creating an instance:
import { TxnNote, CoreEnv } from "@kresuslabs/core";
const configNotes = {
env: CoreEnv.dev;
}
const txnNoteObj = new TxnNote(configNotes);
Insert Txn Record:
/*
chain_name: eth or poly or sol or btc
ack_status: received or not_received
*/
const record = {
txn_id: "78612qwerty",
chain_name: "eth",
pvt_note: "SEND me 5 Eth",
pub_note: "5 eth sent",
ack_status: "not_received",
token_usd_rate: 5,
native_token_usd_rate: 11,
from_addr: "0xbabcuddu",
to_addr: "aisic8dudd"
};
const orgId = "Kresus"; //from IAM
const authToken = "AUTHENTICATION_TOKEN"; //from IAM
await txnNoteObj.insertRecord(record, authToken, orgId);
Update Txn Record (ack_status):
const txnId = "0x28ada8b6fe6fb3474e4182e0abc328be32200ab4d49f983c52abe172cd403142";
const chainName = "eth";
const orgId = "Kresus"; //from IAM
const authToken = "AUTHENTICATION_TOKEN"; //from IAM
const updateObj = {
ack_status: "received"
}
await txnNoteObj.updateRecord(txnId, chainName, updateObj, authToken, orgId);
Get Txn Record(s):
/*
searchKey could be a txn_id or any keyword
present in public or private note
*/
const searchKey = "0x28ada8b6fe6fb3474e4182e0abc328be32200ab4d49f983c52abe172cd403142";
const orgId = "Kresus"; //from IAM
const authToken = "AUTHENTICATION_TOKEN"; //from IAM
await txnNoteObj.getRecord(searchKey, authToken, orgId);
Get All the Txns from given Public Address:
/*
Addresses (From or To) could be of different chains (Ethereum, Polygon, Solana, Bitcoin)
*/
const addressArray = [
"0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5",
"0xc90e79c4b57419b063d4cd757c469c1acf646e5c",
"Beyru8enwLBaknXt1GHhsvDii71qiGXfL2sZyQ1zgnGQ"
];
const orgId = "Kresus"; //from IAM
const authToken = "AUTHENTICATION_TOKEN"; //from IAM
await txnNoteObj.getAllTxnsFromPubAddr(addressArray, authToken, orgId);
Swap Operations
Create a Swap Transaction
import { Swap as SwapClass } from "@kresuslabs/core/lib";
let swap = new SwapClass({env : "prod"});
from = "btc" ;
to = "eth" ;
address = "0x5b63169154C98D6829E3f5264A01ad25b1aa9cc4" ;
amountFrom = "0.05" ;
await swap.createSwap(from, to, address, amountFrom);
Get Currencies List
await swap.getCurrencies()
Get Currencies Full List
await swap.getCurrenciesFull()
Get Exchange Amount
from = "btc" ;
to = "eth" ;
amountFrom = "0.05" ;
await swap.getExchangeAmount(from, to, amountFrom)
Get Transaction Status
id = "303gz6b3ll3dvh24";
await swap.getTransactionsWithId(id)
Get Fixed Rate For Amount
InputArray = [{
"from": "eth",
"to": "btc",
"amountFrom": "5.2"
}]
await swap.getFixRateForAmount(InputArray)
Validate Address
currency = "btc",
address = "<<btc address>>"
await swap.validateAddress(currency, address)
Get Minimum Amount
from = "btc";
to = "eth";
await swap.getMinimumAmount(from, to)
Record Transactions to Proxy
userId = "<<userID>>";
swapTxId = "<<swapTxId>>" ;
swapFrom = "<<swapFrom>>" ;
swapTo = "<<swapTo>>";
txHash = "<<txHash>>";
receiveAddress = "<<receiveAddress>>";
receiveAmount = "<<receiveAmount>>";
sendAmount = "<<sendAmount>>";
await swap.recordTransactionsToProxy(userId, swapTxId, swapFrom, swapTo, txHash, receiveAddress, receiveAmount, sendAmount)
Get Transactions with ID
swapTxId = "<<swapTxId>>" ;
await swap.getStatusWithId(swapTxId) ;
Get Transaction Pending Transaction
userId = "<<userID>>";
await swap.isTxPending(swapTxId) ;
Stake Unstake
const ethAddr = "0x14F6559416101FFc9A32Bb35C8f90127A4E2CEF8";
const rpcApiKey = "alchemy-key-here";
const env = CoreEnv.uat // CoreEnv.prod for production;
const solAddr = "FnTFBoJCiS3XHS2EV1ACECUtGWpHqAW4GZ9qXKUMCURL";
const rpcApiKeySol = "shyft-key-here";
const alchemySolKey = "alchemy-key-sol-here";
const su = new StakeUnstake(
ethAddr,
rpcApiKey,
env,
provider,
solAddr,
rpcApiKeySol,
solMagic,
alchemySolKey
);
Stake ETH:
const amountInEth = "0.01";
await su.eth.stake(amountInEth);
Unstake ETH:
const amountInStEth = "0.01";
await su.eth.unstake(amountInStEth);
Withdraw ETH:
const tokenIds = ["1", "2"];
await su.eth.withdraw(tokenIds);
Get Max Withdrawal Amount:
await su.eth.getMaxWithdrawalAmount();
Get Min Withdrawal Amount:
await su.eth.getMinWithdrawalAmount();
Get Nonce:
await su.eth.getNonce();
Get Staking Info for ETH:
await su.eth.getStakingInfo();
Get Withdrawal Requests:
await su.eth.getWithdrawalRequests();
Get Withdrawal Status:
await su.eth.getWithdrawalStatus();
Instant Unstake using Uniswap:
const amountInStEth = "0.01";
await su.eth.instantUnstake(amountInStEth);
Is Allowance Sufficient:
const amountInStEth = "0.01";
await su.eth.isAllowanceSufficient(amountInStEth);
Is Staking Paused:
await su.eth.isPaused();
Rewards Calculation:
await su.eth.rewardsCalculation(provider);
Stake SOL:
const amountInSol = 0.01;
await su.sol.stake(amountInSol);
Unstake SOL:
const amountInStSol = 0.01;
await su.sol.unstake(amountIn);
Withdraw SOL:
const amountSol = 0.01;
const deactivatingSolAddr = "FnTFBoJCiS3XHS2EV1ACECUtGWpHqAW4GZ9qXKUMCURL";
await su.sol.withdraw(amountInSol, deactivatingSolAddr);
Get Stake Accounts:
await su.sol.getStakeAccounts();
Get Staking Info for SOL:
await su.sol.getStakingInfo();
License
MIT
2 months ago
2 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
4 months ago
4 months ago
4 months ago
4 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
5 months ago
5 months ago
6 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
6 months ago
6 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
8 months ago
8 months ago
10 months ago
11 months ago
11 months ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago